]> git.meshlink.io Git - meshlink/commitdiff
Move meshlink_get_all_edges_state() to devtools_get_all_edges().
authorGuus Sliepen <guus@meshlink.io>
Tue, 15 Aug 2017 18:59:38 +0000 (20:59 +0200)
committerGuus Sliepen <guus@meshlink.io>
Tue, 15 Aug 2017 18:59:38 +0000 (20:59 +0200)
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.

src/devtools.c
src/devtools.h
src/meshlink.c
src/meshlink.h

index ca59ad9b9e54a8b7b4a04aad9da3c58af7296900..c1578e65532eceb6d2e5f8c543c24719b9797507 100644 (file)
@@ -1,5 +1,24 @@
+/*
+    devtools.c -- Debugging and quality control functions.
+    Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
+
+    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 <assert.h>
 
 #include "logger.h"
 #include "meshlink_internal.h"
 
 #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));
 
index d75ada5fea47349908c356a3bc26ed20c8bbf38d..76f9ab539786913ab2d9cda6524ce3e16fe1c024 100644 (file)
     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
index 90cdf0db6d3b644cb45cb144c04a9652ffd315cb..e70ac5f624589023139dc54fda2ba797dd391940 100644 (file)
@@ -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;
index e85be2ff9d7b58769d3c1f4a43dd63e358a29eb8..a89a6c487e1282eeff84a80f4e58c08a549f477e 100644 (file)
@@ -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