py-radix-0.10.0/0000755000076500000240000000000013166705666014341 5ustar schultzmstaff00000000000000py-radix-0.10.0/LICENSE0000644000076500000240000001416413166705156015346 0ustar schultzmstaff00000000000000The latest (post-2014) Python code is subject to this license: /* * Copyright (c) 2014 Michael J. Schultz * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ The original (pre-2014) Python binding code is subject to this license: /* * Copyright (c) 2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ This underlying radix tree code is derived from MRT (http://www.mrtd.net/) and is subject to the following license: /* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ Windows platforms link in code subject the the following licenses: /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ py-radix-0.10.0/MANIFEST.in0000644000076500000240000000007213166705156016070 0ustar schultzmstaff00000000000000include LICENSE include README.rst include radix/_radix/* py-radix-0.10.0/PKG-INFO0000644000076500000240000001467413166705666015452 0ustar schultzmstaff00000000000000Metadata-Version: 1.1 Name: py-radix Version: 0.10.0 Summary: Radix tree implementation Home-page: https://github.com/mjschultz/py-radix Author: Michael J. Schultz Author-email: mjschultz@gmail.com License: BSD Description-Content-Type: UNKNOWN Description: py-radix ======== .. image:: https://travis-ci.org/mjschultz/py-radix.svg?branch=master :target: https://travis-ci.org/mjschultz/py-radix .. image:: https://coveralls.io/repos/mjschultz/py-radix/badge.png?branch=master :target: https://coveralls.io/r/mjschultz/py-radix?branch=master py-radix implements the radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. Installation ------------ Installation is a breeze via pip: :: pip install py-radix Or with the standard Python distutils incantation: :: python setup.py build python setup.py install The C extension will be built for supported python versions. If you do not want the C extension, set the environment variable ``RADIX_NO_EXT=1``. Tests are in the ``tests/`` directory and can be run with ``python setup.py nosetests``. Usage ----- A simple example that demonstrates most of the features: :: import radix # Create a new tree rtree = radix.Radix() # Adding a node returns a RadixNode object. You can create # arbitrary members in its 'data' dict to store your data rnode = rtree.add("10.0.0.0/8") rnode.data["blah"] = "whatever you want" # You can specify nodes as CIDR addresses, or networks with # separate mask lengths. The following three invocations are # identical: rnode = rtree.add("10.0.0.0/16") rnode = rtree.add("10.0.0.0", 16) rnode = rtree.add(network = "10.0.0.0", masklen = 16) # It is also possible to specify nodes using binary packed # addresses, such as those returned by the socket module # functions. In this case, the radix module will assume that # a four-byte address is an IPv4 address and a sixteen-byte # address is an IPv6 address. For example: binary_addr = inet_ntoa("172.18.22.0") rnode = rtree.add(packed = binary_addr, masklen = 23) # Exact search will only return prefixes you have entered # You can use all of the above ways to specify the address rnode = rtree.search_exact("10.0.0.0/8") # Get your data back out print rnode.data["blah"] # Use a packed address addr = socket.inet_ntoa("10.0.0.0") rnode = rtree.search_exact(packed = addr, masklen = 8) # Best-match search will return the longest matching prefix # that contains the search term (routing-style lookup) rnode = rtree.search_best("10.123.45.6") # Worst-search will return the shortest matching prefix # that contains the search term (inverse routing-style lookup) rnode = rtree.search_worst("10.123.45.6") # Covered search will return all prefixes inside the given # search term, as a list (including the search term itself, # if present in the tree) rnodes = rtree.search_covered("10.123.0.0/16") # There are a couple of implicit members of a RadixNode: print rnode.network # -> "10.0.0.0" print rnode.prefix # -> "10.0.0.0/8" print rnode.prefixlen # -> 8 print rnode.family # -> socket.AF_INET print rnode.packed # -> '\n\x00\x00\x00' # IPv6 prefixes are fully supported in the same tree rnode = rtree.add("2001:DB8::/3") rnode = rtree.add("::/0") # Use the nodes() method to return all RadixNodes created nodes = rtree.nodes() for rnode in nodes: print rnode.prefix # The prefixes() method will return all the prefixes (as a # list of strings) that have been entered prefixes = rtree.prefixes() # You can also directly iterate over the tree itself # this would save some memory if the tree is big # NB. Don't modify the tree (add or delete nodes) while # iterating otherwise you will abort the iteration and # receive a RuntimeWarning. Changing a node's data dict # is permitted. for rnode in rtree: print rnode.prefix License ------- py-radix is licensed under a ISC/BSD licence. The underlying radix tree implementation is taken (and modified) from MRTd and is subject to a 4-term BSD license. See the LICENSE file for details. Contributing ------------ Please report bugs via GitHub at https://github.com/mjschultz/py-radix/issues. Code changes can be contributed through a pull request on GitHub or emailed directly to me . The main portions of the directory tree are as follows: :: . ├── radix/*.py # Pure Python code ├── radix/_radix.c # C extension code (compatible with pure python code) ├── radix/_radix/* # C extension code (compatible with pure python code) ├── tests/ # Tests (regression and unit) └── setup.py # Standard setup.py for installation/testing/etc. Keywords: radix tree trie python routing networking Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Networking Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 py-radix-0.10.0/py_radix.egg-info/0000755000076500000240000000000013166705666017652 5ustar schultzmstaff00000000000000py-radix-0.10.0/py_radix.egg-info/dependency_links.txt0000644000076500000240000000000113166705666023720 0ustar schultzmstaff00000000000000 py-radix-0.10.0/py_radix.egg-info/PKG-INFO0000644000076500000240000001467413166705666020763 0ustar schultzmstaff00000000000000Metadata-Version: 1.1 Name: py-radix Version: 0.10.0 Summary: Radix tree implementation Home-page: https://github.com/mjschultz/py-radix Author: Michael J. Schultz Author-email: mjschultz@gmail.com License: BSD Description-Content-Type: UNKNOWN Description: py-radix ======== .. image:: https://travis-ci.org/mjschultz/py-radix.svg?branch=master :target: https://travis-ci.org/mjschultz/py-radix .. image:: https://coveralls.io/repos/mjschultz/py-radix/badge.png?branch=master :target: https://coveralls.io/r/mjschultz/py-radix?branch=master py-radix implements the radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. Installation ------------ Installation is a breeze via pip: :: pip install py-radix Or with the standard Python distutils incantation: :: python setup.py build python setup.py install The C extension will be built for supported python versions. If you do not want the C extension, set the environment variable ``RADIX_NO_EXT=1``. Tests are in the ``tests/`` directory and can be run with ``python setup.py nosetests``. Usage ----- A simple example that demonstrates most of the features: :: import radix # Create a new tree rtree = radix.Radix() # Adding a node returns a RadixNode object. You can create # arbitrary members in its 'data' dict to store your data rnode = rtree.add("10.0.0.0/8") rnode.data["blah"] = "whatever you want" # You can specify nodes as CIDR addresses, or networks with # separate mask lengths. The following three invocations are # identical: rnode = rtree.add("10.0.0.0/16") rnode = rtree.add("10.0.0.0", 16) rnode = rtree.add(network = "10.0.0.0", masklen = 16) # It is also possible to specify nodes using binary packed # addresses, such as those returned by the socket module # functions. In this case, the radix module will assume that # a four-byte address is an IPv4 address and a sixteen-byte # address is an IPv6 address. For example: binary_addr = inet_ntoa("172.18.22.0") rnode = rtree.add(packed = binary_addr, masklen = 23) # Exact search will only return prefixes you have entered # You can use all of the above ways to specify the address rnode = rtree.search_exact("10.0.0.0/8") # Get your data back out print rnode.data["blah"] # Use a packed address addr = socket.inet_ntoa("10.0.0.0") rnode = rtree.search_exact(packed = addr, masklen = 8) # Best-match search will return the longest matching prefix # that contains the search term (routing-style lookup) rnode = rtree.search_best("10.123.45.6") # Worst-search will return the shortest matching prefix # that contains the search term (inverse routing-style lookup) rnode = rtree.search_worst("10.123.45.6") # Covered search will return all prefixes inside the given # search term, as a list (including the search term itself, # if present in the tree) rnodes = rtree.search_covered("10.123.0.0/16") # There are a couple of implicit members of a RadixNode: print rnode.network # -> "10.0.0.0" print rnode.prefix # -> "10.0.0.0/8" print rnode.prefixlen # -> 8 print rnode.family # -> socket.AF_INET print rnode.packed # -> '\n\x00\x00\x00' # IPv6 prefixes are fully supported in the same tree rnode = rtree.add("2001:DB8::/3") rnode = rtree.add("::/0") # Use the nodes() method to return all RadixNodes created nodes = rtree.nodes() for rnode in nodes: print rnode.prefix # The prefixes() method will return all the prefixes (as a # list of strings) that have been entered prefixes = rtree.prefixes() # You can also directly iterate over the tree itself # this would save some memory if the tree is big # NB. Don't modify the tree (add or delete nodes) while # iterating otherwise you will abort the iteration and # receive a RuntimeWarning. Changing a node's data dict # is permitted. for rnode in rtree: print rnode.prefix License ------- py-radix is licensed under a ISC/BSD licence. The underlying radix tree implementation is taken (and modified) from MRTd and is subject to a 4-term BSD license. See the LICENSE file for details. Contributing ------------ Please report bugs via GitHub at https://github.com/mjschultz/py-radix/issues. Code changes can be contributed through a pull request on GitHub or emailed directly to me . The main portions of the directory tree are as follows: :: . ├── radix/*.py # Pure Python code ├── radix/_radix.c # C extension code (compatible with pure python code) ├── radix/_radix/* # C extension code (compatible with pure python code) ├── tests/ # Tests (regression and unit) └── setup.py # Standard setup.py for installation/testing/etc. Keywords: radix tree trie python routing networking Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Networking Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 py-radix-0.10.0/py_radix.egg-info/SOURCES.txt0000644000076500000240000000041313166705666021534 0ustar schultzmstaff00000000000000LICENSE MANIFEST.in README.rst setup.cfg setup.py py_radix.egg-info/PKG-INFO py_radix.egg-info/SOURCES.txt py_radix.egg-info/dependency_links.txt py_radix.egg-info/top_level.txt radix/__init__.py radix/_radix.c radix/radix.py radix/_radix/radix.c radix/_radix/radix.hpy-radix-0.10.0/py_radix.egg-info/top_level.txt0000644000076500000240000000000613166705666022400 0ustar schultzmstaff00000000000000radix py-radix-0.10.0/radix/0000755000076500000240000000000013166705666015450 5ustar schultzmstaff00000000000000py-radix-0.10.0/radix/__init__.py0000644000076500000240000000230313166705503017545 0ustar schultzmstaff00000000000000try: from ._radix import Radix as _Radix except Exception as e: from .radix import Radix as _Radix __version__ = '0.10.0' __all__ = ['Radix'] # This acts as an entrypoint to the underlying object (be it a C # extension or pure python representation, pickle files will work) class Radix(object): def __init__(self): self._radix = _Radix() self.add = self._radix.add self.delete = self._radix.delete self.search_exact = self._radix.search_exact self.search_best = self._radix.search_best self.search_worst = self._radix.search_worst self.search_covered = self._radix.search_covered self.search_covering = self._radix.search_covering self.nodes = self._radix.nodes self.prefixes = self._radix.prefixes def __iter__(self): for elt in self._radix: yield elt def __getstate__(self): return [(elt.prefix, elt.data) for elt in self] def __setstate__(self, state): for prefix, data in state: node = self._radix.add(prefix) for key in data: node.data[key] = data[key] def __reduce__(self): return (Radix, (), self.__getstate__()) py-radix-0.10.0/radix/_radix/0000755000076500000240000000000013166705666016716 5ustar schultzmstaff00000000000000py-radix-0.10.0/radix/_radix/radix.c0000644000076500000240000007140513166705156020172 0ustar schultzmstaff00000000000000/* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "Python.h" #include #include #include #include #include "radix.h" /* $Id$ */ /* * Originally from MRT include/defs.h * $MRTId: defs.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define BIT_TEST(f, b) ((f) & (b)) #define BIT_TEST_SEARCH_BIT(addr, bit) \ BIT_TEST((addr)[(bit) >> 3], 0x80 >> ((bit) & 0x07)) #define BIT_TEST_SEARCH(addr, node) BIT_TEST_SEARCH_BIT(addr, (node)->bit) /* * Originally from MRT include/mrt.h * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define prefix_tochar(prefix) ((char *)&(prefix)->add) #define prefix_touchar(prefix) ((u_char *)&(prefix)->add) #define RADIX_SEARCH_FOREACH(node, head, prefix) \ for ((node) = (head); \ (node) != NULL && (node)->bit < (prefix)->bitlen; \ (node) = BIT_TEST_SEARCH(prefix_touchar(prefix), node) ? (node)->r : (node)->l) #define RADIX_SEARCH_FOREACH_INCLUSIVE(node, head, prefix) \ for ((node) = (head); \ (node) != NULL && (node)->bit <= (prefix)->bitlen; \ (node) = BIT_TEST_SEARCH(prefix_touchar(prefix), node) ? (node)->r : (node)->l) #define RADIX_PHEAD_BY_PREFIX(tree, prefix) \ ((prefix)->family == AF_INET ? &(tree)->head_ipv4 : &(tree)->head_ipv6) #define RADIX_HEAD_BY_PREFIX(tree, prefix) \ ((prefix)->family == AF_INET ? (tree)->head_ipv4 : (tree)->head_ipv6) #define RADIX_MAXBITS_BY_PREFIX(prefix) \ ((prefix)->family == AF_INET ? 32 : 128) #define RADIX_MIN(a, b) ((a) < (b) ? (a) : (b)) /* * Originally from MRT lib/mrt/prefix.c * $MRTId: prefix.c,v 1.1.1.1 2000/08/14 18:46:11 labovit Exp $ */ static int comp_with_mask(u_char *addr, u_char *dest, u_int mask) { if (memcmp(addr, dest, mask / 8) == 0) { u_int n = mask / 8; u_int m = ((~0) << (8 - (mask % 8))); if (mask % 8 == 0 || (addr[n] & m) == (dest[n] & m)) return (1); } return (0); } static prefix_t *New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix) { int dynamic_allocated = 0; int default_bitlen = 32; if (family == AF_INET6) { default_bitlen = 128; if (prefix == NULL) { if ((prefix = PyMem_Malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin6, dest, 16); } else if (family == AF_INET) { if (prefix == NULL) { if ((prefix = PyMem_Malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin, dest, 4); } else return (NULL); prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen; prefix->family = family; prefix->ref_count = 0; if (dynamic_allocated) prefix->ref_count++; return (prefix); } static prefix_t *Ref_Prefix(prefix_t *prefix) { if (prefix == NULL) return (NULL); if (prefix->ref_count == 0) { /* make a copy in case of a static prefix */ return (New_Prefix2(prefix->family, &prefix->add, prefix->bitlen, NULL)); } prefix->ref_count++; return (prefix); } void Deref_Prefix(prefix_t *prefix) { if (prefix == NULL) return; prefix->ref_count--; if (prefix->ref_count <= 0) { PyMem_Free(prefix); return; } } /* * Originally from MRT lib/radix/radix.c * $MRTId: radix.c,v 1.1.1.1 2000/08/14 18:46:13 labovit Exp $ */ /* these routines support continuous mask only */ radix_tree_t *New_Radix(void) { radix_tree_t *radix; if ((radix = PyMem_Malloc(sizeof(*radix))) == NULL) return (NULL); memset(radix, '\0', sizeof(*radix)); radix->head_ipv4 = NULL; radix->head_ipv6 = NULL; radix->num_active_node = 0; return (radix); } /* * if func is supplied, it will be called as func(node->data) * before deleting the node */ static void radix_clear_head(radix_tree_t *radix, radix_node_t *head, rdx_cb_t func, void *cbctx) { if (head) { radix_node_t *Xstack[RADIX_MAXBITS + 1]; radix_node_t **Xsp = Xstack; radix_node_t *Xrn = head; while (Xrn) { radix_node_t *l = Xrn->l; radix_node_t *r = Xrn->r; if (Xrn->prefix) { Deref_Prefix(Xrn->prefix); if (Xrn->data && func) func(Xrn, cbctx); } PyMem_Free(Xrn); radix->num_active_node--; if (l) { if (r) *Xsp++ = r; Xrn = l; } else if (r) { Xrn = r; } else if (Xsp != Xstack) { Xrn = *(--Xsp); } else { Xrn = (radix_node_t *) 0; } } } } void Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { radix_clear_head(radix, radix->head_ipv4, func, cbctx); radix_clear_head(radix, radix->head_ipv6, func, cbctx); } void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { Clear_Radix(radix, func, cbctx); PyMem_Free(radix); } /* * if func is supplied, it will be called as func(node->prefix, node->data) */ void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { radix_node_t *node; RADIX_TREE_WALK(radix, node) { func(node, cbctx); } RADIX_TREE_WALK_END; } radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node, *head; u_int bitlen; if ((head = RADIX_HEAD_BY_PREFIX(radix, prefix)) == NULL) return (NULL); bitlen = prefix->bitlen; RADIX_SEARCH_FOREACH(node, head, prefix); if (node == NULL || node->bit > bitlen || node->prefix == NULL) return (NULL); if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), bitlen)) return (node); return (NULL); } /* seach for a node without checking if it is a "real" node */ radix_node_t *radix_search_node(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node, *head, *node_iter; int right_mismatch = 0; int left_mismatch = 0; u_int bitlen; if ((head = RADIX_HEAD_BY_PREFIX(radix, prefix)) == NULL) return (NULL); bitlen = prefix->bitlen; RADIX_SEARCH_FOREACH(node, head, prefix); if (node == NULL) return (NULL); // if the node has a prefix we can (and must to avoid false negatives) check directly if (node->prefix) { if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), bitlen)) return (node); else return (NULL); } // We can risk landing on an intermediate node (no set prefix), that doesn't match the wanted prefix // When this happens we have to check the two subtrees, if a subtree does not match, that part should // not be returned. If both match the entire node should be returned. If none match, null is returned. RADIX_WALK(node->r, node_iter) { if (node_iter->data != NULL) { if ( ! comp_with_mask(prefix_touchar(node_iter->prefix), prefix_touchar(prefix), bitlen)) { right_mismatch = 1; break; } } } RADIX_WALK_END; RADIX_WALK(node->l, node_iter) { if (node_iter->data != NULL) { if ( ! comp_with_mask(prefix_touchar(node_iter->prefix), prefix_touchar(prefix), bitlen)) { left_mismatch = 1; break; } } } RADIX_WALK_END; if (right_mismatch && left_mismatch) { return (NULL); } if (right_mismatch) { return node->l; } if (left_mismatch) { return node->r; } return node; } /* if inclusive != 0, "best" may be the given prefix itself */ radix_node_t *radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive) { radix_node_t *node, *head; radix_node_t *stack[RADIX_MAXBITS + 1]; u_int bitlen; int cnt = 0; if ((head = RADIX_HEAD_BY_PREFIX(radix, prefix)) == NULL) return (NULL); bitlen = prefix->bitlen; RADIX_SEARCH_FOREACH_INCLUSIVE(node, head, prefix) { if (node->prefix && (inclusive || node->bit != bitlen)) stack[cnt++] = node; } if (cnt <= 0) return (NULL); while (--cnt >= 0) { node = stack[cnt]; if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), node->prefix->bitlen) && node->prefix->bitlen <= bitlen) return (node); } return (NULL); } radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix) { return (radix_search_best2(radix, prefix, 1)); } /* if inclusive != 0, "worst" may be the given prefix itself */ radix_node_t *radix_search_worst2(radix_tree_t *radix, prefix_t *prefix, int inclusive) { radix_node_t *node, *head; radix_node_t *stack[RADIX_MAXBITS + 1]; u_int bitlen; int cnt = 0; int iterator; if ((head = RADIX_HEAD_BY_PREFIX(radix, prefix)) == NULL) return (NULL); bitlen = prefix->bitlen; RADIX_SEARCH_FOREACH_INCLUSIVE(node, head, prefix) { if (node->prefix && (inclusive || node->bit != bitlen)) stack[cnt++] = node; } if (cnt <= 0) return (NULL); for (iterator = 0; iterator < cnt; ++iterator) { node = stack[iterator]; if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), node->prefix->bitlen)) return (node); } return (NULL); } radix_node_t *radix_search_worst(radix_tree_t *radix, prefix_t *prefix) { return (radix_search_worst2(radix, prefix, 1)); } int radix_search_covering(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx) { radix_node_t *node; int rc = 0; node = radix_search_best(radix, prefix); if (node == NULL) return (0); do { if (!node->prefix) continue; if ((rc = func(node, cbctx)) != 0) break; } while ((node = node->parent) != NULL); return (rc); } int radix_search_covered(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx, int inclusive) { radix_node_t *node, *prefixed_node, *prev_node; struct { radix_node_t *node; enum { RADIX_STATE_LEFT = 0, RADIX_STATE_RIGHT, RADIX_STATE_SELF, } state; int checked; } stack[RADIX_MAXBITS + 1], *pst; int stackpos; int rc; #define COMP_NODE_PREFIX(node, prefix_) \ comp_with_mask(prefix_touchar((node)->prefix), \ prefix_touchar(prefix_), \ RADIX_MIN((node)->prefix->bitlen, (prefix_)->bitlen)) prev_node = NULL; prefixed_node = NULL; RADIX_SEARCH_FOREACH_INCLUSIVE( node, RADIX_HEAD_BY_PREFIX(radix, prefix), prefix) { prev_node = node; if (node->bit == prefix->bitlen) break; if (node->prefix != NULL) prefixed_node = node; } if (node == NULL) { if (prev_node == NULL) return (0); node = prev_node; } else { /* node->bit >= prefix->bitlen */ if (node->prefix != NULL) prefixed_node = node; } if (prefixed_node != NULL && !COMP_NODE_PREFIX(prefixed_node, prefix)) return (0); stack[0].state = RADIX_STATE_LEFT; stack[0].node = node; stack[0].checked = (node == prefixed_node && node->bit >= prefix->bitlen); stackpos = 0; while (stackpos >= 0) { pst = stack + stackpos; switch (pst->state) { case RADIX_STATE_LEFT: case RADIX_STATE_RIGHT: if (pst->state == RADIX_STATE_LEFT) { pst->state = RADIX_STATE_RIGHT; node = pst->node->l; } else { pst->state = RADIX_STATE_SELF; node = pst->node->r; } if (node == NULL) continue; /* skip foreign nodes */ if (!pst->checked && node->prefix != NULL && !COMP_NODE_PREFIX(node, prefix)) continue; stackpos++; pst = stack + stackpos; pst->state = RADIX_STATE_LEFT; pst->node = node; pst->checked = (pst[-1].checked || node->prefix != NULL); break; case RADIX_STATE_SELF: if (stackpos > 0 || (inclusive ? pst->node->bit >= prefix->bitlen: pst->node->bit > prefix->bitlen)) { if (pst->node->prefix != NULL && (rc = func(pst->node, cbctx)) != 0) return (rc); } stackpos--; break; } } #undef COMP_NODE_PREFIX return (0); } int radix_search_intersect(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx) { int rc; if ((rc = radix_search_covering(radix, prefix, func, cbctx)) == 0) rc = radix_search_covered(radix, prefix, func, cbctx, 0); return (rc); } radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix) { radix_node_t **phead, *node, *new_node, *parent, *glue; u_char *addr, *test_addr; u_int bitlen, check_bit, differ_bit, maxbits; u_int i, j, r; maxbits = RADIX_MAXBITS_BY_PREFIX(prefix); phead = RADIX_PHEAD_BY_PREFIX(radix, prefix); if (*phead == NULL) { if ((node = PyMem_Malloc(sizeof(*node))) == NULL) return (NULL); memset(node, '\0', sizeof(*node)); node->bit = prefix->bitlen; node->prefix = Ref_Prefix(prefix); node->parent = NULL; node->l = node->r = NULL; node->data = NULL; *phead = node; radix->num_active_node++; return (node); } addr = prefix_touchar(prefix); bitlen = prefix->bitlen; node = *phead; while (node->bit < bitlen || node->prefix == NULL) { if (node->bit < maxbits && BIT_TEST_SEARCH(addr, node)) { if (node->r == NULL) break; node = node->r; } else { if (node->l == NULL) break; node = node->l; } } test_addr = prefix_touchar(node->prefix); /* find the first bit different */ check_bit = (node->bit < bitlen) ? node->bit : bitlen; differ_bit = 0; for (i = 0; i * 8 < check_bit; i++) { if ((r = (addr[i] ^ test_addr[i])) == 0) { differ_bit = (i + 1) * 8; continue; } /* I know the better way, but for now */ for (j = 0; j < 8; j++) { if (BIT_TEST(r, (0x80 >> j))) break; } /* must be found */ differ_bit = i * 8 + j; break; } if (differ_bit > check_bit) differ_bit = check_bit; parent = node->parent; while (parent && parent->bit >= differ_bit) { node = parent; parent = node->parent; } if (differ_bit == bitlen && node->bit == bitlen) { if (node->prefix == NULL) node->prefix = Ref_Prefix(prefix); return (node); } if ((new_node = PyMem_Malloc(sizeof(*new_node))) == NULL) return (NULL); memset(new_node, '\0', sizeof(*new_node)); new_node->bit = prefix->bitlen; new_node->prefix = Ref_Prefix(prefix); new_node->parent = NULL; new_node->l = new_node->r = NULL; new_node->data = NULL; radix->num_active_node++; if (node->bit == differ_bit) { new_node->parent = node; if (node->bit < maxbits && BIT_TEST_SEARCH(addr, node)) node->r = new_node; else node->l = new_node; return (new_node); } if (bitlen == differ_bit) { if (bitlen < maxbits && BIT_TEST_SEARCH_BIT(test_addr, bitlen)) new_node->r = node; else new_node->l = node; new_node->parent = node->parent; if (node->parent == NULL) *phead = new_node; else if (node->parent->r == node) node->parent->r = new_node; else node->parent->l = new_node; node->parent = new_node; } else { if ((glue = PyMem_Malloc(sizeof(*glue))) == NULL) return (NULL); memset(glue, '\0', sizeof(*glue)); glue->bit = differ_bit; glue->prefix = NULL; glue->parent = node->parent; glue->data = NULL; radix->num_active_node++; if (differ_bit < maxbits && BIT_TEST_SEARCH_BIT(addr, differ_bit)) { glue->r = new_node; glue->l = node; } else { glue->r = node; glue->l = new_node; } new_node->parent = glue; if (node->parent == NULL) *phead = glue; else if (node->parent->r == node) node->parent->r = glue; else node->parent->l = glue; node->parent = glue; } return (new_node); } void radix_remove(radix_tree_t *radix, radix_node_t *node) { radix_node_t **phead, *parent, *child; phead = RADIX_PHEAD_BY_PREFIX(radix, node->prefix); if (node->r && node->l) { /* * this might be a placeholder node -- have to check and make * sure there is a prefix aossciated with it ! */ if (node->prefix != NULL) Deref_Prefix(node->prefix); node->prefix = NULL; /* Also I needed to clear data pointer -- masaki */ node->data = NULL; return; } if (node->r == NULL && node->l == NULL) { parent = node->parent; Deref_Prefix(node->prefix); PyMem_Free(node); radix->num_active_node--; if (parent == NULL) { *phead = NULL; return; } if (parent->r == node) { parent->r = NULL; child = parent->l; } else { parent->l = NULL; child = parent->r; } if (parent->prefix) return; /* we need to remove parent too */ if (parent->parent == NULL) *phead = child; else if (parent->parent->r == parent) parent->parent->r = child; else parent->parent->l = child; child->parent = parent->parent; PyMem_Free(parent); radix->num_active_node--; return; } if (node->r) child = node->r; else child = node->l; parent = node->parent; child->parent = parent; Deref_Prefix(node->prefix); PyMem_Free(node); radix->num_active_node--; if (parent == NULL) { *phead = child; return; } if (parent->r == node) parent->r = child; else parent->l = child; } /* Local additions */ static void sanitise_mask(u_char *addr, u_int masklen, u_int maskbits) { u_int i = masklen / 8; u_int j = masklen % 8; if (j != 0) { addr[i] &= (~0) << (8 - j); i++; } for (; i < maskbits / 8; i++) addr[i] = 0; } prefix_t *prefix_pton_ex(prefix_t *ret, const char *string, long len, const char **errmsg) { char save[256], *cp, *ep; struct addrinfo hints, *ai; void *addr; size_t slen; int r; /* Copy the string to parse, because we modify it */ if ((slen = strlen(string) + 1) > sizeof(save)) { *errmsg = "string too long"; return (NULL); } memcpy(save, string, slen); if ((cp = strchr(save, '/')) != NULL) { if (len != -1 ) { *errmsg = "masklen specified twice"; return (NULL); } *cp++ = '\0'; len = strtol(cp, &ep, 10); if (*cp == '\0' || *ep != '\0' || len < 0) { *errmsg = "could not parse masklen"; return (NULL); } /* More checks below */ } memset(&hints, '\0', sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if ((r = getaddrinfo(save, NULL, &hints, &ai)) != 0) { *errmsg = gai_strerror(r); return NULL; } if (ai == NULL || ai->ai_addr == NULL) { *errmsg = "getaddrinfo returned no result"; goto out; } switch (ai->ai_addr->sa_family) { case AF_INET: if (len == -1) len = 32; else if (len < 0 || len > 32) { *errmsg = "invalid prefix length"; goto out; } addr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; sanitise_mask(addr, len, 32); break; case AF_INET6: if (len == -1) len = 128; else if (len < 0 || len > 128) { *errmsg = "invalid prefix length"; goto out; } addr = &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr; sanitise_mask(addr, len, 128); break; default: goto out; } ret = New_Prefix2(ai->ai_addr->sa_family, addr, len, ret); if (ret == NULL) *errmsg = "New_Prefix2 failed"; out: freeaddrinfo(ai); return (ret); } prefix_t *prefix_pton(const char *string, long len, const char **errmsg) { return (prefix_pton_ex(NULL, string, len, errmsg)); } prefix_t *prefix_from_blob_ex(prefix_t *ret, u_char *blob, int len, int prefixlen) { int family, maxprefix; switch (len) { case 4: /* Assume AF_INET */ family = AF_INET; maxprefix = 32; break; case 16: /* Assume AF_INET6 */ family = AF_INET6; maxprefix = 128; break; default: /* Who knows? */ return NULL; } if (prefixlen == -1) prefixlen = maxprefix; if (prefixlen < 0 || prefixlen > maxprefix) return NULL; return (New_Prefix2(family, blob, prefixlen, ret)); } prefix_t *prefix_from_blob(u_char *blob, int len, int prefixlen) { return (prefix_from_blob_ex(NULL, blob, len, prefixlen)); } const char * prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len) { return (inet_ntop(prefix->family, &prefix->add, buf, len)); } const char * prefix_ntop(prefix_t *prefix, char *buf, size_t len) { char addrbuf[128]; if (prefix_addr_ntop(prefix, addrbuf, sizeof(addrbuf)) == NULL) return (NULL); snprintf(buf, len, "%s/%d", addrbuf, prefix->bitlen); return (buf); } py-radix-0.10.0/radix/_radix/radix.h0000644000076500000240000001707413166705156020201 0ustar schultzmstaff00000000000000/* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* $Id$ */ #ifndef _RADIX_H #define _RADIX_H #if defined(_MSC_VER) #include #include #pragma comment(lib,"ws2_32.lib") #else # include # include # include # include # include #endif #if defined(_MSC_VER) # define snprintf _snprintf typedef unsigned __int8 u_int8_t; typedef unsigned __int16 u_int16_t; typedef unsigned __int32 u_int32_t; size_t strlcpy(char *dst, const char *src, size_t size); #endif /* * Originally from MRT include/mrt.h * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ typedef struct _prefix_t { u_int family; /* AF_INET | AF_INET6 */ u_int bitlen; /* same as mask? */ int ref_count; /* reference count */ union { struct in_addr sin; struct in6_addr sin6; } add; } prefix_t; void Deref_Prefix(prefix_t *prefix); /* * Originally from MRT include/radix.h * $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ typedef struct _radix_node_t { u_int bit; /* flag if this node used */ prefix_t *prefix; /* who we are in radix tree */ struct _radix_node_t *l, *r; /* left and right children */ struct _radix_node_t *parent; /* may be used */ void *data; /* pointer to data */ } radix_node_t; typedef struct _radix_tree_t { radix_node_t *head_ipv4; radix_node_t *head_ipv6; int num_active_node; /* for debug purpose */ } radix_tree_t; /* Type of callback function */ typedef void (*rdx_cb_t)(radix_node_t *, void *); typedef int (*rdx_search_cb_t)(radix_node_t *, void *); radix_tree_t *New_Radix(void); void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx); void Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx); radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix); void radix_remove(radix_tree_t *radix, radix_node_t *node); radix_node_t *radix_search_node(radix_tree_t *radix, prefix_t *prefix); radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix); radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix); radix_node_t *radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive); radix_node_t *radix_search_worst(radix_tree_t *radix, prefix_t *prefix); radix_node_t *radix_search_worst2(radix_tree_t *radix, prefix_t *prefix, int inclusive); int radix_search_covering(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx); int radix_search_covered(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx, int inclusive); int radix_search_intersect(radix_tree_t *radix, prefix_t *prefix, rdx_search_cb_t func, void *cbctx); void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx); #define RADIX_MAXBITS 128 #define RADIX_WALK(Xhead, Xnode) \ do { \ radix_node_t *Xstack[RADIX_MAXBITS+1]; \ radix_node_t **Xsp = Xstack; \ radix_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (Xnode->prefix) #define RADIX_WALK_END \ if (Xrn->l) { \ if (Xrn->r) { \ *Xsp++ = Xrn->r; \ } \ Xrn = Xrn->l; \ } else if (Xrn->r) { \ Xrn = Xrn->r; \ } else if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (radix_node_t *) 0; \ } \ } \ } while (0) #define RADIX_TREE_WALK(Xtree, Xnode) \ do { \ radix_node_t *Xheads[] = { \ (Xtree)->head_ipv4, \ (Xtree)->head_ipv6, \ }; \ radix_node_t **Xpnode = &Xnode; \ unsigned Xi; \ for (Xi = 0; Xi < sizeof(Xheads) / sizeof(Xheads[0]); Xi++) { \ RADIX_WALK(Xheads[Xi], Xnode) #define RADIX_TREE_WALK_END \ RADIX_WALK_END; \ if (*Xpnode) \ break; \ } \ } while (0) /* Local additions */ prefix_t *prefix_pton(const char *string, long len, const char **errmsg); prefix_t *prefix_from_blob(u_char *blob, int len, int prefixlen); prefix_t *prefix_pton_ex(prefix_t *prefix, const char *string, long len, const char **errmsg); prefix_t *prefix_from_blob_ex(prefix_t *prefix, u_char *blob, int len, int prefixlen); const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len); const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len); #endif /* _RADIX_H */ py-radix-0.10.0/radix/_radix.c0000644000076500000240000010372013166705156017057 0ustar schultzmstaff00000000000000/* * Copyright (c) 2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "Python.h" #include "structmember.h" #include "_radix/radix.h" /* $Id$ */ /* for Py3K */ #if PY_MAJOR_VERSION >= 3 # define PyInt_FromLong PyLong_FromLong # define PyString_FromString PyUnicode_FromString # define PyString_FromStringAndSize PyBytes_FromStringAndSize #endif /* for version before 2.6 */ #ifndef PyVarObject_HEAD_INIT # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, #endif #ifndef Py_TYPE # define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif /* Prototypes */ struct _RadixObject; struct _RadixIterObject; static struct _RadixIterObject *newRadixIterObject(struct _RadixObject *); static PyObject *radix_Radix(PyObject *, PyObject *); /* ------------------------------------------------------------------------ */ PyObject *radix_constructor; /* RadixNode: tree nodes */ typedef struct { PyObject_HEAD PyObject *user_attr; /* User-specified attributes */ PyObject *network; PyObject *prefix; PyObject *prefixlen; PyObject *family; PyObject *packed; radix_node_t *rn; /* Actual radix node (pointer to parent) */ } RadixNodeObject; static PyTypeObject RadixNode_Type; static RadixNodeObject * newRadixNodeObject(radix_node_t *rn) { RadixNodeObject *self; char network[256], prefix[256]; /* Sanity check */ if (rn == NULL || rn->prefix == NULL || (rn->prefix->family != AF_INET && rn->prefix->family != AF_INET6)) return NULL; self = PyObject_New(RadixNodeObject, &RadixNode_Type); if (self == NULL) return NULL; self->rn = rn; /* Format addresses for packing into objects */ prefix_addr_ntop(rn->prefix, network, sizeof(network)); prefix_ntop(rn->prefix, prefix, sizeof(prefix)); self->user_attr = PyDict_New(); self->network = PyString_FromString(network); self->prefix = PyString_FromString(prefix); self->prefixlen = PyInt_FromLong(rn->prefix->bitlen); self->family = PyInt_FromLong(rn->prefix->family); self->packed = PyString_FromStringAndSize((char*)&rn->prefix->add, rn->prefix->family == AF_INET ? 4 : 16); if (self->user_attr == NULL || self->prefixlen == NULL || self->family == NULL || self->network == NULL || self->prefix == NULL) { /* RadixNode_dealloc will clean up for us */ Py_XDECREF(self); return (NULL); } return self; } /* RadixNode methods */ static void RadixNode_dealloc(RadixNodeObject *self) { Py_XDECREF(self->user_attr); Py_XDECREF(self->prefixlen); Py_XDECREF(self->family); Py_XDECREF(self->network); Py_XDECREF(self->prefix); Py_XDECREF(self->packed); PyObject_Del(self); } static PyObject * Radix_parent(RadixNodeObject *self, void *closure) { PyObject *ret; radix_node_t *node; node = self->rn; /* walk up through parent to find parent node with data */ for ( ; ; ) { if (node && node->parent) { node = node->parent; if (node->data) { ret = node->data; Py_XINCREF(ret); return ret; } } else break; } Py_RETURN_NONE; } static PyMemberDef RadixNode_members[] = { {"data", T_OBJECT, offsetof(RadixNodeObject, user_attr), READONLY}, {"network", T_OBJECT, offsetof(RadixNodeObject, network), READONLY}, {"prefix", T_OBJECT, offsetof(RadixNodeObject, prefix), READONLY}, {"prefixlen", T_OBJECT, offsetof(RadixNodeObject, prefixlen), READONLY}, {"family", T_OBJECT, offsetof(RadixNodeObject, family), READONLY}, {"packed", T_OBJECT, offsetof(RadixNodeObject, packed), READONLY}, {NULL} }; static PyGetSetDef node_getter[] = { {"parent", (getter) Radix_parent, /* C function to get the attribute */ NULL, /* C function to set the attribute */ "parent of node", /* optional doc string */ NULL /* optional additional data for getter and setter */ }, {NULL} /* Sentinel */ }; PyDoc_STRVAR(RadixNode_doc, "Node in a radix tree"); static PyTypeObject RadixNode_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyVarObject_HEAD_INIT(NULL, 0) "_radix.RadixNode", /*tp_name*/ sizeof(RadixNodeObject),/*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)RadixNode_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ RadixNode_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ RadixNode_members, /*tp_members*/ node_getter, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ typedef struct _RadixObject { PyObject_HEAD radix_tree_t *rt; /* Radix tree for IPv4 & IPv6 addresses */ unsigned int gen_id; /* Detect modification during iterations */ } RadixObject; static PyTypeObject Radix_Type; #define Radix_CheckExact(op) (Py_TYPE(op) == &Radix_Type) static RadixObject * newRadixObject(void) { RadixObject *self; radix_tree_t *rt; if ((rt = New_Radix()) == NULL) return (NULL); if ((self = PyObject_New(RadixObject, &Radix_Type)) == NULL) { free(rt); return (NULL); } self->rt = rt; self->gen_id = 0; return (self); } /* Radix methods */ static void Radix_dealloc(RadixObject *self) { radix_node_t *rn; RadixNodeObject *node; RADIX_TREE_WALK(self->rt, rn) { if (rn->data != NULL) { node = rn->data; node->rn = NULL; Py_DECREF(node); } } RADIX_TREE_WALK_END; Destroy_Radix(self->rt, NULL, NULL); PyObject_Del(self); } static prefix_t *args_to_prefix(prefix_t *prefix, char *addr, char *packed, int packlen, long prefixlen) { prefix_t *old_prefix = prefix; const char *errmsg; if (addr != NULL && packed != NULL) { PyErr_SetString(PyExc_TypeError, "Two address types specified. Please pick one."); return NULL; } if (addr == NULL && packed == NULL) { PyErr_SetString(PyExc_TypeError, "No address specified (use 'address' or 'packed')"); return NULL; } if (addr != NULL) { /* Parse a string address */ if ((prefix = prefix_pton_ex(prefix, addr, prefixlen, &errmsg)) == NULL) { PyErr_SetString(PyExc_ValueError, errmsg ? errmsg : "Invalid address format"); } } else if (packed != NULL) { /* "parse" a packed binary address */ if ((prefix = prefix_from_blob_ex(prefix, (u_char*)packed, packlen, prefixlen)) == NULL) { PyErr_SetString(PyExc_ValueError, "Invalid packed address format"); } } if (prefix != NULL && prefix->family != AF_INET && prefix->family != AF_INET6) { if (old_prefix == NULL) Deref_Prefix(prefix); return (NULL); } return prefix; } static PyObject * create_add_node(RadixObject *self, prefix_t *prefix) { radix_node_t *node; RadixNodeObject *node_obj; if ((node = radix_lookup(self->rt, prefix)) == NULL) { PyErr_SetString(PyExc_MemoryError, "Couldn't add prefix"); return NULL; } /* * Create a RadixNode object in the data area of the node * We duplicate most of the node's identity, because the radix.c:node * itself has a lifetime independent of the Python node object * Confusing? yeah... */ if (node->data == NULL) { if ((node_obj = newRadixNodeObject(node)) == NULL) return (NULL); node->data = node_obj; } else node_obj = node->data; self->gen_id++; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_add_doc, "Radix.add(network[, masklen][, packed]) -> new RadixNode object\n\ \n\ Adds the network specified by 'network' and 'masklen' to the radix\n\ tree. 'network' may be a string in CIDR format, a unicast host\n\ address or a network address, with the mask length specified using\n\ the optional 'masklen' parameter.\n\ \n\ Alternately, the address may be specified in a packed binary format\n\ using the 'packed' keyword argument (instead of 'network'). This is\n\ useful with binary addresses returned by socket.getpeername(),\n\ socket.inet_ntoa(), etc.\n\ \n\ Both IPv4 and IPv6 addresses/networks are supported and may be mixed in\n\ the same tree.\n\ \n\ This method returns a RadixNode object. Arbitrary data may be strored\n\ in the RadixNode.data dict."); static PyObject * Radix_add(RadixObject *self, PyObject *args, PyObject *kw_args) { prefix_t *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; PyObject *node_obj; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:add", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(NULL, addr, packed, packlen, prefixlen)) == NULL) return NULL; node_obj = create_add_node(self, prefix); Deref_Prefix(prefix); return node_obj; } PyDoc_STRVAR(Radix_delete_doc, "Radix.delete(network[, masklen][, packed] -> None\n\ \n\ Deletes the specified network from the radix tree."); static PyObject * Radix_delete(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t lprefix, *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:delete", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((node = radix_search_exact(self->rt, prefix)) == NULL) { PyErr_SetString(PyExc_KeyError, "no such address"); return NULL; } if (node->data != NULL) { node_obj = node->data; node_obj->rn = NULL; Py_XDECREF(node_obj); } radix_remove(self->rt, node); self->gen_id++; Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(Radix_search_exact_doc, "Radix.search_exact(network[, masklen][, packed] -> RadixNode or None\n\ \n\ Search for the specified network in the radix tree. In order to\n\ match, the 'prefix' must be specified exactly. Contrast with the\n\ Radix.search_best method.\n\ \n\ If no match is found, then this method returns None."); static PyObject * Radix_search_exact(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t lprefix, *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:search_exact", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) return NULL; node = radix_search_exact(self->rt, prefix); if (node == NULL || node->data == NULL) { Py_INCREF(Py_None); return Py_None; } node_obj = node->data; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_search_best_doc, "Radix.search_best(network[, masklen][, packed] -> None\n\ \n\ Search for the specified network in the radix tree.\n\ \n\ search_best will return the best (longest) entry that includes the\n\ specified 'prefix', much like a IP routing table lookup.\n\ \n\ If no match is found, then returns None."); static PyObject * Radix_search_best(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t lprefix, *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:search_best", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((node = radix_search_best(self->rt, prefix)) == NULL || node->data == NULL) { Py_INCREF(Py_None); return Py_None; } node_obj = node->data; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_search_worst_doc, "Radix.search_worst(network[, masklen][, packed] -> None\n\ \n\ Search for the specified network in the radix tree.\n\ \n\ search_worst will return the worst (shortest) entry that includes the\n\ specified 'prefix', much like opposite of a IP routing table lookup.\n\ \n\ If no match is found, then returns None."); static PyObject * Radix_search_worst(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t lprefix, *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:search_worst", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((node = radix_search_worst(self->rt, prefix)) == NULL || node->data == NULL) { Py_INCREF(Py_None); return Py_None; } node_obj = node->data; Py_XINCREF(node_obj); return (PyObject *)node_obj; } static int add_node_to_list(radix_node_t *node, void *arg) { PyObject *ret = arg; if (node->data != NULL) PyList_Append(ret, ((RadixNodeObject *)node->data)); return (0); } PyDoc_STRVAR(Radix_search_covered_doc, "Radix.search_covered(network[, masklen][, packed] -> None\n\ \n\ FIXME"); static PyObject * Radix_search_covered(RadixObject *self, PyObject *args, PyObject *kw_args) { prefix_t lprefix, *prefix; PyObject *ret; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:search_covered", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((ret = PyList_New(0)) == NULL) return NULL; radix_search_covered(self->rt, prefix, add_node_to_list, ret, 1); return (ret); } PyDoc_STRVAR(Radix_search_covering_doc, "Radix.search_covering(network[, masklen][, packed] -> List of RadixNode\n\ \n\ Returns a list of nodes with a less specific or same prefix than network.\n\ \n\ If no match is found, then returns an empty list."); static PyObject * Radix_search_covering(RadixObject *self, PyObject *args, PyObject *kw_args) { prefix_t lprefix, *prefix; PyObject *ret; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|zlz#:search_covering", keywords, &addr, &prefixlen, &packed, &packlen)) { return NULL; } if ((prefix = args_to_prefix(&lprefix, addr, packed, packlen, prefixlen)) == NULL) { return NULL; } if ((ret = PyList_New(0)) == NULL) { return NULL; } radix_search_covering(self->rt, prefix, add_node_to_list, ret); return ret; } PyDoc_STRVAR(Radix_nodes_doc, "Radix.nodes(prefix) -> List of RadixNode\n\ \n\ Returns a list containing all the RadixNode objects that have been\n\ entered into the tree. This list may be empty if no prefixes have\n\ been entered."); static PyObject * Radix_nodes(RadixObject *self, PyObject *args) { radix_node_t *node; PyObject *ret; if (!PyArg_ParseTuple(args, ":nodes")) return NULL; if ((ret = PyList_New(0)) == NULL) return NULL; RADIX_TREE_WALK(self->rt, node) { if (node->data != NULL) PyList_Append(ret, (PyObject *)node->data); } RADIX_TREE_WALK_END; return (ret); } PyDoc_STRVAR(Radix_prefixes_doc, "Radix.prefixes(prefix) -> List of prefix strings\n\ \n\ Returns a list containing all the prefixes that have been entered\n\ into the tree. This list may be empty if no prefixes have been\n\ entered."); static PyObject * Radix_prefixes(RadixObject *self, PyObject *args) { radix_node_t *node; PyObject *ret; if (!PyArg_ParseTuple(args, ":prefixes")) return NULL; if ((ret = PyList_New(0)) == NULL) return NULL; RADIX_TREE_WALK(self->rt, node) { if (node->data != NULL) { PyList_Append(ret, ((RadixNodeObject *)node->data)->prefix); } } RADIX_TREE_WALK_END; return (ret); } static PyObject * Radix_getiter(RadixObject *self) { return (PyObject *)newRadixIterObject(self); } PyDoc_STRVAR(Radix_doc, "Radix tree"); static PyMethodDef Radix_methods[] = { {"add", (PyCFunction)Radix_add, METH_VARARGS|METH_KEYWORDS, Radix_add_doc }, {"delete", (PyCFunction)Radix_delete, METH_VARARGS|METH_KEYWORDS, Radix_delete_doc }, {"search_exact",(PyCFunction)Radix_search_exact,METH_VARARGS|METH_KEYWORDS, Radix_search_exact_doc }, {"search_best", (PyCFunction)Radix_search_best, METH_VARARGS|METH_KEYWORDS, Radix_search_best_doc }, {"search_worst", (PyCFunction)Radix_search_worst, METH_VARARGS|METH_KEYWORDS, Radix_search_worst_doc }, {"search_covered", (PyCFunction)Radix_search_covered, METH_VARARGS|METH_KEYWORDS, Radix_search_covered_doc }, {"search_covering",(PyCFunction)Radix_search_covering, METH_VARARGS|METH_KEYWORDS, Radix_search_covering_doc }, {"nodes", (PyCFunction)Radix_nodes, METH_VARARGS, Radix_nodes_doc }, {"prefixes", (PyCFunction)Radix_prefixes, METH_VARARGS, Radix_prefixes_doc }, {NULL, NULL} /* sentinel */ }; static PyTypeObject Radix_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyVarObject_HEAD_INIT(NULL, 0) "_radix.Radix", /*tp_name*/ sizeof(RadixObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)Radix_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ Radix_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ (getiterfunc)Radix_getiter, /*tp_iter*/ 0, /*tp_iternext*/ Radix_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ /* RadixIter: radix tree iterator */ typedef struct _RadixIterObject { PyObject_HEAD RadixObject *parent; radix_node_t *iterstack[RADIX_MAXBITS+1]; radix_node_t **sp; radix_node_t *rn; int af; unsigned int gen_id; /* Detect tree modifications */ } RadixIterObject; static PyTypeObject RadixIter_Type; static RadixIterObject * newRadixIterObject(RadixObject *parent) { RadixIterObject *self; self = PyObject_New(RadixIterObject, &RadixIter_Type); if (self == NULL) return NULL; self->parent = parent; Py_XINCREF(self->parent); self->sp = self->iterstack; self->rn = self->parent->rt->head_ipv4; self->gen_id = self->parent->gen_id; self->af = AF_INET; return self; } /* RadixIter methods */ static void RadixIter_dealloc(RadixIterObject *self) { Py_XDECREF(self->parent); PyObject_Del(self); } static PyObject * RadixIter_iternext(RadixIterObject *self) { radix_node_t *node; PyObject *ret; if (self->gen_id != self->parent->gen_id) { PyErr_SetString(PyExc_RuntimeWarning, "Radix tree modified during iteration"); return (NULL); } again: if ((node = self->rn) == NULL) { /* We have walked both trees */ if (self->af == AF_INET6) return NULL; /* Otherwise reset and start walk of IPv6 tree */ self->sp = self->iterstack; self->rn = self->parent->rt->head_ipv6; self->af = AF_INET6; goto again; } /* Get next node */ if (self->rn->l) { if (self->rn->r) *self->sp++ = self->rn->r; self->rn = self->rn->l; } else if (self->rn->r) self->rn = self->rn->r; else if (self->sp != self->iterstack) self->rn = *(--self->sp); else self->rn = NULL; if (node->prefix == NULL || node->data == NULL) goto again; ret = node->data; Py_INCREF(ret); return (ret); } PyDoc_STRVAR(RadixIter_doc, "Radix tree iterator"); static PyTypeObject RadixIter_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyVarObject_HEAD_INIT(NULL, 0) "_radix.RadixIter", /*tp_name*/ sizeof(RadixIterObject),/*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)RadixIter_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ RadixIter_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ (iternextfunc)RadixIter_iternext, /*tp_iternext*/ 0, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ /* Radix object creator */ PyDoc_STRVAR(radix_Radix_doc, "Radix() -> new Radix tree object\n\ \n\ Instantiate a new radix tree object."); static PyObject * radix_Radix(PyObject *self, PyObject *args) { RadixObject *rv; if (!PyArg_ParseTuple(args, ":Radix")) return NULL; rv = newRadixObject(); if (rv == NULL) return NULL; return (PyObject *)rv; } static PyMethodDef radix_methods[] = { {"Radix", radix_Radix, METH_VARARGS, radix_Radix_doc }, {NULL, NULL} /* sentinel */ }; PyDoc_STRVAR(module_doc, "Implementation of a radix tree data structure for network prefixes.\n" "\n" "The radix tree is the data structure most commonly used for routing\n" "table lookups. It efficiently stores network prefixes of varying\n" "lengths and allows fast lookups of containing networks.\n" "\n" "Simple example:\n" "\n" " import radix\n" " import socket\n" "\n" " # Create a new tree\n" " rtree = radix.Radix()\n" "\n" " # Adding a node returns a RadixNode object. You can create\n" " # arbitrary members in its 'data' dict to store your data\n" " rnode = rtree.add(\"10.0.0.0/8\")\n" " rnode.data[\"blah\"] = \"whatever you want\"\n" "\n" " # You can specify nodes as CIDR addresses, or networks with\n" " # separate mask lengths. The following three invocations are\n" " # identical:\n" " rnode = rtree.add(\"10.0.0.0/16\")\n" " rnode = rtree.add(\"10.0.0.0\", 16)\n" " rnode = rtree.add(network = \"10.0.0.0\", masklen = 16)\n" "\n" " # It is also possible to specify nodes using binary packed\n" " # addresses, such as those returned by the socket module\n" " # functions. In this case, the radix module will assume that\n" " # a four-byte address is an IPv4 address and a sixteen-byte\n" " # address is an IPv6 address. For example:\n" " binary_addr = inet_aton(\"172.18.22.0\")\n" " rnode = rtree.add(packed = binary_addr, masklen = 23)\n" "\n" " # Exact search will only return prefixes you have entered\n" " # You can use all of the above ways to specify the address\n" " rnode = rtree.search_exact(\"10.0.0.0/8\")\n" " # Get your data back out\n" " print rnode.data[\"blah\"]\n" " # Use a packed address\n" " addr = socket.inet_aton(\"10.0.0.0\")\n" " rnode = rtree.search_exact(packed = addr, masklen = 8)\n" "\n" " # Best-match search will return the longest matching prefix\n" " # that contains the search term (routing-style lookup)\n" " rnode = rtree.search_best(\"10.123.45.6\")\n" "\n" " # Worst-match search will return the shortest matching prefix\n" " # that contains the search term (inverse routing-style lookup)\n" " rnode = rtree.search_worst(\"10.123.45.6\")\n" "\n" " # There are a couple of implicit members of a RadixNode:\n" " print rnode.network # -> \"10.0.0.0\"\n" " print rnode.prefix # -> \"10.0.0.0/8\"\n" " print rnode.prefixlen # -> 8\n" " print rnode.family # -> socket.AF_INET\n" " print rnode.packed # -> '\\n\\x00\\x00\\x00'\n" " print rnode.parent # -> None\n" "\n" " # IPv6 prefixes are fully supported in the same tree\n" " rnode = rtree.add(\"2001:DB8::/32\")\n" " rnode = rtree.add(\"::/0\")\n" "\n" " # Use the nodes() method to return all RadixNodes created\n" " nodes = rtree.nodes()\n" " for rnode in nodes:\n" " print rnode.prefix\n" "\n" " # The prefixes() method will return all the prefixes (as a\n" " # list of strings) that have been entered\n" " prefixes = rtree.prefixes()\n" "\n" " # You can also directly iterate over the tree itself\n" " # this would save some memory if the tree is big\n" " # NB. Don't modify the tree (add or delete nodes) while\n" " # iterating otherwise you will abort the iteration and\n" " # receive a RuntimeWarning. Changing a node's data dict\n" " # is permitted.\n" " for rnode in rtree:\n" " print rnode.prefix\n" ); #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef radix_module_def = { PyModuleDef_HEAD_INIT, "_radix", module_doc, -1, radix_methods, // methods NULL, // m_reload NULL, // traverse NULL, // clear NULL // free }; #endif static PyObject *module_initialize(void) { PyObject *m, *d; if (PyType_Ready(&Radix_Type) < 0) return NULL; if (PyType_Ready(&RadixNode_Type) < 0) return NULL; #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&radix_module_def); #else m = Py_InitModule3("_radix", radix_methods, module_doc); #endif /* Stash the callable constructor for use in Radix.__reduce__ */ d = PyModule_GetDict(m); radix_constructor = PyDict_GetItemString(d, "Radix"); PyModule_AddIntConstant(m, "__accelerator__", 1); return m; } #if PY_MAJOR_VERSION >= 3 PyMODINIT_FUNC PyInit__radix(void) { return module_initialize(); } #else PyMODINIT_FUNC init_radix(void) { module_initialize(); } #endif py-radix-0.10.0/radix/radix.py0000644000076500000240000004034713166705156017133 0ustar schultzmstaff00000000000000from socket import (getaddrinfo, gaierror, inet_pton, inet_ntop, AF_INET, AF_INET6, SOCK_RAW, AI_NUMERICHOST) class RadixPrefix(object): family = None bitlen = 0 addr = None def __init__(self, network=None, masklen=None, packed=None): if network and packed: raise ValueError('Two address types specified. Please pick one.') if network is None and packed is None: raise TypeError('No address specified (use `address` or `packed`)') if network: self._from_network(network, masklen) elif packed: self._from_packed(packed, masklen) def __str__(self): return '{0}/{1}'.format(self.network, self.bitlen) @property def packed(self): return bytes(self.addr) @property def network(self): if not self.addr: return None return inet_ntop(self.family, bytes(self.addr)) def _inet_pton(self, family, sockaddr, masklen): addr = bytearray(inet_pton(family, sockaddr)) if family == AF_INET: max_masklen = 32 elif family == AF_INET6: max_masklen = 128 quotient, remainder = divmod(masklen, 8) if remainder != 0: addr[quotient] = addr[quotient] & ((~0) << (8 - remainder)) quotient += 1 while quotient < max_masklen / 8: addr[quotient] = 0 quotient += 1 return addr def _from_network(self, network, masklen): split = network.split('/') if len(split) > 1: # network has prefix in it if masklen: raise ValueError('masklen specified twice') network = split[0] masklen = int(split[1]) else: network = split[0] try: family, _, _, _, sockaddr = getaddrinfo( network, None, 0, SOCK_RAW, 6, AI_NUMERICHOST)[0] except gaierror as e: raise ValueError(e) if family == AF_INET: if masklen is None: masklen = 32 if not (0 <= masklen <= 32): raise ValueError('invalid prefix length') elif family == AF_INET6: if masklen is None: masklen = 128 if not (0 <= masklen <= 128): raise ValueError('invalid prefix length') else: return self.addr = self._inet_pton(family, sockaddr[0], masklen) self.bitlen = masklen self.family = family def _from_packed(self, packed, masklen): packed_len = len(packed) if packed_len == 4: family = AF_INET if masklen is None: masklen = 32 if not (0 <= masklen <= 32): raise ValueError('invalid prefix length') elif packed_len == 16: family = AF_INET6 if masklen is None: masklen = 128 if not (0 <= masklen <= 128): raise ValueError('invalid prefix length') else: return self.addr = packed self.bitlen = masklen self.family = family class RadixTree(object): def __init__(self): self.maxbits = 128 self.head = None self.active_nodes = 0 def _addr_test(self, addr, bitlen): left = addr[bitlen >> 3] right = 0x80 >> (bitlen & 0x07) return left & right def add(self, prefix): if self.head is None: # easy case node = RadixNode(prefix) self.head = node self.active_nodes += 1 return node addr = prefix.addr bitlen = prefix.bitlen node = self.head # find the best place for the node while node.bitlen < bitlen or node._prefix.addr is None: if (node.bitlen < self.maxbits and self._addr_test(addr, node.bitlen)): if node.right is None: break node = node.right else: if node.left is None: break node = node.left # find the first differing bit test_addr = node._prefix.addr check_bit = node.bitlen if node.bitlen < bitlen else bitlen differ_bit = 0 i = 0 while i * 8 < check_bit: r = addr[i] ^ test_addr[i] if r == 0: differ_bit = (i + 1) * 8 i += 1 continue # bitwise check for j in range(8): if r & (0x80 >> j): break differ_bit = i * 8 + j break if differ_bit > check_bit: differ_bit = check_bit # now figure where to insert parent = node.parent while parent and parent.bitlen >= differ_bit: node, parent = parent, node.parent # found a match if differ_bit == bitlen and node.bitlen == bitlen: if isinstance(node._prefix, RadixGlue): node._prefix = prefix return node # no match, new node new_node = RadixNode(prefix) self.active_nodes += 1 # fix it up if node.bitlen == differ_bit: new_node.parent = node if (node.bitlen < self.maxbits and self._addr_test(addr, node.bitlen)): node.right = new_node else: node.left = new_node return new_node if bitlen == differ_bit: if bitlen < self.maxbits and self._addr_test(test_addr, bitlen): new_node.right = node else: new_node.left = node new_node.parent = node.parent if node.parent is None: self.head = new_node elif node.parent.right == node: node.parent.right = new_node else: node.parent.left = new_node node.parent = new_node else: glue_node = RadixNode(prefix_size=differ_bit, parent=node.parent) self.active_nodes += 1 if differ_bit < self.maxbits and self._addr_test(addr, differ_bit): glue_node.right = new_node glue_node.left = node else: glue_node.right = node glue_node.left = new_node new_node.parent = glue_node if node.parent is None: self.head = glue_node elif node.parent.right == node: node.parent.right = glue_node else: node.parent.left = glue_node node.parent = glue_node return new_node def remove(self, node): if node.right and node.left: node._prefix.addr = None node.data = None node.bitlen = 0 return if node.right is None and node.left is None: parent = node.parent self.active_nodes -= 1 if parent is None: self.head = None return if parent.right == node: parent.right = None child = parent.left else: parent.left = None child = parent.right if parent._prefix.addr: return # remove the parent too if parent.parent is None: self.head = child elif parent.parent.right == parent: parent.parent.right = child else: parent.parent.left = child child.parent = parent.parent self.active_nodes -= 1 return if node.right: child = node.right else: child = node.left parent = node.parent child.parent = parent self.active_nodes -= 1 if parent is None: self.head = child return if parent.right == node: parent.right = child else: parent.left = child return def search_best(self, prefix): if self.head is None: return None node = self.head addr = prefix.addr bitlen = prefix.bitlen stack = [] while node.bitlen < bitlen: if node._prefix.addr: stack.append(node) if self._addr_test(addr, node.bitlen): node = node.right else: node = node.left if node is None: break if node and node._prefix.addr: stack.append(node) if len(stack) <= 0: return None for node in stack[::-1]: if (self._prefix_match(node._prefix, prefix, node.bitlen) and node.bitlen <= bitlen): return node return None def search_exact(self, prefix): if self.head is None: return None node = self.head addr = prefix.addr bitlen = prefix.bitlen while node.bitlen < bitlen: if self._addr_test(addr, node.bitlen): node = node.right else: node = node.left if node is None: return None if node.bitlen > bitlen or node._prefix.addr is None: return None if self._prefix_match(node._prefix, prefix, bitlen): return node return None def search_worst(self, prefix): if self.head is None: return None node = self.head addr = prefix.addr bitlen = prefix.bitlen stack = [] while node.bitlen < bitlen: if node._prefix.addr: stack.append(node) if self._addr_test(addr, node.bitlen): node = node.right else: node = node.left if node is None: break if node and node._prefix.addr: stack.append(node) if len(stack) <= 0: return None for node in stack: if self._prefix_match(node._prefix, prefix, node.bitlen): return node return None def search_covered(self, prefix): results = [] if self.head is None: return results node = self.head addr = prefix.addr bitlen = prefix.bitlen while node.bitlen < bitlen: if self._addr_test(addr, node.bitlen): node = node.right else: node = node.left if node is None: return results stack = [node] while stack: node = stack.pop() if self._prefix_match(node._prefix, prefix, prefix.bitlen): results.append(node) if node.right: stack.append(node.right) if node.left: stack.append(node.left) return results def _prefix_match(self, left, right, bitlen): l = left.addr r = right.addr if l is None or r is None: return False quotient, remainder = divmod(bitlen, 8) if l[:quotient] != r[:quotient]: return False if remainder == 0: return True mask = (~0) << (8 - remainder) if (l[quotient] & mask) == (r[quotient] & mask): return True return False class RadixGlue(RadixPrefix): def __init__(self, bitlen=None): self.bitlen = bitlen class RadixNode(object): def __init__(self, prefix=None, prefix_size=None, data=None, parent=None, left=None, right=None): if prefix: self._prefix = prefix else: self._prefix = RadixGlue(bitlen=prefix_size) self._parent = parent self.bitlen = self._prefix.bitlen self.left = left self.right = right self.data = data self._cache = {} def __str__(self): return self.prefix def __repr__(self): return '<{0}>'.format(self.prefix) @property def network(self): return self._prefix.network @property def prefix(self): return str(self._prefix) @property def prefixlen(self): return self.bitlen @property def family(self): return self._prefix.family @property def packed(self): return self._prefix.packed def __set_parent(self, parent): self._parent = parent def __get_parent(self): return self._parent parent = property(__get_parent, __set_parent, None, "parent of node") class Radix(object): def __init__(self): self._tree4 = RadixTree() self._tree6 = RadixTree() self.gen_id = 0 # detection of modifiction during iteration def add(self, network=None, masklen=None, packed=None): prefix = RadixPrefix(network, masklen, packed) if prefix.family == AF_INET: node = self._tree4.add(prefix) else: node = self._tree6.add(prefix) if node.data is None: node.data = {} self.gen_id += 1 return node def delete(self, network=None, masklen=None, packed=None): node = self.search_exact(network, masklen, packed) if not node: raise KeyError('match not found') if node.family == AF_INET: self._tree4.remove(node) else: self._tree6.remove(node) self.gen_id += 1 def search_exact(self, network=None, masklen=None, packed=None): prefix = RadixPrefix(network, masklen, packed) if prefix.family == AF_INET: node = self._tree4.search_exact(prefix) else: node = self._tree6.search_exact(prefix) if node and node.data is not None: return node else: return None def search_best(self, network=None, masklen=None, packed=None): prefix = RadixPrefix(network, masklen, packed) if prefix.family == AF_INET: node = self._tree4.search_best(prefix) else: node = self._tree6.search_best(prefix) if node and node.data is not None: return node else: return None def search_worst(self, network=None, masklen=None, packed=None): prefix = RadixPrefix(network, masklen, packed) if prefix.family == AF_INET: node = self._tree4.search_worst(prefix) else: node = self._tree6.search_worst(prefix) if node and node.data is not None: return node else: return None def search_covered(self, network=None, masklen=None, packed=None): prefix = RadixPrefix(network, masklen, packed) if prefix.family == AF_INET: return self._tree4.search_covered(prefix) else: return self._tree6.search_covered(prefix) def search_covering(self, network=None, masklen=None, packed=None): node = self.search_best(network=network, masklen=masklen, packed=packed) stack = [] while node is not None: if node._prefix.addr and node.data is not None: stack.append(node) node = node.parent return stack def _iter(self, node): stack = [] while node is not None: if node._prefix.addr and node.data is not None: yield node if node.left: if node.right: # we'll come back to it stack.append(node.right) node = node.left elif node.right: node = node.right elif len(stack) != 0: node = stack.pop() else: break return def nodes(self): return [elt for elt in self] def prefixes(self): return [str(elt._prefix) for elt in self] def __iter__(self): init_id = self.gen_id for elt in self._iter(self._tree4.head): if init_id != self.gen_id: raise RuntimeWarning('detected modification during iteration') yield elt for elt in self._iter(self._tree6.head): if init_id != self.gen_id: raise RuntimeWarning('detected modification during iteration') yield elt py-radix-0.10.0/README.rst0000644000076500000240000001105113166705437016022 0ustar schultzmstaff00000000000000py-radix ======== .. image:: https://travis-ci.org/mjschultz/py-radix.svg?branch=master :target: https://travis-ci.org/mjschultz/py-radix .. image:: https://coveralls.io/repos/mjschultz/py-radix/badge.png?branch=master :target: https://coveralls.io/r/mjschultz/py-radix?branch=master py-radix implements the radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. Installation ------------ Installation is a breeze via pip: :: pip install py-radix Or with the standard Python distutils incantation: :: python setup.py build python setup.py install The C extension will be built for supported python versions. If you do not want the C extension, set the environment variable ``RADIX_NO_EXT=1``. Tests are in the ``tests/`` directory and can be run with ``python setup.py nosetests``. Usage ----- A simple example that demonstrates most of the features: :: import radix # Create a new tree rtree = radix.Radix() # Adding a node returns a RadixNode object. You can create # arbitrary members in its 'data' dict to store your data rnode = rtree.add("10.0.0.0/8") rnode.data["blah"] = "whatever you want" # You can specify nodes as CIDR addresses, or networks with # separate mask lengths. The following three invocations are # identical: rnode = rtree.add("10.0.0.0/16") rnode = rtree.add("10.0.0.0", 16) rnode = rtree.add(network = "10.0.0.0", masklen = 16) # It is also possible to specify nodes using binary packed # addresses, such as those returned by the socket module # functions. In this case, the radix module will assume that # a four-byte address is an IPv4 address and a sixteen-byte # address is an IPv6 address. For example: binary_addr = inet_ntoa("172.18.22.0") rnode = rtree.add(packed = binary_addr, masklen = 23) # Exact search will only return prefixes you have entered # You can use all of the above ways to specify the address rnode = rtree.search_exact("10.0.0.0/8") # Get your data back out print rnode.data["blah"] # Use a packed address addr = socket.inet_ntoa("10.0.0.0") rnode = rtree.search_exact(packed = addr, masklen = 8) # Best-match search will return the longest matching prefix # that contains the search term (routing-style lookup) rnode = rtree.search_best("10.123.45.6") # Worst-search will return the shortest matching prefix # that contains the search term (inverse routing-style lookup) rnode = rtree.search_worst("10.123.45.6") # Covered search will return all prefixes inside the given # search term, as a list (including the search term itself, # if present in the tree) rnodes = rtree.search_covered("10.123.0.0/16") # There are a couple of implicit members of a RadixNode: print rnode.network # -> "10.0.0.0" print rnode.prefix # -> "10.0.0.0/8" print rnode.prefixlen # -> 8 print rnode.family # -> socket.AF_INET print rnode.packed # -> '\n\x00\x00\x00' # IPv6 prefixes are fully supported in the same tree rnode = rtree.add("2001:DB8::/3") rnode = rtree.add("::/0") # Use the nodes() method to return all RadixNodes created nodes = rtree.nodes() for rnode in nodes: print rnode.prefix # The prefixes() method will return all the prefixes (as a # list of strings) that have been entered prefixes = rtree.prefixes() # You can also directly iterate over the tree itself # this would save some memory if the tree is big # NB. Don't modify the tree (add or delete nodes) while # iterating otherwise you will abort the iteration and # receive a RuntimeWarning. Changing a node's data dict # is permitted. for rnode in rtree: print rnode.prefix License ------- py-radix is licensed under a ISC/BSD licence. The underlying radix tree implementation is taken (and modified) from MRTd and is subject to a 4-term BSD license. See the LICENSE file for details. Contributing ------------ Please report bugs via GitHub at https://github.com/mjschultz/py-radix/issues. Code changes can be contributed through a pull request on GitHub or emailed directly to me . The main portions of the directory tree are as follows: :: . ├── radix/*.py # Pure Python code ├── radix/_radix.c # C extension code (compatible with pure python code) ├── radix/_radix/* # C extension code (compatible with pure python code) ├── tests/ # Tests (regression and unit) └── setup.py # Standard setup.py for installation/testing/etc. py-radix-0.10.0/setup.cfg0000644000076500000240000000017513166705666016165 0ustar schultzmstaff00000000000000[nosetests] verbosity = 2 detailed-errors = 1 with-coverage = 1 cover-package = radix [egg_info] tag_build = tag_date = 0 py-radix-0.10.0/setup.py0000755000076500000240000000361313166705503016047 0ustar schultzmstaff00000000000000#!/usr/bin/env python import codecs import sys import os from setuptools import setup, find_packages, Extension from os.path import abspath, dirname, join here = abspath(dirname(__file__)) # determine the python version IS_PYPY = hasattr(sys, 'pypy_version_info') RADIX_NO_EXT = os.environ.get('RADIX_NO_EXT', '0') RADIX_NO_EXT = True if RADIX_NO_EXT not in ('0', 'false', 'False') else False with codecs.open(join(here, 'README.rst'), encoding='utf-8') as f: README = f.read() # introduce some extra setup_args if Python 2.x extra_kwargs = {} if not IS_PYPY and not RADIX_NO_EXT: sources = ['radix/_radix.c', 'radix/_radix/radix.c'] radix = Extension('radix._radix', sources=sources, include_dirs=[join(here, 'radix')]) extra_kwargs['ext_modules'] = [radix] tests_require = ['nose', 'coverage'] if sys.version_info < (2, 7): tests_require.append('unittest2') setup( name='py-radix', version='0.10.0', maintainer='Michael J. Schultz', maintainer_email='mjschultz@gmail.com', url='https://github.com/mjschultz/py-radix', description='Radix tree implementation', long_description=README, license='BSD', keywords='radix tree trie python routing networking', classifiers=[ 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Networking', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', ], tests_require=tests_require, packages=find_packages(exclude=['tests', 'tests.*']), test_suite='nose.collector', **extra_kwargs )