From: Guus Sliepen Date: Tue, 15 Aug 2017 18:59:38 +0000 (+0200) Subject: Move meshlink_get_all_edges_state() to devtools_get_all_edges(). X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=6a73467d16a57ca1645d805776c4dfcaf2b05798 Move meshlink_get_all_edges_state() to devtools_get_all_edges(). There should be no reason for the application to query the list of edges, so move this functionality to devtools.[ch]. Also, flatten the array to reduce the amount of memory allocations and bookkeeping necessary. --- diff --git a/src/devtools.c b/src/devtools.c index ca59ad9b..c1578e65 100644 --- a/src/devtools.c +++ b/src/devtools.c @@ -1,5 +1,24 @@ +/* + devtools.c -- Debugging and quality control functions. + Copyright (C) 2014, 2017 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #include "system.h" +#include #include "logger.h" #include "meshlink_internal.h" @@ -10,6 +29,67 @@ #include "devtools.h" +/* Return an array of edges in the current network graph. + * Data captures the current state and will not be updated. + * Caller must deallocate data when done. + */ +devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb) { + if(!mesh || !nmemb || (*nmemb && !edges)) { + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } + + pthread_mutex_lock(&(mesh->mesh_mutex)); + + devtool_edge_t *result = NULL; + int result_size = 0; + + result_size = mesh->edges->count / 2; + + // if result is smaller than edges, we have to dealloc all the excess devtool_edge_t + if(result_size > *nmemb) + result = realloc(edges, result_size * sizeof(*result)); + else + result = edges; + + if(result) { + devtool_edge_t *p = result; + int n = 0; + + for splay_each(edge_t, e, mesh->edges) { + // skip edges that do not represent a two-directional connection + if((!e->reverse) || (e->reverse->to != e->from)) + continue; + + // don't count edges twice + if(e->to < e->from) + continue; + + assert(n < result_size); + + p->from = (meshlink_node_t *)e->from; + p->to = (meshlink_node_t *)e->to; + p->address = e->address.storage; + p->options = e->options; + p->weight = e->weight; + + n++; + p++; + } + + // shrink result to the actual amount of memory used + result = realloc(result, n * sizeof(*result)); + *nmemb = n; + } else { + *nmemb = 0; + meshlink_errno = MESHLINK_ENOMEM; + } + + pthread_mutex_unlock(&(mesh->mesh_mutex)); + + return result; +} + static bool fstrwrite(const char *str, FILE *stream) { size_t len = strlen(str); @@ -38,7 +118,7 @@ bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) size_t edge_count = 0; meshlink_node_t **nodes = meshlink_get_all_nodes(mesh, NULL, &node_count); - meshlink_edge_t **edges = meshlink_get_all_edges_state(mesh, NULL, &edge_count); + devtool_edge_t *edges = devtool_get_all_edges(mesh, NULL, &edge_count); if((!nodes && node_count != 0) || (!edges && edge_count != 0)) goto fail; @@ -77,17 +157,17 @@ bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) goto fail; for(size_t i = 0; i < edge_count; ++i) { - if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i]->from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i]->to->name, stream) || !fstrwrite("\": {\n", stream)) + if(!fstrwrite("\t\t\"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("_to_", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\": {\n", stream)) goto fail; - if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i]->from->name, stream) || !fstrwrite("\",\n", stream)) + if(!fstrwrite("\t\t\t\"from\": \"", stream) || !fstrwrite(edges[i].from->name, stream) || !fstrwrite("\",\n", stream)) goto fail; - if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i]->to->name, stream) || !fstrwrite("\",\n", stream)) + if(!fstrwrite("\t\t\t\"to\": \"", stream) || !fstrwrite(edges[i].to->name, stream) || !fstrwrite("\",\n", stream)) goto fail; char *host = NULL, *port = NULL, *address = NULL; - sockaddr2str((const sockaddr_t *) & (edges[i]->address), &host, &port); + sockaddr2str((const sockaddr_t *) & (edges[i].address), &host, &port); if(host && port) xasprintf(&address, "{ \"host\": \"%s\", \"port\": %s }", host, port); @@ -102,10 +182,10 @@ bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream) free(address); - if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(__itoa(edges[i]->options), stream) || !fstrwrite(",\n", stream)) + if(!fstrwrite("\t\t\t\"options\": ", stream) || !fstrwrite(__itoa(edges[i].options), stream) || !fstrwrite(",\n", stream)) goto fail; - if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(__itoa(edges[i]->weight), stream) || !fstrwrite("\n", stream)) + if(!fstrwrite("\t\t\t\"weight\": ", stream) || !fstrwrite(__itoa(edges[i].weight), stream) || !fstrwrite("\n", stream)) goto fail; if(!fstrwrite((i + 1) != edge_count ? "\t\t},\n" : "\t\t}\n", stream)) @@ -126,15 +206,8 @@ fail: result = false; done: - - if(nodes) - free(nodes); - - for(size_t i = 0; edges && i < edge_count; ++i) - free(edges[i]); - - if(nodes) - free(edges); + free(nodes); + free(edges); pthread_mutex_unlock(&(mesh->mesh_mutex)); diff --git a/src/devtools.h b/src/devtools.h index d75ada5f..76f9ab53 100644 --- a/src/devtools.h +++ b/src/devtools.h @@ -20,6 +20,66 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +/// \file devtools.h +/** This header files declares functions that are only intended for debugging and quality control. + * They are not necessary for the normal operation of MeshLink. + * Applications should not depend on any of these functions for their normal operation. + */ + +/// An edge in the MeshLink network. +typedef struct devtool_edge devtool_edge_t; + +/// An edge in the MeshLink network. +struct devtool_edge { + struct meshlink_node *from; ///< Pointer to a node. Node memory is + // owned by meshlink and should not be + // deallocated. Node contents may be + // changed by meshlink. + struct meshlink_node *to; ///< Pointer to a node. Node memory is + // owned by meshlink and should not be + // deallocated. Node contents may be + // changed by meshlink. + struct sockaddr_storage address;///< The address information associated + // with this edge. + uint32_t options; ///< Edge options. @TODO what are edge options? + int weight; ///< Weight assigned to this edge. +}; + +/// Get a list of edges. +/** This function returns an array with copies of all known bidirectional edges. + * The edges are copied to capture the mesh state at call time, since edges + * mutate frequently. The nodes pointed to within the devtool_edge_t type + * are not copies; these are the same pointers that one would get from a call + * to meshlink_get_all_nodes(). + * + * @param mesh A handle which represents an instance of MeshLink. + * @param edges A pointer to a previously allocated array of + * devtool_edge_t, or NULL in which case MeshLink will + * allocate a new array. + * The application is allowed to call free() on the array whenever it wishes. + * The pointers in the devtool_edge_t elements are valid until + * meshlink_close() is called. + * @param nmemb A pointer to a variable holding the number of elements that + * are stored in the array. In case the @a edges @a + * argument is not NULL, MeshLink might call realloc() + * on the array to change its size. + * The contents of this variable will be changed to reflect + * the new size of the array. + * @return A pointer to an array containing devtool_edge_t elements, + * or NULL in case of an error. + * If the @a edges @a argument was not NULL, then the + * retun value can be either the same value or a different + * value. If the new values is NULL, then the old array + * will have been freed by Meshlink. + */ +extern devtool_edge_t *devtool_get_all_edges(meshlink_handle_t *mesh, devtool_edge_t *edges, size_t *nmemb); + +/// Export a list of edges to a file in JSON format. +/* @param mesh A handle which represents an instance of MeshLink. + * @param FILE An open file descriptor to which a JSON representation of the edges will be written. + * + * @return True in case of success, false otherwise. + */ extern bool devtool_export_json_all_edges_state(meshlink_handle_t *mesh, FILE *stream); #endif diff --git a/src/meshlink.c b/src/meshlink.c index 90cdf0db..e70ac5f6 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -2099,67 +2099,6 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const // @TODO do we want to fire off a connection attempt right away? } -/* Return an array of edges in the current network graph. - * Data captures the current state and will not be updated. - * Caller must deallocate data when done. - */ -meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink_edge_t **edges, size_t *nmemb) { - if(!mesh || !nmemb || (*nmemb && !edges)) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - pthread_mutex_lock(&(mesh->mesh_mutex)); - - meshlink_edge_t **result = NULL; - meshlink_edge_t *copy = NULL; - int result_size = 0; - - result_size = mesh->edges->count; - - // if result is smaller than edges, we have to dealloc all the excess meshlink_edge_t - if(result_size > *nmemb) - result = realloc(edges, result_size * sizeof(meshlink_edge_t *)); - else - result = edges; - - if(result) { - meshlink_edge_t **p = result; - int n = 0; - for splay_each(edge_t, e, mesh->edges) { - // skip edges that do not represent a two-directional connection - if((!e->reverse) || (e->reverse->to != e->from)) { - result_size--; - continue; - } - n++; - // the first *nmemb members of result can be re-used - if(n > *nmemb) - copy = xzalloc(sizeof(*copy)); - else - copy = *p; - copy->from = (meshlink_node_t *)e->from; - copy->to = (meshlink_node_t *)e->to; - copy->address = e->address.storage; - copy->options = e->options; - copy->weight = e->weight; - *p++ = copy; - } - // shrink result to the actual amount of memory used - for(int i = *nmemb; i > result_size; i--) - free(result[i - 1]); - result = realloc(result, result_size * sizeof(meshlink_edge_t *)); - *nmemb = result_size; - } else { - *nmemb = 0; - meshlink_errno = MESHLINK_ENOMEM; - } - - pthread_mutex_unlock(&(mesh->mesh_mutex)); - - return result; -} - static bool channel_pre_accept(struct utcp *utcp, uint16_t port) { node_t *n = utcp->priv; meshlink_handle_t *mesh = n->mesh; diff --git a/src/meshlink.h b/src/meshlink.h index e85be2ff..a89a6c48 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -48,9 +48,6 @@ typedef struct meshlink_handle meshlink_handle_t; /// A handle for a MeshLink node. typedef struct meshlink_node meshlink_node_t; -/// A handle for a MeshLink edge. -typedef struct meshlink_edge meshlink_edge_t; - /// A handle for a MeshLink channel. typedef struct meshlink_channel meshlink_channel_t; @@ -112,22 +109,6 @@ struct meshlink_channel { #endif // MESHLINK_INTERNAL_H -/// An edge in the meshlink network. -struct meshlink_edge { - struct meshlink_node *from; ///< Pointer to a node. Node memory is - // owned by meshlink and should not be - // deallocated. Node contents may be - // changed by meshlink. - struct meshlink_node *to; ///< Pointer to a node. Node memory is - // owned by meshlink and should not be - // deallocated. Node contents may be - // changed by meshlink. - struct sockaddr_storage address;///< The address information associated - // with this edge. - uint32_t options; ///< Edge options. @TODO what are edge options? - int weight; ///< Weight assigned to this edge. -}; - /// Get the text for the given MeshLink error code. /** This function returns a pointer to the string containing the description of the given error code. * @@ -795,43 +776,6 @@ extern uint32_t meshlink_channel_get_flags(meshlink_handle_t *mesh, meshlink_cha */ extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr); -/// Get a list of edges. -/** This function returns an array with copies of all known bidirectional edges. - * The edges are copied to capture the mesh state at call time, since edges - * mutate frequently. The nodes pointed to within the meshlink_edge_t type - * are not copies; these are the same pointers that one would get from a call - * to meshlink_get_all_nodes(). - * - * @param mesh A handle which represents an instance of MeshLink. - * @param edges A pointer to a previously allocated array of pointers to - * meshlink_edge_t, or NULL in which case MeshLink will - * allocate a new array. The application CANNOT supply an - * array it allocated itself with malloc, but CAN use - * the return value from the previous call to this function - * (which is the preferred way). - * The pointers in the array are valid until meshlink_close() is called. - * ATTENTION: The pointers and values should never be modified - * by the application!!! - * @param nmemb A pointer to a variable holding the number of nodes that - * are stored in the array. In case the @a nodes @a - * argument is not NULL, MeshLink might call realloc() - * on the array to change its size. - * The contents of this variable will be changed to reflect - * the new size of the array. - * @return A pointer to an array containing pointers to all known - * edges, or NULL in case of an error. - * If the @a edges @a argument was not NULL, then the - * retun value can be either the same value or a different - * value. If the new values is NULL, then the old array - * will have been freed by Meshlink. - * The caller must call free() on each element of this - * array (but not the contents of said elements), - * as well as the array itself when it is finished. - * ATTENTION: The pointers and values should never be modified - * by the application!!! - */ -extern meshlink_edge_t **meshlink_get_all_edges_state(meshlink_handle_t *mesh, meshlink_edge_t **edges, size_t *nmemb); - /// Enable or disable zeroconf discovery of local peers /** This controls whether zeroconf discovery using the Catta library will be