]> jfr.im git - irc/quakenet/newserv.git/blame - patricia/patricialib.c
macros
[irc/quakenet/newserv.git] / patricia / patricialib.c
CommitLineData
526e7c1d
P
1/*
2 * $Id: patricia.c,v 1.7 2005/12/07 20:46:41 dplonka Exp $
3 * Dave Plonka <plonka@doit.wisc.edu>
4 *
5 * This product includes software developed by the University of Michigan,
6 * Merit Network, Inc., and their contributors.
7 *
8 * This file had been called "radix.c" in the MRT sources.
9 *
10 * I renamed it to "patricia.c" since it's not an implementation of a general
11 * radix trie. Also I pulled in various requirements from "prefix.c" and
12 * "demo.c" so that it could be used as a standalone API.
13 */
14
6f8dfee9 15char patricia_copyright[] =
526e7c1d
P
16"This product includes software developed by the University of Michigan, Merit"
17"Network, Inc., and their contributors.";
18
526e7c1d
P
19#include <assert.h> /* assert */
20#include <ctype.h> /* isdigit */
21#include <errno.h> /* errno */
22#include <math.h> /* sin */
23#include <stddef.h> /* NULL */
24#include <stdio.h> /* sprintf, fprintf, stderr */
25#include <stdlib.h> /* free, atol, calloc */
26#include <string.h> /* memcpy, strchr, strlen */
27
28#include "patricia.h"
29
526e7c1d
P
30/* prefix_tochar
31 * convert prefix information to bytes
32 */
33u_char *
34prefix_tochar (prefix_t * prefix)
35{
36 if (prefix == NULL)
37 return (NULL);
38
39 return ((u_char *) & prefix->sin);
40}
41
42int
43comp_with_mask (void *addr, void *dest, u_int mask)
44{
45
46 if ( /* mask/8 == 0 || */ memcmp (addr, dest, mask / 8) == 0) {
47 int n = mask / 8;
48 int m = ((-1) << (8 - (mask % 8)));
49
50 if (mask % 8 == 0 || (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m))
51 return (1);
52 }
53 return (0);
54}
55
56prefix_t *
57patricia_new_prefix (struct irc_in_addr *dest, int bitlen)
58{
7a25133f
P
59 prefix_t *prefix = newprefix();
60 assert (0 != prefix);
526e7c1d 61
526e7c1d 62 memcpy (&prefix->sin, dest, 16);
526e7c1d
P
63 prefix->bitlen = (bitlen >= 0)? bitlen: 128;
64 prefix->ref_count = 1;
7a25133f 65
526e7c1d
P
66 return prefix;
67}
68
69prefix_t *
70patricia_ref_prefix (prefix_t * prefix)
71{
72 if (prefix == NULL)
73 return (NULL);
74 if (prefix->ref_count == 0) {
75 /* make a copy in case of a static prefix */
76 return (patricia_new_prefix (&prefix->sin, prefix->bitlen));
77 }
78 prefix->ref_count++;
526e7c1d
P
79 return (prefix);
80}
81
82void
83patricia_deref_prefix (prefix_t * prefix)
84{
85 if (prefix == NULL)
86 return;
87 /* for secure programming, raise an assert. no static prefix can call this */
88 assert (prefix->ref_count > 0);
89
90 prefix->ref_count--;
526e7c1d 91 if (prefix->ref_count <= 0) {
7a25133f 92 freeprefix(prefix);
526e7c1d
P
93 return;
94 }
95}
96
526e7c1d
P
97/* these routines support continuous mask only */
98
99patricia_tree_t *
100patricia_new_tree (int maxbits)
101{
102 patricia_tree_t *patricia = calloc(1, sizeof *patricia);
103
104 patricia->maxbits = maxbits;
105 patricia->head = NULL;
106 patricia->num_active_node = 0;
107 assert (maxbits <= PATRICIA_MAXBITS); /* XXX */
108 return (patricia);
109}
110
111
112void
113patricia_clear_tree (patricia_tree_t *patricia, void_fn_t func)
114{
115 assert (patricia);
116 if (patricia->head) {
117
118 patricia_node_t *Xstack[PATRICIA_MAXBITS+1];
119 patricia_node_t **Xsp = Xstack;
120 patricia_node_t *Xrn = patricia->head;
121
122 while (Xrn) {
123 patricia_node_t *l = Xrn->l;
124 patricia_node_t *r = Xrn->r;
125
126 if (Xrn->prefix) {
a8ba1373
P
127 if (Xrn->exts && func)
128 func(Xrn->exts);
526e7c1d
P
129 patricia_deref_prefix (Xrn->prefix);
130 }
7a25133f 131 freenode(Xrn);
526e7c1d
P
132 patricia->num_active_node--;
133
134 if (l) {
135 if (r) {
136 *Xsp++ = r;
137 }
138 Xrn = l;
139 } else if (r) {
140 Xrn = r;
141 } else if (Xsp != Xstack) {
142 Xrn = *(--Xsp);
143 } else {
144 Xrn = (patricia_node_t *) 0;
145 }
146 }
147 }
148 assert (patricia->num_active_node == 0);
526e7c1d
P
149}
150
526e7c1d
P
151void
152patricia_destroy_tree (patricia_tree_t *patricia, void_fn_t func)
153{
154 patricia_clear_tree (patricia, func);
155 free(patricia);
7a25133f 156 /*TODO: EXTENSIONS!*/
526e7c1d
P
157}
158
159
160/*
161 * if func is supplied, it will be called as func(node->prefix)
162 */
163
164void
165patricia_process (patricia_tree_t *patricia, void_fn_t func)
166{
167 patricia_node_t *node;
168 assert (func);
169
170 PATRICIA_WALK (patricia->head, node) {
171 func (node->prefix);
172 } PATRICIA_WALK_END;
173}
174
175size_t
176patricia_walk_inorder(patricia_node_t *node, void_fn_t func)
177{
178 size_t n = 0;
179 assert(func);
180
181 if (node->l) {
182 n += patricia_walk_inorder(node->l, func);
183 }
184
185 if (node->prefix) {
186 func(node->prefix);
187 n++;
188 }
189
190 if (node->r) {
191 n += patricia_walk_inorder(node->r, func);
192 }
193
194 return n;
195}
196
197
198patricia_node_t *
199patricia_search_exact (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen)
200{
201 patricia_node_t *node;
202 u_char *addr;
203
204 assert (patricia);
205 assert (sin);
206 assert (bitlen <= patricia->maxbits);
207
208 if (patricia->head == NULL)
209 return (NULL);
210
211 node = patricia->head;
212 addr = (u_char *)sin;
213
214 while (node->bit < bitlen) {
a9b4ce08 215 if (is_bit_set(addr,node->bit))
526e7c1d
P
216 node = node->r;
217 else
218 node = node->l;
219
220 if (node == NULL)
221 return (NULL);
222 }
223
224 if (node->bit > bitlen || node->prefix == NULL)
225 return (NULL);
226 assert (node->bit == bitlen);
227 assert (node->bit == node->prefix->bitlen);
228 if (comp_with_mask (prefix_tochar (node->prefix), addr, bitlen)) {
229 return (node);
230 }
231 return (NULL);
232}
233
234
235/* if inclusive != 0, "best" may be the given prefix itself */
236patricia_node_t *
237patricia_search_best2 (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen, int inclusive)
238{
239 patricia_node_t *node;
240 patricia_node_t *stack[PATRICIA_MAXBITS + 1];
241 u_char *addr;
242 int cnt = 0;
243
244 assert (patricia);
245 assert (sin);
246 assert (bitlen <= patricia->maxbits);
247
248 if (patricia->head == NULL)
249 return (NULL);
250
251 node = patricia->head;
252 addr = (u_char *)sin;
253
254 while (node->bit < bitlen) {
255
256 if (node->prefix) {
257 stack[cnt++] = node;
258 }
259
a9b4ce08 260 if (is_bit_set(addr,node->bit)) {
526e7c1d
P
261 node = node->r;
262 }
263 else {
264 node = node->l;
265 }
266
267 if (node == NULL)
268 break;
269 }
270
271 if (inclusive && node && node->prefix)
272 stack[cnt++] = node;
273
274 if (cnt <= 0)
275 return (NULL);
276
277 while (--cnt >= 0) {
278 node = stack[cnt];
279 if (comp_with_mask (prefix_tochar (node->prefix),
280 addr,
281 node->prefix->bitlen)) {
282 return (node);
283 }
284 }
285 return (NULL);
286}
287
288
289patricia_node_t *
290patricia_search_best (patricia_tree_t *patricia, struct irc_in_addr *sin, unsigned char bitlen)
291{
292 return (patricia_search_best2 (patricia, sin, bitlen, 1));
293}
294
295
296patricia_node_t *
297patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix)
298{
299 patricia_node_t *node, *new_node, *parent, *glue;
300 u_char *addr, *test_addr;
301 u_int bitlen, check_bit, differ_bit;
302 int i, j, r;
303
304 assert (patricia);
305 assert (prefix);
306 assert (prefix->bitlen <= patricia->maxbits);
307
308 /* if new trie, create the first node */
309 if (patricia->head == NULL) {
7a25133f 310 node = patricia_new_node(patricia, prefix->bitlen, patricia_ref_prefix (prefix));
526e7c1d 311 patricia->head = node;
526e7c1d
P
312 return (node);
313 }
314
315 addr = prefix_touchar (prefix);
316 bitlen = prefix->bitlen;
317 node = patricia->head;
318
319 /* while ( bitlength of tree node < bitlength of node we're searching for || the node has no prefix */
320 while (node->bit < bitlen || node->prefix == NULL) {
321 /* check that we're not at the lowest leaf i.e. node->bit is less than max bits */
322 if (node->bit < patricia->maxbits &&
a9b4ce08 323 (is_bit_set(addr,node->bit))) {
526e7c1d
P
324 if (node->r == NULL)
325 break;
326 node = node->r;
327 }
328 else {
329 if (node->l == NULL)
330 break;
331 node = node->l;
332 }
333
334 assert (node);
335 }
336
337 assert (node->prefix);
338
339 test_addr = prefix_touchar (node->prefix);
340 /* find the first bit different */
341 check_bit = (node->bit < bitlen)? node->bit: bitlen;
342 differ_bit = 0;
343 for (i = 0; i*8 < check_bit; i++) {
344 if ((r = (addr[i] ^ test_addr[i])) == 0) {
345 differ_bit = (i + 1) * 8;
346 continue;
347 }
348 /* I know the better way, but for now */
349 for (j = 0; j < 8; j++) {
350 if ((r) & ((0x80 >> j)))
351 break;
352 }
353 /* must be found */
354 assert (j < 8);
355 differ_bit = i * 8 + j;
356 break;
357 }
358 if (differ_bit > check_bit)
359 differ_bit = check_bit;
360
361
362 parent = node->parent;
363 while (parent && parent->bit >= differ_bit) {
364 node = parent;
365 parent = node->parent;
366 }
367
368 if (differ_bit == bitlen && node->bit == bitlen) {
950714ee
P
369 if (!node->prefix) {
370 node->prefix = patricia_ref_prefix (prefix);
371 }
526e7c1d
P
372 return (node);
373 }
374
7a25133f 375 new_node = patricia_new_node(patricia, prefix->bitlen, patricia_ref_prefix (prefix));
526e7c1d
P
376
377 if (node->bit == differ_bit) {
378 new_node->parent = node;
379 if (node->bit < patricia->maxbits &&
a9b4ce08 380 (is_bit_set(addr, node->bit))) {
526e7c1d
P
381 assert (node->r == NULL);
382 node->r = new_node;
383 }
384 else {
385 assert (node->l == NULL);
386 node->l = new_node;
387 }
388 return (new_node);
389 }
390
391 if (bitlen == differ_bit) {
392 if (bitlen < patricia->maxbits &&
a9b4ce08 393 (is_bit_set(addr,bitlen))) {
526e7c1d
P
394 new_node->r = node;
395 }
396 else {
397 new_node->l = node;
398 }
399 new_node->parent = node->parent;
400 if (node->parent == NULL) {
401 assert (patricia->head == node);
402 patricia->head = new_node;
403 }
404 else if (node->parent->r == node) {
405 node->parent->r = new_node;
406 }
407 else {
408 node->parent->l = new_node;
409 }
410 node->parent = new_node;
411 }
412 else {
7a25133f 413 glue = patricia_new_node(patricia, differ_bit, NULL);
526e7c1d 414 glue->parent = node->parent;
7a25133f 415
526e7c1d 416 if (differ_bit < patricia->maxbits &&
a9b4ce08 417 (is_bit_set(addr, differ_bit))) {
526e7c1d
P
418 glue->r = new_node;
419 glue->l = node;
420 }
421 else {
422 glue->r = node;
423 glue->l = new_node;
424 }
425 new_node->parent = glue;
426
427 if (node->parent == NULL) {
428 assert (patricia->head == node);
429 patricia->head = glue;
430 }
431 else if (node->parent->r == node) {
432 node->parent->r = glue;
433 }
434 else {
435 node->parent->l = glue;
436 }
437 node->parent = glue;
438 }
439 return (new_node);
440}
441
442void
443patricia_remove (patricia_tree_t *patricia, patricia_node_t *node)
444{
445 patricia_node_t *parent, *child;
446
447 assert (patricia);
448 assert (node);
449
450 if (node->r && node->l) {
451 /* this might be a placeholder node -- have to check and make sure
452 * there is a prefix aossciated with it ! */
453 if (node->prefix != NULL)
454 patricia_deref_prefix (node->prefix);
455 node->prefix = NULL;
456 return;
457 }
458
459 if (node->r == NULL && node->l == NULL) {
460 parent = node->parent;
461 patricia_deref_prefix (node->prefix);
7a25133f 462 freenode(node);
526e7c1d
P
463 patricia->num_active_node--;
464
465 if (parent == NULL) {
466 assert (patricia->head == node);
467 patricia->head = NULL;
468 return;
469 }
470
471 if (parent->r == node) {
472 parent->r = NULL;
473 child = parent->l;
474 }
475 else {
476 assert (parent->l == node);
477 parent->l = NULL;
478 child = parent->r;
479 }
480
481 if (parent->prefix)
482 return;
483
484 /* we need to remove parent too */
485
486 if (parent->parent == NULL) {
487 assert (patricia->head == parent);
488 patricia->head = child;
489 }
490 else if (parent->parent->r == parent) {
491 parent->parent->r = child;
492 }
493 else {
494 assert (parent->parent->l == parent);
495 parent->parent->l = child;
496 }
497 child->parent = parent->parent;
7a25133f 498 freenode(parent);
526e7c1d
P
499 patricia->num_active_node--;
500 return;
501 }
502
503 if (node->r) {
504 child = node->r;
505 }
506 else {
507 assert (node->l);
508 child = node->l;
509 }
510 parent = node->parent;
511 child->parent = parent;
512
513 patricia_deref_prefix (node->prefix);
7a25133f 514 freenode(node);
526e7c1d
P
515 patricia->num_active_node--;
516
517 if (parent == NULL) {
518 assert (patricia->head == node);
519 patricia->head = child;
520 return;
521 }
522
523 if (parent->r == node) {
524 parent->r = child;
525 }
526 else {
527 assert (parent->l == node);
528 parent->l = child;
529 }
530}
531
526e7c1d
P
532
533patricia_node_t *
534refnode(patricia_tree_t *tree, struct irc_in_addr *sin, int bitlen) {
535 patricia_node_t *node;
536 prefix_t *prefix;
537
01a924c2 538 node = patricia_search_exact(tree, sin, bitlen);
526e7c1d
P
539
540 if (node == NULL) {
541 prefix = patricia_new_prefix(sin, bitlen);
542 node = patricia_lookup(tree, prefix);
526e7c1d
P
543 patricia_deref_prefix(prefix);
544 } else if (node->prefix) {
545 patricia_ref_prefix(node->prefix);
546 }
547
548 return node;
549}
550
551void
552derefnode(patricia_tree_t *tree, patricia_node_t *node) {
553 if (!node || !node->prefix)
554 return;
555
556 if (node->prefix->ref_count == 1) {
526e7c1d
P
557 patricia_remove(tree, node);
558 } else
559 patricia_deref_prefix(node->prefix);
560}
7a25133f
P
561
562patricia_node_t *patricia_new_node(patricia_tree_t *patricia, unsigned char bit,prefix_t *prefix ) {
563 patricia_node_t *new_node = newnode();
564 memset(new_node->exts, 0, PATRICIA_MAXSLOTS * sizeof(void *));
565 new_node->bit = bit;
566 new_node->prefix = prefix;
567 new_node->usercount = 0;
568 new_node->parent = NULL;
569 new_node->l = new_node->r = NULL;
570 patricia->num_active_node++;
571 return new_node;
572}
573