-
-
Notifications
You must be signed in to change notification settings - Fork 398
Refactor rizin graph in rz_util #4788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
#include <rz_list.h> | ||
|
||
/** | ||
* @brief Represents a node in a graph, node should be hashable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rizin uses \brief
syntax, here and everywhere please use this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks already way way better then before! Thanks a lot.
Some questions you might have already considered.
- Should the graph's API mostly work on Node pointers or the node ids? Having both would be nice. If the node content doesn't matter to the user. But working on pointers is fine for me as well for now.
- Runtime complexity for getting neighbors. Please let's not clone the nodes every time the neighbor list is requested. This doesn't scale well. Instead a read only
RzIterator
over the lists/matrix is a better fit I think. - Could the elements in the List based graph implementation contain edges? It would maybe reduce internal complexity getting the neighbors. Especially for directed graphs.
bool (*add_edge)(RzGraphNew *graph, const RzGraphNodeNew *from, const RzGraphNodeNew *to); | ||
bool (*remove_edge)(RzGraphNew *graph, const RzGraphNodeNew *from, const RzGraphNodeNew *to); | ||
RzList *(*get_in_neighbours)(RzGraphNew *graph, const RzGraphNodeNew *node); | ||
RzList *(*get_out_neighbours)(RzGraphNew *graph, const RzGraphNodeNew *node); | ||
bool (*is_directed_connect)(RzGraphNew *graph, const RzGraphNodeNew *from, const RzGraphNodeNew *to); | ||
bool (*del_node)(RzGraphNew *graph, RzGraphNodeNew *node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it is useful to have the same callbacks, but taking the node ids, instead of the node pointers.
This way users can decide they don't want to mess with the pointers at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the design, these pointers should all be internal to the graph API, with external access only through a unified set of functions.
unsigned int n_nodes; | ||
unsigned int n_edges; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned int n_nodes; | |
unsigned int n_edges; | |
ut64 n_nodes; | |
ut64 n_edges; |
Just in case :)
unsigned int n_nodes; | ||
unsigned int n_edges; | ||
|
||
HtUP *nodes; /* <hash_id, RzGraphNode> */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can add a typedef RzGraphHashId;
|
||
typedef struct rz_graph_matrix_edge_impl_t { | ||
st64 **adj_matrix; | ||
unsigned int n_nodes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned int n_nodes; | |
ut64 n_nodes; |
if (!graph || !from || !to) { | ||
rz_return_val_if_fail(graph && from && to, false); | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a simple rz_return_val_if`?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess just auto-complete and choose one of them
return false; | ||
} | ||
|
||
impl->adj_matrix[from->hash_id][to->hash_id] = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the magic values like 1
and 0
into macros. EDGE_SET
, EDGE_UNSET
or similar.
It might be even useful to add some small inline
functions to test this property.
E.g.:
static inline bool edge_set(impl, from, to) { return impl->adj_matrix[from->hash_id][to->hash_id] & 1; }
This way we can extend the graph easier with special edges. By using the upper bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, I would fix this dirty and quick implementation
return NULL; | ||
} | ||
|
||
for (unsigned int i = 0; i < impl->n_nodes; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (unsigned int i = 0; i < impl->n_nodes; i++) { | |
for (ut64 i = 0; i < impl->n_nodes; i++) { |
for (unsigned int i = 0; i < impl->n_nodes; i++) { | ||
if (impl->adj_matrix[i][node->hash_id] == 1) { | ||
RzGraphNodeNew *new_node = ht_up_find(graph->nodes, i, NULL); | ||
if (!new_node) { | ||
RZ_LOG_WARN("Failed to clone the node") | ||
rz_list_free(neighbours); | ||
return NULL; | ||
} | ||
rz_list_append(neighbours, new_node); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above. Let's not clone all the nodes just because the neighbors are requested. The pointers to the neighbors are enough.
It could instead return a RzIterator
for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would largely prevent the graph to be accessed by multiple threads. Because the pointers returned by the iterator might live longer then the nodes in the graph.
But we would need to add another implementation for this use case anyways.
* @param impl The implementation of the graph. IMPLEMENTATION_LIST or IMPLEMENTATION_MATRIX etc. | ||
* @return The new graph, NULL if the graph is not created successfully. | ||
*/ | ||
RZ_API RzGraphNew *rz_graph_new_new(RzGraphIdentifierHash user_hash, RzGraphUserdataFree user_free, RzGraphImpl impl) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RZ_API RzGraphNew *rz_graph_new_new(RzGraphIdentifierHash user_hash, RzGraphUserdataFree user_free, RzGraphImpl impl) { | |
RZ_API RZ_OWN RzGraphNew *rz_graph_new_new(RzGraphIdentifierHash user_hash, RzGraphUserdataFree user_free, RzGraphImpl impl) { |
* @return RzList contains all out neighbour nodes (borrowed instance) | ||
*/ | ||
RZ_API RzList *rz_graph_get_out_nodes_new(RzGraphNew *g, RzGraphNodeNew *node) { | ||
return g->edge_set->ops->get_out_neighbours(g, node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These callbacks might are a performance problem (see: #4769). We would need to test this and maybe replace them with a switch.
|
You can checkout
You make a good point there. Considering code complexity (meaning: many lines of code) vs. low runtime complexity. If you don't want to add an edge list for now (because too much work/ too many things to consider), it is fine. cc @wargio |
agreed with @Rot127 I think it is ok to have first an implementation that works, then we can improve the execution time. |
Your checklist for this pull request
Detailed description
working on implement functions
Add more graph representation for rizin, see #4231
Test plan
Closing issues
...