]> git.ipfire.org Git - location/libloc.git/commitdiff
network-tree: Split into a separate file
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 21 Feb 2024 13:53:20 +0000 (13:53 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 21 Feb 2024 13:53:20 +0000 (13:53 +0000)
There are no functional changes here, but I find this code easier to
handle if it is split across smaller files.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/libloc/network-tree.h [new file with mode: 0644]
src/libloc/network.h
src/network-tree.c [new file with mode: 0644]
src/network.c
src/writer.c

index 89fc8b5d2f2090a08bfc2ec9bb4a628faf44190b..e41ae8386de6b9352e98836f2b34269661ab5ccc 100644 (file)
@@ -104,6 +104,7 @@ pkginclude_HEADERS = \
        src/libloc/format.h \
        src/libloc/network.h \
        src/libloc/network-list.h \
+       src/libloc/network-tree.h \
        src/libloc/private.h \
        src/libloc/stringpool.h \
        src/libloc/resolv.h \
@@ -122,6 +123,7 @@ src_libloc_la_SOURCES = \
        src/database.c \
        src/network.c \
        src/network-list.c \
+       src/network-tree.c \
        src/resolv.c \
        src/stringpool.c \
        src/writer.c
diff --git a/src/libloc/network-tree.h b/src/libloc/network-tree.h
new file mode 100644 (file)
index 0000000..13052b7
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+       libloc - A library to determine the location of someone on the Internet
+
+       Copyright (C) 2017-2024 IPFire Development Team <info@ipfire.org>
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library 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
+       Lesser General Public License for more details.
+*/
+
+#ifndef LIBLOC_NETWORK_TREE_H
+#define LIBLOC_NETWORK_TREE_H
+
+#ifdef LIBLOC_PRIVATE
+
+#include <libloc/libloc.h>
+#include <libloc/network.h>
+
+struct loc_network_tree;
+
+int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree);
+
+struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree);
+
+struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree);
+
+int loc_network_tree_walk(struct loc_network_tree* tree,
+               int(*filter_callback)(struct loc_network* network, void* data),
+               int(*callback)(struct loc_network* network, void* data), void* data);
+
+int loc_network_tree_dump(struct loc_network_tree* tree);
+
+int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network);
+
+size_t loc_network_tree_count_nodes(struct loc_network_tree* tree);
+
+int loc_network_tree_cleanup(struct loc_network_tree* tree);
+
+/*
+       Nodes
+*/
+
+struct loc_network_tree_node;
+
+int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node);
+
+struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node);
+struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node);
+
+struct loc_network_tree_node* loc_network_tree_node_get(
+       struct loc_network_tree_node* node, unsigned int index);
+
+int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node);
+
+struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node);
+
+#endif /* LIBLOC_PRIVATE */
+
+#endif /* LIBLOC_NETWORK_TREE_H */
index ccbcaa232fb84a9bc8b8d30cadea463e99235478..51901ba44fcc0c23ed021845ff7688b7c2b53103 100644 (file)
@@ -68,31 +68,13 @@ struct loc_network_list* loc_network_exclude_list(
 
 #ifdef LIBLOC_PRIVATE
 
+int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other);
+
 int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj);
 int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
                struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj);
 
-struct loc_network_tree;
-int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree);
-struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree);
-struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree);
-int loc_network_tree_walk(struct loc_network_tree* tree,
-               int(*filter_callback)(struct loc_network* network, void* data),
-               int(*callback)(struct loc_network* network, void* data), void* data);
-int loc_network_tree_dump(struct loc_network_tree* tree);
-int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network);
-size_t loc_network_tree_count_nodes(struct loc_network_tree* tree);
-
-struct loc_network_tree_node;
-int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node);
-struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node);
-struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node);
-struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index);
-
-int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node);
-struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node);
-
-int loc_network_tree_cleanup(struct loc_network_tree* tree);
+int loc_network_merge(struct loc_network** n, struct loc_network* n1, struct loc_network* n2);
 
 #endif
 #endif
diff --git a/src/network-tree.c b/src/network-tree.c
new file mode 100644 (file)
index 0000000..d8442c9
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+       libloc - A library to determine the location of someone on the Internet
+
+       Copyright (C) 2024 IPFire Development Team <info@ipfire.org>
+
+       This library is free software; you can redistribute it and/or
+       modify it under the terms of the GNU Lesser General Public
+       License as published by the Free Software Foundation; either
+       version 2.1 of the License, or (at your option) any later version.
+
+       This library 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
+       Lesser General Public License for more details.
+*/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <libloc/libloc.h>
+#include <libloc/address.h>
+#include <libloc/network-tree.h>
+#include <libloc/private.h>
+
+struct loc_network_tree {
+       struct loc_ctx* ctx;
+       int refcount;
+
+       struct loc_network_tree_node* root;
+};
+
+struct loc_network_tree_node {
+       struct loc_ctx* ctx;
+       int refcount;
+
+       struct loc_network_tree_node* zero;
+       struct loc_network_tree_node* one;
+
+       struct loc_network* network;
+
+       // Set if deleted
+       int deleted:1;
+};
+
+int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
+       struct loc_network_tree* t = calloc(1, sizeof(*t));
+       if (!t)
+               return 1;
+
+       t->ctx = loc_ref(ctx);
+       t->refcount = 1;
+
+       // Create the root node
+       int r = loc_network_tree_node_new(ctx, &t->root);
+       if (r) {
+               loc_network_tree_unref(t);
+               return r;
+       }
+
+       DEBUG(t->ctx, "Network tree allocated at %p\n", t);
+       *tree = t;
+       return 0;
+}
+
+struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
+       return loc_network_tree_node_ref(tree->root);
+}
+
+static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
+       struct loc_network_tree_node** n = NULL;
+       int r;
+
+       switch (path) {
+               case 0:
+                       n = &node->zero;
+                       break;
+
+               case 1:
+                       n = &node->one;
+                       break;
+
+               default:
+                       errno = EINVAL;
+                       return NULL;
+       }
+
+       // If the node existed, but has been deleted, we undelete it
+       if (*n && (*n)->deleted) {
+               (*n)->deleted = 0;
+
+       // If the desired node doesn't exist, yet, we will create it
+       } else if (!*n) {
+               r = loc_network_tree_node_new(node->ctx, n);
+               if (r)
+                       return NULL;
+       }
+
+       return *n;
+}
+
+static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
+       struct loc_network_tree_node* node = tree->root;
+
+       for (unsigned int i = 0; i < prefix; i++) {
+               // Check if the ith bit is one or zero
+               node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
+       }
+
+       return node;
+}
+
+static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
+               int(*filter_callback)(struct loc_network* network, void* data),
+               int(*callback)(struct loc_network* network, void* data), void* data) {
+       int r;
+
+       // If the node has been deleted, don't process it
+       if (node->deleted)
+               return 0;
+
+       // Finding a network ends the walk here
+       if (node->network) {
+               if (filter_callback) {
+                       int f = filter_callback(node->network, data);
+                       if (f < 0)
+                               return f;
+
+                       // Skip network if filter function returns value greater than zero
+                       if (f > 0)
+                               return 0;
+               }
+
+               r = callback(node->network, data);
+               if (r)
+                       return r;
+       }
+
+       // Walk down on the left side of the tree first
+       if (node->zero) {
+               r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
+               if (r)
+                       return r;
+       }
+
+       // Then walk on the other side
+       if (node->one) {
+               r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+int loc_network_tree_walk(struct loc_network_tree* tree,
+               int(*filter_callback)(struct loc_network* network, void* data),
+               int(*callback)(struct loc_network* network, void* data), void* data) {
+       return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
+}
+
+static void loc_network_tree_free(struct loc_network_tree* tree) {
+       DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
+
+       loc_network_tree_node_unref(tree->root);
+
+       loc_unref(tree->ctx);
+       free(tree);
+}
+
+struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
+       if (--tree->refcount > 0)
+               return tree;
+
+       loc_network_tree_free(tree);
+       return NULL;
+}
+
+static int __loc_network_tree_dump(struct loc_network* network, void* data) {
+       struct loc_ctx* ctx = data;
+
+       DEBUG(ctx, "Dumping network at %p\n", network);
+
+       const char* s = loc_network_str(network);
+       if (!s)
+               return 1;
+
+       INFO(ctx, "%s\n", s);
+
+       return 0;
+}
+
+int loc_network_tree_dump(struct loc_network_tree* tree) {
+       DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
+
+       return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, tree->ctx);
+}
+
+int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
+       DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
+
+       const struct in6_addr* first_address = loc_network_get_first_address(network);
+       const unsigned int prefix = loc_network_prefix(network);
+
+       struct loc_network_tree_node* node = loc_network_tree_get_path(tree, first_address, prefix);
+       if (!node) {
+               ERROR(tree->ctx, "Could not find a node\n");
+               return -ENOMEM;
+       }
+
+       // Check if node has not been set before
+       if (node->network) {
+               DEBUG(tree->ctx, "There is already a network at this path: %s\n",
+                       loc_network_str(node->network));
+               return -EBUSY;
+       }
+
+       // Point node to the network
+       node->network = loc_network_ref(network);
+
+       return 0;
+}
+
+static int loc_network_tree_delete_network(
+               struct loc_network_tree* tree, struct loc_network* network) {
+       struct loc_network_tree_node* node = NULL;
+
+       DEBUG(tree->ctx, "Deleting network %s from tree...\n", loc_network_str(network));
+
+       const struct in6_addr* first_address = loc_network_get_first_address(network);
+               const unsigned int prefix = loc_network_prefix(network);
+
+       node = loc_network_tree_get_path(tree, first_address, prefix);
+       if (!node) {
+               ERROR(tree->ctx, "Network was not found in tree %s\n", loc_network_str(network));
+               return 1;
+       }
+
+       // Drop the network
+       if (node->network) {
+               loc_network_unref(node->network);
+               node->network = NULL;
+       }
+
+       // Mark the node as deleted if it was a leaf
+       if (!node->zero && !node->one)
+               node->deleted = 1;
+
+       return 0;
+}
+
+static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
+       size_t counter = 1;
+
+       // Don't count deleted nodes
+       if (node->deleted)
+               return 0;
+
+       if (node->zero)
+               counter += __loc_network_tree_count_nodes(node->zero);
+
+       if (node->one)
+               counter += __loc_network_tree_count_nodes(node->one);
+
+       return counter;
+}
+
+size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
+       return __loc_network_tree_count_nodes(tree->root);
+}
+
+int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
+       struct loc_network_tree_node* n = calloc(1, sizeof(*n));
+       if (!n)
+               return -ENOMEM;
+
+       n->ctx = loc_ref(ctx);
+       n->refcount = 1;
+
+       n->zero = n->one = NULL;
+
+       DEBUG(n->ctx, "Network node allocated at %p\n", n);
+       *node = n;
+       return 0;
+}
+
+struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
+       if (node)
+               node->refcount++;
+
+       return node;
+}
+
+static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
+       DEBUG(node->ctx, "Releasing network node at %p\n", node);
+
+       if (node->network)
+               loc_network_unref(node->network);
+
+       if (node->zero)
+               loc_network_tree_node_unref(node->zero);
+
+       if (node->one)
+               loc_network_tree_node_unref(node->one);
+
+       loc_unref(node->ctx);
+       free(node);
+}
+
+struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
+       if (--node->refcount > 0)
+               return node;
+
+       loc_network_tree_node_free(node);
+       return NULL;
+}
+
+struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
+       if (index == 0)
+               node = node->zero;
+       else
+               node = node->one;
+
+       if (!node)
+               return NULL;
+
+       return loc_network_tree_node_ref(node);
+}
+
+int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
+       return (!!node->network);
+}
+
+struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
+       return loc_network_ref(node->network);
+}
+
+/*
+       Merge the tree!
+*/
+
+struct loc_network_tree_merge_ctx {
+       struct loc_network_tree* tree;
+       struct loc_network_list* networks;
+       unsigned int merged;
+};
+
+static int loc_network_tree_merge_step(struct loc_network* network, void* data) {
+       struct loc_network_tree_merge_ctx* ctx = (struct loc_network_tree_merge_ctx*)data;
+       struct loc_network* n = NULL;
+       struct loc_network* m = NULL;
+       int r;
+
+       // How many networks do we have?
+       size_t i = loc_network_list_size(ctx->networks);
+
+       // If the list is empty, just add the network
+       if (i == 0)
+               return loc_network_list_push(ctx->networks, network);
+
+       while (i--) {
+               // Fetch the last network of the list
+               n = loc_network_list_get(ctx->networks, i);
+
+               // Try to merge the two networks
+               r = loc_network_merge(&m, n, network);
+               if (r)
+                       goto ERROR;
+
+               // Did we get a result?
+               if (m) {
+                       DEBUG(ctx->tree->ctx, "Merged networks %s + %s -> %s\n",
+                               loc_network_str(n), loc_network_str(network), loc_network_str(m));
+
+                       // Add the new network
+                       r = loc_network_tree_add_network(ctx->tree, m);
+                       switch (r) {
+                               case 0:
+                                       break;
+
+                               // There might already be a network
+                               case -EBUSY:
+                                       r = 0;
+                                       goto ERROR;
+
+                               default:
+                                       goto ERROR;
+                       }
+
+                       // Remove the merge networks
+                       r = loc_network_tree_delete_network(ctx->tree, network);
+                       if (r)
+                               goto ERROR;
+
+                       r = loc_network_tree_delete_network(ctx->tree, n);
+                       if (r)
+                               goto ERROR;
+
+                       // Add the new network to the stack
+                       r = loc_network_list_push(ctx->networks, m);
+                       if (r)
+                               goto ERROR;
+
+                       // Remove the previous network from the stack
+                       r = loc_network_list_remove(ctx->networks, n);
+                       if (r)
+                               goto ERROR;
+
+                       // Count merges
+                       ctx->merged++;
+
+                       // Try merging the new network with others
+                       r = loc_network_tree_merge_step(m, data);
+                       if (r)
+                               goto ERROR;
+
+                       loc_network_unref(m);
+                       m = NULL;
+
+                       // Once we have found a merge, we are done
+                       break;
+
+               // If we could not merge the two networks, we add the current one
+               } else {
+                       r = loc_network_list_push(ctx->networks, network);
+                       if (r)
+                               goto ERROR;
+               }
+
+               loc_network_unref(n);
+               n = NULL;
+       }
+
+       const unsigned int prefix = loc_network_prefix(network);
+
+       // Remove any networks that we cannot merge
+       loc_network_list_remove_with_prefix_smaller_than(ctx->networks, prefix);
+
+ERROR:
+       if (m)
+               loc_network_unref(m);
+       if (n)
+               loc_network_unref(n);
+
+       return r;
+}
+
+static int loc_network_tree_merge(struct loc_network_tree* tree) {
+       struct loc_network_tree_merge_ctx ctx = {
+               .tree     = tree,
+               .networks = NULL,
+               .merged   = 0,
+       };
+       int r;
+
+       // Create a new list
+       r = loc_network_list_new(tree->ctx, &ctx.networks);
+       if (r)
+               goto ERROR;
+
+       // Walk through the entire tree
+       r = loc_network_tree_walk(tree, NULL, loc_network_tree_merge_step, &ctx);
+       if (r)
+               goto ERROR;
+
+       DEBUG(tree->ctx, "%u network(s) have been merged\n", ctx.merged);
+
+ERROR:
+       if (ctx.networks)
+               loc_network_list_unref(ctx.networks);
+
+       return r;
+}
+
+/*
+       Deduplicate the tree
+*/
+
+struct loc_network_tree_dedup_ctx {
+       struct loc_network_tree* tree;
+       struct loc_network_list* stack;
+       unsigned int removed;
+};
+
+static int loc_network_tree_dedup_step(struct loc_network* network, void* data) {
+       struct loc_network_tree_dedup_ctx* ctx = (struct loc_network_tree_dedup_ctx*)data;
+       struct loc_network* n = NULL;
+       int r;
+
+       // First call when we have not seen any networks, yet
+       if (loc_network_list_empty(ctx->stack))
+               return loc_network_list_push(ctx->stack, network);
+
+       const unsigned int prefix = loc_network_prefix(network);
+
+       // Remove any networks that are not interesting
+       loc_network_list_remove_with_prefix_smaller_than(ctx->stack, prefix);
+
+       for (int i = loc_network_list_size(ctx->stack) - 1; i >= 0; i--) {
+               n = loc_network_list_get(ctx->stack, i);
+
+               // Is network a subnet?
+               if (loc_network_is_subnet(n, network)) {
+                       // Do all properties match?
+                       if (loc_network_properties_cmp(n, network) == 0) {
+                               r = loc_network_tree_delete_network(ctx->tree, network);
+                               if (r)
+                                       return r;
+
+                               // Count
+                               ctx->removed++;
+
+                               // Once we removed the subnet, we are done
+                               goto END;
+                       }
+
+                       // Once we found a subnet, we are done
+                       break;
+               }
+       }
+
+       // If network did not get removed, we push it into the stack
+       r = loc_network_list_push(ctx->stack, network);
+       if (r)
+               return r;
+
+END:
+       if (n)
+               loc_network_unref(n);
+
+       return r;
+}
+
+static int loc_network_tree_dedup(struct loc_network_tree* tree) {
+       struct loc_network_tree_dedup_ctx ctx = {
+               .tree    = tree,
+               .stack   = NULL,
+               .removed = 0,
+       };
+       int r;
+
+       r = loc_network_list_new(tree->ctx, &ctx.stack);
+       if (r)
+               return r;
+
+       // Walk through the entire tree
+       r = loc_network_tree_walk(tree, NULL, loc_network_tree_dedup_step, &ctx);
+       if (r)
+               goto ERROR;
+
+       DEBUG(tree->ctx, "%u network(s) have been removed\n", ctx.removed);
+
+ERROR:
+       if (ctx.stack)
+               loc_network_list_unref(ctx.stack);
+
+       return r;
+}
+
+static int loc_network_tree_delete_node(struct loc_network_tree* tree,
+               struct loc_network_tree_node** node) {
+       struct loc_network_tree_node* n = *node;
+       int r0 = 1;
+       int r1 = 1;
+
+       // Return for nodes that have already been deleted
+       if (n->deleted)
+               goto DELETE;
+
+       // Delete zero
+       if (n->zero) {
+               r0 = loc_network_tree_delete_node(tree, &n->zero);
+               if (r0 < 0)
+                       return r0;
+       }
+
+       // Delete one
+       if (n->one) {
+               r1 = loc_network_tree_delete_node(tree, &n->one);
+               if (r1 < 0)
+                       return r1;
+       }
+
+       // Don't delete this node if we are a leaf
+       if (n->network)
+               return 0;
+
+       // Don't delete this node if has child nodes that we need
+       if (!r0 || !r1)
+               return 0;
+
+       // Don't delete root
+       if (tree->root == n)
+               return 0;
+
+DELETE:
+       // It is now safe to delete the node
+       loc_network_tree_node_unref(n);
+       *node = NULL;
+
+       return 1;
+}
+
+static int loc_network_tree_delete_nodes(struct loc_network_tree* tree) {
+       int r;
+
+       r = loc_network_tree_delete_node(tree, &tree->root);
+       if (r < 0)
+               return r;
+
+       return 0;
+}
+
+int loc_network_tree_cleanup(struct loc_network_tree* tree) {
+       int r;
+
+       // Deduplicate the tree
+       r = loc_network_tree_dedup(tree);
+       if (r)
+               return r;
+
+       // Merge networks
+       r = loc_network_tree_merge(tree);
+       if (r) {
+               ERROR(tree->ctx, "Could not merge networks: %m\n");
+               return r;
+       }
+
+       // Delete any unneeded nodes
+       r = loc_network_tree_delete_nodes(tree);
+       if (r)
+               return r;
+
+       return 0;
+}
index b6b45a6051b3affe404227039a5af7709ed3eb00..fc76e59af75eee4d8a664740e34a206b0ef5b01e 100644 (file)
@@ -264,7 +264,7 @@ LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* oth
        return 0;
 }
 
-static int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
+int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
        int r;
 
        // Check country code
@@ -575,7 +575,7 @@ LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
        return subnets;
 }
 
-static int loc_network_merge(struct loc_network** n,
+int loc_network_merge(struct loc_network** n,
                struct loc_network* n1, struct loc_network* n2) {
        struct loc_network* network = NULL;
        struct in6_addr address;
@@ -584,6 +584,8 @@ static int loc_network_merge(struct loc_network** n,
        // Reset pointer
        *n = NULL;
 
+       DEBUG(n1->ctx, "Attempting to merge %s and %s\n", loc_network_str(n1), loc_network_str(n2));
+
        // Family must match
        if (n1->family != n2->family)
                return 0;
@@ -687,607 +689,3 @@ int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** n
 
        return 0;
 }
-
-struct loc_network_tree {
-       struct loc_ctx* ctx;
-       int refcount;
-
-       struct loc_network_tree_node* root;
-};
-
-struct loc_network_tree_node {
-       struct loc_ctx* ctx;
-       int refcount;
-
-       struct loc_network_tree_node* zero;
-       struct loc_network_tree_node* one;
-
-       struct loc_network* network;
-
-       // Set if deleted
-       int deleted:1;
-};
-
-int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
-       struct loc_network_tree* t = calloc(1, sizeof(*t));
-       if (!t)
-               return 1;
-
-       t->ctx = loc_ref(ctx);
-       t->refcount = 1;
-
-       // Create the root node
-       int r = loc_network_tree_node_new(ctx, &t->root);
-       if (r) {
-               loc_network_tree_unref(t);
-               return r;
-       }
-
-       DEBUG(t->ctx, "Network tree allocated at %p\n", t);
-       *tree = t;
-       return 0;
-}
-
-struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
-       return loc_network_tree_node_ref(tree->root);
-}
-
-static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
-       struct loc_network_tree_node** n = NULL;
-       int r;
-
-       switch (path) {
-               case 0:
-                       n = &node->zero;
-                       break;
-
-               case 1:
-                       n = &node->one;
-                       break;
-
-               default:
-                       errno = EINVAL;
-                       return NULL;
-       }
-
-       // If the node existed, but has been deleted, we undelete it
-       if (*n && (*n)->deleted) {
-               (*n)->deleted = 0;
-
-       // If the desired node doesn't exist, yet, we will create it
-       } else if (!*n) {
-               r = loc_network_tree_node_new(node->ctx, n);
-               if (r)
-                       return NULL;
-       }
-
-       return *n;
-}
-
-static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
-       struct loc_network_tree_node* node = tree->root;
-
-       for (unsigned int i = 0; i < prefix; i++) {
-               // Check if the ith bit is one or zero
-               node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
-       }
-
-       return node;
-}
-
-static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
-               int(*filter_callback)(struct loc_network* network, void* data),
-               int(*callback)(struct loc_network* network, void* data), void* data) {
-       int r;
-
-       // If the node has been deleted, don't process it
-       if (node->deleted)
-               return 0;
-
-       // Finding a network ends the walk here
-       if (node->network) {
-               if (filter_callback) {
-                       int f = filter_callback(node->network, data);
-                       if (f < 0)
-                               return f;
-
-                       // Skip network if filter function returns value greater than zero
-                       if (f > 0)
-                               return 0;
-               }
-
-               r = callback(node->network, data);
-               if (r)
-                       return r;
-       }
-
-       // Walk down on the left side of the tree first
-       if (node->zero) {
-               r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
-               if (r)
-                       return r;
-       }
-
-       // Then walk on the other side
-       if (node->one) {
-               r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
-               if (r)
-                       return r;
-       }
-
-       return 0;
-}
-
-int loc_network_tree_walk(struct loc_network_tree* tree,
-               int(*filter_callback)(struct loc_network* network, void* data),
-               int(*callback)(struct loc_network* network, void* data), void* data) {
-       return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
-}
-
-static void loc_network_tree_free(struct loc_network_tree* tree) {
-       DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
-
-       loc_network_tree_node_unref(tree->root);
-
-       loc_unref(tree->ctx);
-       free(tree);
-}
-
-struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
-       if (--tree->refcount > 0)
-               return tree;
-
-       loc_network_tree_free(tree);
-       return NULL;
-}
-
-static int __loc_network_tree_dump(struct loc_network* network, void* data) {
-       DEBUG(network->ctx, "Dumping network at %p\n", network);
-
-       const char* s = loc_network_str(network);
-       if (!s)
-               return 1;
-
-       INFO(network->ctx, "%s\n", s);
-
-       return 0;
-}
-
-int loc_network_tree_dump(struct loc_network_tree* tree) {
-       DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
-
-       return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
-}
-
-int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
-       DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
-
-       struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
-                       &network->first_address, network->prefix);
-       if (!node) {
-               ERROR(tree->ctx, "Could not find a node\n");
-               return -ENOMEM;
-       }
-
-       // Check if node has not been set before
-       if (node->network) {
-               DEBUG(tree->ctx, "There is already a network at this path: %s\n",
-                       loc_network_str(node->network));
-               return -EBUSY;
-       }
-
-       // Point node to the network
-       node->network = loc_network_ref(network);
-
-       return 0;
-}
-
-static int loc_network_tree_delete_network(
-               struct loc_network_tree* tree, struct loc_network* network) {
-       struct loc_network_tree_node* node = NULL;
-
-       DEBUG(tree->ctx, "Deleting network %s from tree...\n", loc_network_str(network));
-
-       node = loc_network_tree_get_path(tree, &network->first_address, network->prefix);
-       if (!node) {
-               ERROR(tree->ctx, "Network was not found in tree %s\n", loc_network_str(network));
-               return 1;
-       }
-
-       // Drop the network
-       if (node->network) {
-               loc_network_unref(node->network);
-               node->network = NULL;
-       }
-
-       // Mark the node as deleted if it was a leaf
-       if (!node->zero && !node->one)
-               node->deleted = 1;
-
-       return 0;
-}
-
-static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
-       size_t counter = 1;
-
-       // Don't count deleted nodes
-       if (node->deleted)
-               return 0;
-
-       if (node->zero)
-               counter += __loc_network_tree_count_nodes(node->zero);
-
-       if (node->one)
-               counter += __loc_network_tree_count_nodes(node->one);
-
-       return counter;
-}
-
-size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
-       return __loc_network_tree_count_nodes(tree->root);
-}
-
-int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
-       struct loc_network_tree_node* n = calloc(1, sizeof(*n));
-       if (!n)
-               return -ENOMEM;
-
-       n->ctx = loc_ref(ctx);
-       n->refcount = 1;
-
-       n->zero = n->one = NULL;
-
-       DEBUG(n->ctx, "Network node allocated at %p\n", n);
-       *node = n;
-       return 0;
-}
-
-struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
-       if (node)
-               node->refcount++;
-
-       return node;
-}
-
-static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
-       DEBUG(node->ctx, "Releasing network node at %p\n", node);
-
-       if (node->network)
-               loc_network_unref(node->network);
-
-       if (node->zero)
-               loc_network_tree_node_unref(node->zero);
-
-       if (node->one)
-               loc_network_tree_node_unref(node->one);
-
-       loc_unref(node->ctx);
-       free(node);
-}
-
-struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
-       if (--node->refcount > 0)
-               return node;
-
-       loc_network_tree_node_free(node);
-       return NULL;
-}
-
-struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
-       if (index == 0)
-               node = node->zero;
-       else
-               node = node->one;
-
-       if (!node)
-               return NULL;
-
-       return loc_network_tree_node_ref(node);
-}
-
-int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
-       return (!!node->network);
-}
-
-struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
-       return loc_network_ref(node->network);
-}
-
-/*
-       Merge the tree!
-*/
-
-struct loc_network_tree_merge_ctx {
-       struct loc_network_tree* tree;
-       struct loc_network_list* networks;
-       unsigned int merged;
-};
-
-static int loc_network_tree_merge_step(struct loc_network* network, void* data) {
-       struct loc_network_tree_merge_ctx* ctx = (struct loc_network_tree_merge_ctx*)data;
-       struct loc_network* n = NULL;
-       struct loc_network* m = NULL;
-       int r;
-
-       // How many networks do we have?
-       size_t i = loc_network_list_size(ctx->networks);
-
-       // If the list is empty, just add the network
-       if (i == 0)
-               return loc_network_list_push(ctx->networks, network);
-
-       while (i--) {
-               // Fetch the last network of the list
-               n = loc_network_list_get(ctx->networks, i);
-
-               // Try to merge the two networks
-               r = loc_network_merge(&m, n, network);
-               if (r)
-                       goto ERROR;
-
-               // Did we get a result?
-               if (m) {
-                       DEBUG(ctx->tree->ctx, "Merged networks %s + %s -> %s\n",
-                               loc_network_str(n), loc_network_str(network), loc_network_str(m));
-
-                       // Add the new network
-                       r = loc_network_tree_add_network(ctx->tree, m);
-                       switch (r) {
-                               case 0:
-                                       break;
-
-                               // There might already be a network
-                               case -EBUSY:
-                                       r = 0;
-                                       goto ERROR;
-
-                               default:
-                                       goto ERROR;
-                       }
-
-                       // Remove the merge networks
-                       r = loc_network_tree_delete_network(ctx->tree, network);
-                       if (r)
-                               goto ERROR;
-
-                       r = loc_network_tree_delete_network(ctx->tree, n);
-                       if (r)
-                               goto ERROR;
-
-                       // Add the new network to the stack
-                       r = loc_network_list_push(ctx->networks, m);
-                       if (r)
-                               goto ERROR;
-
-                       // Remove the previous network from the stack
-                       r = loc_network_list_remove(ctx->networks, n);
-                       if (r)
-                               goto ERROR;
-
-                       // Count merges
-                       ctx->merged++;
-
-                       // Try merging the new network with others
-                       r = loc_network_tree_merge_step(m, data);
-                       if (r)
-                               goto ERROR;
-
-                       loc_network_unref(m);
-                       m = NULL;
-
-                       // Once we have found a merge, we are done
-                       break;
-
-               // If we could not merge the two networks, we add the current one
-               } else {
-                       r = loc_network_list_push(ctx->networks, network);
-                       if (r)
-                               goto ERROR;
-               }
-
-               loc_network_unref(n);
-               n = NULL;
-       }
-
-       const unsigned int prefix = loc_network_prefix(network);
-
-       // Remove any networks that we cannot merge
-       loc_network_list_remove_with_prefix_smaller_than(ctx->networks, prefix);
-
-ERROR:
-       if (m)
-               loc_network_unref(m);
-       if (n)
-               loc_network_unref(n);
-
-       return r;
-}
-
-static int loc_network_tree_merge(struct loc_network_tree* tree) {
-       struct loc_network_tree_merge_ctx ctx = {
-               .tree     = tree,
-               .networks = NULL,
-               .merged   = 0,
-       };
-       int r;
-
-       // Create a new list
-       r = loc_network_list_new(tree->ctx, &ctx.networks);
-       if (r)
-               goto ERROR;
-
-       // Walk through the entire tree
-       r = loc_network_tree_walk(tree, NULL, loc_network_tree_merge_step, &ctx);
-       if (r)
-               goto ERROR;
-
-       DEBUG(tree->ctx, "%u network(s) have been merged\n", ctx.merged);
-
-ERROR:
-       if (ctx.networks)
-               loc_network_list_unref(ctx.networks);
-
-       return r;
-}
-
-/*
-       Deduplicate the tree
-*/
-
-struct loc_network_tree_dedup_ctx {
-       struct loc_network_tree* tree;
-       struct loc_network_list* stack;
-       unsigned int removed;
-};
-
-static int loc_network_tree_dedup_step(struct loc_network* network, void* data) {
-       struct loc_network_tree_dedup_ctx* ctx = (struct loc_network_tree_dedup_ctx*)data;
-       struct loc_network* n = NULL;
-       int r;
-
-       // First call when we have not seen any networks, yet
-       if (loc_network_list_empty(ctx->stack))
-               return loc_network_list_push(ctx->stack, network);
-
-       const unsigned int prefix = loc_network_prefix(network);
-
-       // Remove any networks that are not interesting
-       loc_network_list_remove_with_prefix_smaller_than(ctx->stack, prefix);
-
-       for (int i = loc_network_list_size(ctx->stack) - 1; i >= 0; i--) {
-               n = loc_network_list_get(ctx->stack, i);
-
-               // Is network a subnet?
-               if (loc_network_is_subnet(n, network)) {
-                       // Do all properties match?
-                       if (loc_network_properties_cmp(n, network) == 0) {
-                               r = loc_network_tree_delete_network(ctx->tree, network);
-                               if (r)
-                                       return r;
-
-                               // Count
-                               ctx->removed++;
-
-                               // Once we removed the subnet, we are done
-                               goto END;
-                       }
-
-                       // Once we found a subnet, we are done
-                       break;
-               }
-       }
-
-       // If network did not get removed, we push it into the stack
-       r = loc_network_list_push(ctx->stack, network);
-       if (r)
-               return r;
-
-END:
-       if (n)
-               loc_network_unref(n);
-
-       return r;
-}
-
-static int loc_network_tree_dedup(struct loc_network_tree* tree) {
-       struct loc_network_tree_dedup_ctx ctx = {
-               .tree    = tree,
-               .stack   = NULL,
-               .removed = 0,
-       };
-       int r;
-
-       r = loc_network_list_new(tree->ctx, &ctx.stack);
-       if (r)
-               return r;
-
-       // Walk through the entire tree
-       r = loc_network_tree_walk(tree, NULL, loc_network_tree_dedup_step, &ctx);
-       if (r)
-               goto ERROR;
-
-       DEBUG(tree->ctx, "%u network(s) have been removed\n", ctx.removed);
-
-ERROR:
-       if (ctx.stack)
-               loc_network_list_unref(ctx.stack);
-
-       return r;
-}
-
-static int loc_network_tree_delete_node(struct loc_network_tree* tree,
-               struct loc_network_tree_node** node) {
-       struct loc_network_tree_node* n = *node;
-       int r0 = 1;
-       int r1 = 1;
-
-       // Return for nodes that have already been deleted
-       if (n->deleted)
-               goto DELETE;
-
-       // Delete zero
-       if (n->zero) {
-               r0 = loc_network_tree_delete_node(tree, &n->zero);
-               if (r0 < 0)
-                       return r0;
-       }
-
-       // Delete one
-       if (n->one) {
-               r1 = loc_network_tree_delete_node(tree, &n->one);
-               if (r1 < 0)
-                       return r1;
-       }
-
-       // Don't delete this node if we are a leaf
-       if (n->network)
-               return 0;
-
-       // Don't delete this node if has child nodes that we need
-       if (!r0 || !r1)
-               return 0;
-
-       // Don't delete root
-       if (tree->root == n)
-               return 0;
-
-DELETE:
-       // It is now safe to delete the node
-       loc_network_tree_node_unref(n);
-       *node = NULL;
-
-       return 1;
-}
-
-static int loc_network_tree_delete_nodes(struct loc_network_tree* tree) {
-       int r;
-
-       r = loc_network_tree_delete_node(tree, &tree->root);
-       if (r < 0)
-               return r;
-
-       return 0;
-}
-
-int loc_network_tree_cleanup(struct loc_network_tree* tree) {
-       int r;
-
-       // Deduplicate the tree
-       r = loc_network_tree_dedup(tree);
-       if (r)
-               return r;
-
-       // Merge networks
-       r = loc_network_tree_merge(tree);
-       if (r) {
-               ERROR(tree->ctx, "Could not merge networks: %m\n");
-               return r;
-       }
-
-       // Delete any unneeded nodes
-       r = loc_network_tree_delete_nodes(tree);
-       if (r)
-               return r;
-
-       return 0;
-}
index a3cb99345c73580b7efe199ba95d7c17a20db15a..13948c282dbd199bd8fc7417c8f06d5a24c1071a 100644 (file)
@@ -40,6 +40,7 @@
 #include <libloc/database.h>
 #include <libloc/format.h>
 #include <libloc/network.h>
+#include <libloc/network-tree.h>
 #include <libloc/private.h>
 #include <libloc/writer.h>