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