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