瀏覽代碼

add trie to library directory

- add generic digit trie implementation to lib directory,
  needed from userblacklist and carrieroute
- add a note to the README
Henning Westerholt 17 年之前
父節點
當前提交
99d1967f5c
共有 4 個文件被更改,包括 434 次插入0 次删除
  1. 3 0
      lib/README
  2. 9 0
      lib/trie/Makefile
  3. 248 0
      lib/trie/dtrie.c
  4. 174 0
      lib/trie/dtrie.h

+ 3 - 0
lib/README

@@ -26,6 +26,9 @@ xcap - Common XCAP operations and structures (XCAP authorization documents
 
 Used by modules: pa, rls, dialog, rpa
 
+trie - Common digit trie implementation for prefix matching, used by
+       carrierroute and userblacklist
+
 Usage:
 -----
 

+ 9 - 0
lib/trie/Makefile

@@ -0,0 +1,9 @@
+include ../../Makefile.defs
+auto_gen=
+NAME:=trie
+MAJOR_VER=1
+MINOR_VER=0
+BUGFIX_VER=0
+LIBS=
+
+include ../../Makefile.libs

+ 248 - 0
lib/trie/dtrie.c

@@ -0,0 +1,248 @@
+/*
+ * $Id: dtrie.c 5237 2008-11-21 10:17:10Z henningw $
+ *
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of sip-router, a free SIP server.
+ *
+ * sip-router 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
+ *
+ * sip-router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * \file
+ * \brief Trie datastructure with utility functions
+ *
+ * Provides a generic trie datastructure and utility functions to
+ * initialize and manage individual nodes. Its optimized towards
+ * the usecase of a matching tree that contains only digits, e.g.
+ * for LCR or blacklist modules. Nevertheless it also supports the
+ * matching of characters when configured correctly. For normal
+ * digit only matching you need to use a branches parameter of
+ * 10, when you use 128, the complete standard ascii charset is
+ * available for matching. The trie is setup in shared memory.
+ * - Module: \ref carrierroute
+ * - Module: \ref userblacklist
+ */
+
+
+#include "dtrie.h"
+
+#include <assert.h>
+
+#include "../../dprint.h"
+#include "../../mem/shm_mem.h"
+#include "../../mem/mem.h"
+
+
+struct dtrie_node_t *dtrie_init(const unsigned int branches)
+{
+	struct dtrie_node_t *root;
+
+	root = shm_malloc(sizeof(struct dtrie_node_t));
+	if (root == NULL) {
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	LM_DBG("allocate %lu bytes for root at %p",
+			(long unsigned)sizeof(struct dtrie_node_t), root);
+	memset(root, 0, sizeof(struct dtrie_node_t));
+
+	root->child = shm_malloc(sizeof(struct dtrie_node_t *) * branches);
+	if (root->child == NULL) {
+		shm_free(root);
+		SHM_MEM_ERROR;
+		return NULL;
+	}
+	LM_DBG("allocate %lu bytes for %d root children pointer at %p",
+			(long unsigned)sizeof(struct dtrie_node_t *) * branches,
+			branches, root->child);
+	memset(root->child, 0, sizeof(struct dtrie_node_t *) * branches);
+
+	return root;
+}
+
+
+void dtrie_delete(struct dtrie_node_t *root, struct dtrie_node_t *node,
+		dt_delete_func_t delete_payload, const unsigned int branches)
+{
+	unsigned int i;
+	if (node==NULL) return;
+
+	for (i=0; i<branches; i++) {
+		dtrie_delete(root, node->child[i], delete_payload, branches);
+		node->child[i] = NULL;
+	}
+
+	if (delete_payload) {
+		delete_payload(node->data);
+		node->data = NULL;
+	}
+
+	if (node != root) {
+		LM_DBG("free node at %p", node);
+		shm_free(node);
+	}
+}
+
+
+void dtrie_destroy(struct dtrie_node_t **root, dt_delete_func_t delete_payload, const unsigned int branches)
+{
+	if ((root!=NULL) && (*root!=NULL)) {
+		dtrie_delete(*root, *root, delete_payload, branches);
+		LM_DBG("free root at %p", root);
+		shm_free(*root);
+		*root = NULL;
+	}
+}
+
+
+void dtrie_clear(struct dtrie_node_t *root, dt_delete_func_t delete_payload,
+		const unsigned int branches)
+{
+	dtrie_delete(root, root, delete_payload, branches);
+}
+
+
+int dtrie_insert(struct dtrie_node_t *root, const char *number, const unsigned int numberlen,
+		void *data, const unsigned int branches)
+{
+	struct dtrie_node_t *node = root;
+	unsigned char digit, i=0;
+
+	while (i<numberlen) {
+		if (branches==10) {
+			digit = number[i] - '0';
+			if (digit>9) {
+				LM_ERR("cannot insert non-numerical character\n");
+				return -1;
+			}
+		} else {
+			digit = number[i];
+			if (digit>127) {
+				LM_ERR("cannot insert extended ascii character\n");
+				return -1;
+			}
+		}
+
+		if (node->child[digit] == NULL) {
+			node->child[digit] = shm_malloc(sizeof(struct dtrie_node_t));
+			assert(node->child[digit] != NULL);
+			LM_DBG("allocate %lu bytes for node at %p", (long unsigned)sizeof(struct dtrie_node_t), node->child[digit]);
+			memset(node->child[digit], 0, sizeof(struct dtrie_node_t));
+
+			node->child[digit]->child = shm_malloc(sizeof(struct dtrie_node_t *) * branches);
+			assert(node->child[digit]->child != NULL);
+			LM_DBG("allocate %lu bytes for %d root children pointer at %p",
+					(long unsigned)sizeof(struct dtrie_node_t *) * branches,
+					branches, node->child[digit]->child);
+			memset(node->child[digit]->child, 0, sizeof(struct dtrie_node_t *) * branches);
+		}
+		node = node->child[digit];
+		i++;
+	}
+	node->data = data;
+	return 0;
+}
+
+
+unsigned int dtrie_size(const struct dtrie_node_t *root, const unsigned int branches)
+{
+	unsigned int i, sum = 0;
+
+	if (root == NULL) return 0;
+
+	for (i=0; i<branches; i++) {
+		sum += dtrie_size(root->child[i], branches);
+	}
+
+	return sum+1;
+}
+
+
+unsigned int dtrie_loaded_nodes(const struct dtrie_node_t *root, const unsigned int branches)
+{
+	unsigned int i, sum = 0;
+
+	if (root == NULL) return 0;
+
+	for (i=0; i<branches; i++) {
+		sum += dtrie_loaded_nodes(root->child[i], branches);
+	}
+
+	if (root->data != NULL) sum++;
+
+	return sum;
+}
+
+
+unsigned int dtrie_leaves(const struct dtrie_node_t *root, const unsigned int branches)
+{
+	unsigned int i, sum = 0, leaf = 1;
+
+	for (i=0; i<branches; i++) {
+		if (root->child[i]) {
+			sum += dtrie_leaves(root->child[i], branches);
+			leaf = 0;
+		}
+	}
+
+	return sum+leaf;
+}
+
+
+void **dtrie_longest_match(struct dtrie_node_t *root, const char *number,
+		const unsigned int numberlen, int *nmatchptr, const unsigned int branches)
+{
+	struct dtrie_node_t *node = root;
+	unsigned char digit, i = 0;
+	void **ret = NULL;
+
+	if (nmatchptr) *nmatchptr=-1;
+	if (node->data != NULL) {
+		if (nmatchptr) *nmatchptr=0;
+		ret = &node->data;
+	}
+	while (i<numberlen) {
+		if (branches==10) {
+			digit = number[i] - '0';
+			if (digit>9) return ret;
+		} else {
+			digit = number[i];
+			if (digit>127) return ret;
+		}
+		
+		if (node->child[digit] == NULL) return ret;
+		node = node->child[digit];
+		i++;
+		if (node->data != NULL) {
+			if (nmatchptr) *nmatchptr=i;
+			ret = &node->data;
+		}
+	}
+
+	return ret;
+}
+
+
+void **dtrie_contains(struct dtrie_node_t *root, const char *number,
+		const unsigned int numberlen, const unsigned int branches)
+{
+	int nmatch;
+	void **ret;
+	ret = dtrie_longest_match(root, number, numberlen, &nmatch, branches);
+
+	if (nmatch == numberlen) return ret;
+	return NULL;
+}

+ 174 - 0
lib/trie/dtrie.h

@@ -0,0 +1,174 @@
+/*
+ * $Id: dtrie.h 5237 2008-11-21 10:17:10Z henningw $
+ *
+ * Copyright (C) 2008 1&1 Internet AG
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * \file
+ * \brief Trie datastructure with utility functions
+ *
+ * Provides a generic trie datastructure and utility functions to
+ * initialize and manage individual nodes. Its optimized towards
+ * the usecase of a matching tree that contains only digits, e.g.
+ * for LCR or blacklist modules. Nevertheless it also supports the
+ * matching of characters when configured correctly. For normal
+ * digit only matching you need to use a branches parameter of
+ * 10, when you use 128, the complete standard ascii charset is
+ * available for matching. The trie is setup in shared memory.
+ * - Module: \ref carrierroute
+ * - Module: \ref userblacklist
+ */
+
+#ifndef _DTRIE_H_
+#define _DTRIE_H_
+
+
+/*! Trie node */
+struct dtrie_node_t {
+	struct dtrie_node_t **child; /*!< children */
+	void *data; /*!< custom data */
+};
+
+
+/*! Function signature for destroying the payload. First parameter is the payload. */
+typedef void(*dt_delete_func_t)(void *);
+
+
+/*!
+ * \brief Allocates memory for the root node and initializes it
+ * \param branches number of branches in the trie
+ * \return pointer to an initialized root node on success, NULL otherwise.
+ */
+struct dtrie_node_t *dtrie_init(const unsigned int branches);
+
+
+/*!
+ * \brief Deletes a subtree and frees memory including node
+ * \param root root node of the whole tree
+ * \param node root of the subtree
+ * \param delete_payload pointer to a function for deleting payload. If NULL, it will not be used.
+ * \param branches number of branches in the trie
+ * \note Memory for the root node is not freed.
+ */
+void dtrie_delete(struct dtrie_node_t *root, struct dtrie_node_t *node,
+		dt_delete_func_t delete_payload, const unsigned int branches);
+
+
+/*!
+ * \brief Deletes the whole tree and frees the memory including the root node
+ * \param root root node
+ * \param delete_payload pointer to a function for deleting payload. If NULL, it will not be used.
+ * \param branches number of branches in the trie
+ */
+void dtrie_destroy(struct dtrie_node_t **root, dt_delete_func_t delete_payload,
+		const unsigned int branches);
+
+
+/*!
+ * \brief Deletes everything but the root node
+ *
+ * Deletes everything but the root node, the root node is new initialized.
+ * This could be used to create an empty tree like after dtrie_init. It
+ * also deletes eventual payload on all nodes including the root node.
+ * \param root root node
+ * \param delete_payload pointer to a function for deleting payload. If NULL, it will not be used.
+ * \param branches number of branches in the trie
+ */
+void dtrie_clear(struct dtrie_node_t *root, dt_delete_func_t delete_payload,
+		const unsigned int branches);
+
+
+/*!
+ * \brief Insert a number with a corresponding id
+ *
+ * Insert a number with a corresponding id. Nodes are created if necessary
+ * and the node after the last digit is marked with the given id.
+ * \param root root node
+ * \param number inserted number string
+ * \param numberlen number of individual numbers in number
+ * \param data pointer to some custom data
+ * \param branches number of branches in the trie
+ * \return 0 on success, -1 otherwise.
+ */
+int dtrie_insert(struct dtrie_node_t *root, const char *number, const unsigned int numberlen,
+		void *data, const unsigned int dtrie_size);
+
+
+/*!
+ * \brief Returns the number of nodes in the given tree
+ * \param root root node
+ * \param branches number of branches in the trie
+ * \return number of nodes in tree, at least 1
+ */
+unsigned int dtrie_size(const struct dtrie_node_t *root, const unsigned int branches);
+
+
+/*!
+ * \brief Returns the number of nodes in the given tree that holds custom data.
+ *
+ * Returns the number of nodes in the given tree that are loaded with data (data != NULL).
+ * \param root root node
+ * \param branches number of branches in the trie
+ * \return number of nodes in the tree with custom data
+ */
+unsigned int dtrie_loaded_nodes(const struct dtrie_node_t *root, const unsigned int branches);
+
+
+/*!
+ * \brief Returns the number of leaf nodes in the given tree
+ *
+ * Returns the number of leaf nodes in the given tree.
+ * On leaf nodes the leaf flag is set, on other nodes it is cleared.
+ * \param root root node
+ * \param branches number of branches in the trie
+ * \return number of leaf nodes
+ */
+unsigned int dtrie_leaves(const struct dtrie_node_t *root, const unsigned int branches);
+
+
+/*!
+ *\brief Find the longest prefix match of number in root.
+ *
+ * Find the longest prefix match of number in root.
+ * Set *data according to value in the tree.
+ * \param root root node
+ * \param number matched prefix
+ * \param numberlen length of number
+ * \param nmatchptr if not NULL store the number of matched digits or -1 if not found.
+ * \param branches number of branches in the trie
+ * \return the address of the pointer in the tree node if number is found in root, NULL if the number is not found.
+ */
+void **dtrie_longest_match(struct dtrie_node_t *root, const char *number,
+		const unsigned int numberlen, int *nmatchptr, const unsigned int branches);
+
+
+/*!
+ * \brief Check if the trie contains a number
+ * \param root root node
+ * \param number matched prefix
+ * \param numberlen length of number
+ * \param branches number of branches in the trie
+ * \return the address of the pointer in the tree node if number is found in root, NULL if the number is not found.
+ */
+void **dtrie_contains(struct dtrie_node_t *root, const char *number,
+		const unsigned int numberlen, const unsigned int branches);
+
+
+#endif