The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
trie.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Path-compressed prefix tries
18 *
19 * @file src/lib/util/trie.c
20 *
21 * @copyright 2017 Alan DeKok (aland@freeradius.org)
22 */
23RCSID("$Id: 0c2e975b6d8169f07c309a70593953b7f3bcd440 $")
24
25#include <freeradius-devel/util/dict.h>
26#include <freeradius-devel/util/skip.h>
27#include <freeradius-devel/util/syserror.h>
28#include <freeradius-devel/util/trie.h>
29
30
31/*
32 * This file implements path-compressed, level-compressed
33 * patricia tries. The original research paper is:
34 *
35 * https://www.nada.kth.se/~snilsson/publications/Dynamic-trie-compression-implementation/
36 *
37 * The functionality has been extended to include intermediate
38 * nodes which consume 0 bits, but which hold user context data.
39 * These intermediate nodes allow for "longest prefix" matching.
40 * For example, in networking, you can have a routing table entry
41 * with 0/0 leading to one destination, and 10/8 leading to a
42 * different one. Looking up an address in the 10/8 network will
43 * return the 10/8 destination. Looking up any other address
44 * will return the default destination.
45 *
46 * In addition, we desire the ability to add and delete nodes
47 * dynamically. In the example given above, this means that
48 * after deleting 10/8, the trie should contain only the 0/0
49 * network and associated destination.
50 *
51 * As of yet, it does not do level compression. This can be
52 * added without (hopefully) too much work. That would require
53 * an additional step to "normalize" the trie.
54 *
55 * This code could be extended to do packet matching, through the
56 * inclusion of "don't care" paths. e.g. parsing an IP header,
57 * where the src/dst IP addresses are 32-bit "don't care" fields.
58 *
59 * It could also be extended via "count" paths, where the path
60 * holds a count that is used in another part of the trie. For
61 * example, in RADIUS. The attribute encoding is one byte
62 * attribute, one byte length, followed by "length - 2" bytes of
63 * data. At that point though, you might as well just use Ragel.
64 */
65
66/** Enable path compression (or not)
67 *
68 * With path compression, long sequences of bits are stored as a
69 * path, e.g. "abcdef". Without path compression, we would have to
70 * create a large number of intermediate 2^N-way nodes, all of which
71 * would have only one edge.
72 */
73#if !defined(NO_PATH_COMPRESSION) && !defined(WITH_PATH_COMPRESSION)
74#define WITH_PATH_COMPRESSION
75#endif
76
77//#define WITH_NODE_COMPRESSION
78
79#ifdef WITH_NODE_COMPRESSION
80#ifndef WITH_PATH_COMPRESSION
81#define WITH_PATH_COMPRESSION
82#endif
83
84#ifndef MAX_COMP_BITS
85#define MAX_COMP_BITS (8)
86#endif
87
88#ifndef MAX_COMP_EDGES
89#define MAX_COMP_EDGES (4)
90#endif
91
92#endif /* WITH_NODE_COMPRESSION */
93
94#define MAX_KEY_BYTES (256)
95#define MAX_KEY_BITS (MAX_KEY_BYTES * 8)
96
97#ifndef MAX_NODE_BITS
98#define MAX_NODE_BITS (4)
99#endif
100
101/** Internal sanity checks for debugging.
102 *
103 * Tries are complex. So we have verification routines for every
104 * type of node. These routines are called from within the trie
105 * manipulation functions. If the trie manipulation has a bug, the
106 * verification routines are likely to catch some of the more
107 * egregious issues.
108 */
109DIAG_OFF(unused-macros)
110#ifdef TESTING
111#define WITH_TRIE_VERIFY (1)
112# define MPRINT(...) fprintf(stderr, ## __VA_ARGS__)
113
114 /* define this to be MPRINT for additional debugging */
115# define MPRINT2(...)
116# define MPRINT3(...)
117static void trie_sprint(fr_trie_t *trie, uint8_t const *key, int start_bit, int lineno);
118#else
119# define MPRINT(...)
120# define MPRINT2(...)
121# define MPRINT3(...)
122#define trie_sprint(_trie, _key, _start_bit, _lineno)
123#endif
124
125#ifdef WITH_TRIE_VERIFY
126static int trie_verify(fr_trie_t *trie);
127//#define VERIFY(_x) fr_cond_assert(trie_verify((fr_trie_t *) _x) == 0)
128#define VERIFY(_x) if (trie_verify((fr_trie_t *) _x) < 0) do { fprintf(stderr, "FAIL VERIFY at %d - %s\n", __LINE__, fr_strerror()); fr_cond_assert(0); } while (0)
129#else
130#define VERIFY(_x)
131#endif
132
133/*
134 * Macros to swap one for the other.
135 */
136#define BITSOF(_x) ((_x) * 8)
137#define BYTEOF(_x) ((_x) >> 3)
138#define BYTES(_x) (((_x) + 0x07) >> 3)
139DIAG_ON(unused-macros)
140
141
142// @todo - do level compression
143// stop merging nodes if a key ends at the top of the level
144// otherwise merge so we have at least 2^4 way fan-out, but no more than 2^8
145// that should be a decent trade-off between memory and speed
146
147// @todo - generalized function to normalize the trie.
148
149
151 0xff, 0x7f, 0x3f, 0x1f,
152 0x0f, 0x07, 0x03, 0x01
153};
154
156 0x80, 0xc0, 0xe0, 0xf0,
157 0xf8, 0xfc, 0xfe, 0xff,
158};
159
160#if 0
161/*
162 * For testing and debugging.
163 */
164static char const *spaces = " ";
165#endif
166
167
168#if defined(WITH_PATH_COMPRESSION) || defined(TESTING)
169/*
170 * Table of how many leading bits there are in KEY1^KEY2.
171 */
172static uint8_t xor2lcp[256] = {
173 8, 7, 6, 6,
174 5, 5, 5, 5, /* 4x 5 */
175 4, 4, 4, 4, /* 8x 4 */
176 4, 4, 4, 4,
177 3, 3, 3, 3, /* 16x 3 */
178 3, 3, 3, 3,
179 3, 3, 3, 3,
180 3, 3, 3, 3,
181 2, 2, 2, 2, /* 32x 2 */
182 2, 2, 2, 2,
183 2, 2, 2, 2,
184 2, 2, 2, 2,
185 2, 2, 2, 2,
186 2, 2, 2, 2,
187 2, 2, 2, 2,
188 2, 2, 2, 2,
189 1, 1, 1, 1, /* 64x 1 */
190 1, 1, 1, 1,
191 1, 1, 1, 1,
192 1, 1, 1, 1,
193 1, 1, 1, 1,
194 1, 1, 1, 1,
195 1, 1, 1, 1,
196 1, 1, 1, 1,
197 1, 1, 1, 1,
198 1, 1, 1, 1,
199 1, 1, 1, 1,
200 1, 1, 1, 1,
201 1, 1, 1, 1,
202 1, 1, 1, 1,
203 1, 1, 1, 1,
204 1, 1, 1, 1,
205 0, 0, 0, 0, /* 128x 0 */
206 0, 0, 0, 0,
207 0, 0, 0, 0,
208 0, 0, 0, 0,
209 0, 0, 0, 0,
210 0, 0, 0, 0,
211 0, 0, 0, 0,
212 0, 0, 0, 0,
213 0, 0, 0, 0,
214 0, 0, 0, 0,
215 0, 0, 0, 0,
216 0, 0, 0, 0,
217 0, 0, 0, 0,
218 0, 0, 0, 0,
219 0, 0, 0, 0,
220 0, 0, 0, 0,
221};
222
223
224/** Get the longest prefix of the two keys.
225 *
226 */
227static int fr_trie_key_lcp(uint8_t const *key1, int keylen1, uint8_t const *key2, int keylen2, int start_bit)
228{
229 int lcp, end_bit;
230
231 if (!keylen1 || !keylen2) return 0;
232 fr_cond_assert((start_bit & 0x07) == start_bit);
233
234 end_bit = keylen1;
235 if (end_bit > keylen2) end_bit = keylen2;
236 end_bit += start_bit;
237
238 MPRINT2("%.*sLCP %02x%02x %02x%02x start %d length %d, %d\n",
239 start_bit, spaces, key1[0], key1[1], key2[0], key2[1], start_bit, keylen1, keylen2);
240
241 lcp = 0;
242
243 while (end_bit > 0) {
244 int num_bits;
245 uint8_t cmp1, cmp2, xor;
246
247 MPRINT2("END %d\n", end_bit);
248
249 /*
250 * Default to grabbing the whole byte.
251 */
252 cmp1 = key1[0];
253 cmp2 = key2[0];
254 num_bits = 8;
255
256 /*
257 * The LCP ends in this byte. Mask off the
258 * trailing bits so that they don't affect the
259 * result.
260 */
261 if (end_bit < 8) {
262 cmp1 &= used_bit_mask[end_bit - 1];
263 cmp2 &= used_bit_mask[end_bit - 1];
264 num_bits = end_bit;
265 }
266
267 /*
268 * The key doesn't start on the leading bit.
269 * Shift the data left until it does start there.
270 */
271 if ((start_bit & 0x07) != 0) {
272 cmp1 <<= start_bit;
273 cmp2 <<= start_bit;
274 num_bits -= start_bit;
275 end_bit -= start_bit;
276
277 /*
278 * For subsequent bytes we start on a
279 * byte boundary.
280 */
281 start_bit = 0;
282 }
283
284 xor = cmp1 ^ cmp2;
285
286 /*
287 * A table lookup is faster than looping through
288 * the bits. If the LCP is smaller than the
289 * number of bits we're looking up, we can stop.
290 *
291 * On the other hand, if it returns the same or
292 * too many bits, just do another round through
293 * the loop, so that we can update the pointers
294 * and check the exit conditions.
295 */
296 if (xor2lcp[xor] < num_bits) {
297 MPRINT2("RETURN %d + %d\n", lcp, xor2lcp[xor]);
298 return lcp + xor2lcp[xor];
299 }
300
301 /*
302 * The LCP may be longer than num_bits if we're
303 * checking the first byte, which has only
304 * "start_bit" things we care about. Ignore that
305 * case, and just keep going.
306 */
307
308 lcp += num_bits;
309 end_bit -= num_bits;
310 key1++;
311 key2++;
312 }
313
314 return lcp;
315}
316#endif
317
318//#define HEX_DUMP
319
320#ifdef HEX_DUMP
321static void hex_dump(FILE *fp, char const *msg, uint8_t const *key, int start_bit, int end_bit)
322{
323 int i;
324
325 fprintf(fp, "%s\ts=%zd e=%zd\t\t", msg, start_bit, end_bit);
326
327 for (i = 0; i < BYTES(end_bit); i++) {
328 fprintf(fp, "%02x ", key[i]);
329 }
330 fprintf(fp, "\n");
331}
332#endif
333
334/** Return a chunk of a key (in the low bits) for use in 2^N node de-indexing
335 *
336 */
337static CC_HINT(nonnull) uint16_t get_chunk(uint8_t const *key, uint32_t start_bit, uint32_t num_bits)
338{
339 uint16_t chunk;
340 int end_bit;
341
342 fr_cond_assert(num_bits > 0);
343 fr_cond_assert(num_bits <= 16);
344
345 /*
346 * Normalize it so that the caller doesn't have to.
347 */
348 if (start_bit > 7) {
349 key += BYTEOF(start_bit);
350 start_bit &= 0x07;
351 }
352
353 /*
354 * Special-case 1-bit lookups.
355 */
356 if (num_bits == 1) {
357 chunk = key[0] >> (7 - start_bit);
358 chunk &= 0x01;
359 return chunk;
360 }
361
362 /*
363 * Catch some simple use-cases.
364 */
365 if (start_bit == 0) {
366 if (num_bits < 7) return key[0] >> (8 - num_bits);
367 if (num_bits == 8) return key[0];
368
369 chunk = (key[0] << 8) | key[1];
370 if (num_bits < 16) chunk >>= (16 - num_bits);
371 fr_cond_assert(chunk < (1 << num_bits));
372 return chunk;
373 }
374
375 /*
376 * Load the first byte and mask off the bits we don't
377 * want.
378 */
379 chunk = key[0] & start_bit_mask[start_bit & 0x07];
380
381 fr_cond_assert(BYTEOF(start_bit + num_bits - 1) <= 1);
382
383 if (BYTEOF(start_bit + num_bits - 1) != 0) {
384 chunk <<= 8;
385 chunk |= key[1];
386 }
387
388 /*
389 * The bits we want are now all in the higher bits
390 * of "chunk". But we only want some of them.
391 *
392 * Shift the chunk so that the bits we want are now in
393 * the low bits.
394 */
395 end_bit = (start_bit + num_bits) & 0x07;
396 if (end_bit != 0) chunk >>= 8 - end_bit;
397
398 fr_cond_assert(chunk < (1 << num_bits));
399
400 return chunk;
401}
402
403
404static void write_chunk(uint8_t *out, int start_bit, int num_bits, uint16_t chunk) CC_HINT(nonnull);
405
406/** Write a chunk to an output buffer
407 *
408 */
409static void write_chunk(uint8_t *out, int start_bit, int num_bits, uint16_t chunk)
410{
411 fr_cond_assert(chunk < (1 << num_bits));
412
413 /*
414 * Normalize it so that the caller doesn't have to.
415 */
416 if (start_bit > 7) {
417 out += BYTEOF(start_bit);
418 start_bit &= 0x07;
419 }
420
421 /*
422 * Special-case 1-bit writes.
423 */
424 if (num_bits == 1) {
425 out[0] &= ~(1 << (7 - start_bit));
426 out[0] |= chunk << (7 - start_bit);
427 return;
428 }
429
430 /*
431 * Ensure that we don't write to more than 2 octets at
432 * the same time.
433 */
434 fr_cond_assert((start_bit + num_bits) <= 16);
435
436 /*
437 * Shift the chunk to the high bits, but leave room for
438 * start_bit
439 */
440 if ((start_bit + num_bits) < 16) chunk <<= (16 - (start_bit + num_bits));
441
442 /*
443 * Mask off the first bits that are already in the
444 * output. Then OR in the relevant bits of "chunk".
445 */
446 out[0] &= (used_bit_mask[start_bit] << 1);
447 out[0] |= chunk >> 8;
448
449 if ((start_bit + num_bits) > 8) {
450 out[1] = chunk & 0xff;
451 }
452}
453
454typedef enum fr_trie_type_t {
457#ifdef WITH_PATH_COMPRESSION
459#endif
460#ifdef WITH_NODE_COMPRESSION
461 FR_TRIE_COMP, /* 4-way, N bits deep */
462#endif
465
466#define FR_TRIE_MAX (FR_TRIE_NODE + 1)
467
468#ifdef TESTING
469static int trie_number = 0;
470
471#define TRIE_HEADER uint8_t type; uint8_t bits; int number
472#define TRIE_TYPE_CHECK(_x, _r) do { if ((trie->type == FR_TRIE_INVALID) || \
473 (trie->type >= FR_TRIE_MAX) || \
474 !trie_ ## _x ##_table [trie->type]) { \
475 fr_strerror_printf("unknown trie type %d", trie->type); \
476 return _r; \
477 } } while (0)
478
479#else
480#define TRIE_HEADER uint8_t type; uint8_t bits
481#define TRIE_TYPE_CHECK(_x, _r)
482#endif
483
484struct fr_trie_s {
486
487 fr_trie_t *trie; /* for USER and PATH nodes*/
488};
489
490typedef struct {
492
493 int used;
494 fr_trie_t *trie[];
496
497typedef struct {
499
501 void *data;
503
504#ifdef WITH_PATH_COMPRESSION
513#endif
514
515#ifdef WITH_NODE_COMPRESSION
516typedef struct {
518
519 int used; //!< number of used entries
520 uint8_t index[MAX_COMP_EDGES];
521 fr_trie_t *trie[MAX_COMP_EDGES];
522} fr_trie_comp_t;
523#endif
524
525
526/* ALLOC FUNCTIONS */
527
528static fr_trie_node_t *trie_node_alloc(TALLOC_CTX *ctx, int bits)
529{
530 fr_trie_node_t *node;
531 int size;
532
533 if ((bits <= 0) || (bits > 8)) {
534 fr_strerror_printf("Invalid bit size %d passed to node alloc", bits);
535 return NULL;
536 }
537
538 size = 1 << bits;
539
540 node = (fr_trie_node_t *) talloc_zero_array(ctx, uint8_t, sizeof(fr_trie_node_t) + sizeof(node->trie[0]) * size);
541 if (!node) {
542 fr_strerror_const("failed allocating node trie");
543 return NULL;
544 }
545
546 talloc_set_name_const(node, "fr_trie_node_t");
547 node->type = FR_TRIE_NODE;
548 node->bits = bits;
549
550#ifdef TESTING
551 node->number = trie_number++;
552#endif
553 return node;
554}
555
556/** Free a fr_trie_t
557 *
558 * We can't use talloc_free(), because we can't talloc_parent the
559 * nodes from each other, as talloc_steal() is O(N). So, we just
560 * recurse manually.
561 */
562static void trie_free(fr_trie_t *trie)
563{
564 if (!trie) return;
565
566 if (trie->type == FR_TRIE_USER) {
567 fr_trie_user_t *user = (fr_trie_user_t *) trie;
568
569 trie_free(user->trie);
570 talloc_free(user);
571 return;
572 }
573
574 if (trie->type == FR_TRIE_NODE) {
575 fr_trie_node_t *node = (fr_trie_node_t *) trie;
576 int i;
577
578 for (i = 0; i < (1 << node->bits); i++) {
579 if (!node->trie[i]) continue; /* save a function call in the common case */
580
581 trie_free(node->trie[i]);
582 }
583
584 talloc_free(node);
585 return;
586 }
587
588#ifdef WITH_PATH_COMPRESSION
589 if (trie->type == FR_TRIE_PATH) {
590 fr_trie_path_t *path = (fr_trie_path_t *) trie;
591
592 trie_free(path->trie);
593 talloc_free(path);
594 return;
595 }
596#endif
597
598#ifdef WITH_NODE_COMPRESSION
599 if (trie->type == FR_TRIE_COMP) {
600 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
601 int i;
602
603 for (i = 0; i < comp->used; i++) {
604 trie_free(comp->trie[i]);
605 }
606
608 return;
609 }
610#endif
611}
612
613static CC_HINT(nonnull(2)) fr_trie_user_t *fr_trie_user_alloc(TALLOC_CTX *ctx, void const *data)
614{
615 fr_trie_user_t *user;
616
617 user = talloc_zero(ctx, fr_trie_user_t);
618 if (!user) {
619 fr_strerror_const("failed allocating user trie");
620 return NULL;
621 }
622
623 user->type = FR_TRIE_USER;
624 user->data = UNCONST(void *, data);
625
626#ifdef TESTING
627 user->number = trie_number++;
628#endif
629
630 return user;
631}
632
633#ifdef WITH_PATH_COMPRESSION
634static CC_HINT(nonnull(2)) fr_trie_path_t *fr_trie_path_alloc(TALLOC_CTX *ctx, uint8_t const *key, int start_bit, int end_bit)
635{
636 fr_trie_path_t *path;
637
638 if (end_bit <= start_bit) {
639 fr_strerror_printf("path asked for start >= end, %d >= %d", start_bit, end_bit);
640 return NULL;
641 }
642
643 /*
644 * Normalize it so that the caller doesn't have to.
645 */
646 if (start_bit > 7) {
647 key += (start_bit >> 3);
648 end_bit -= 8 * (start_bit >> 3);
649 start_bit -= 8 * (start_bit >> 3);
650 }
651
652 if ((end_bit - start_bit) > 16) {
653 fr_strerror_printf("path asked for too many bits (%d)", end_bit - start_bit);
654 return NULL;
655 }
656
657 /*
658 * The "end_bit" is the bit we're not using, so it's
659 * allowed to point past the end of path->key.
660 */
661 if ((BYTEOF(start_bit) - BYTEOF(end_bit - 1)) > 1) {
662 fr_strerror_printf("path asked for too many bits / bytes (%d)", end_bit - start_bit);
663 return NULL;
664 }
665
666 path = talloc_zero(ctx, fr_trie_path_t);
667 if (!path) {
668 fr_strerror_const("failed allocating path trie");
669 return NULL;
670 }
671
672 path->type = FR_TRIE_PATH;
673 path->bits = end_bit - start_bit;
674 path->chunk = get_chunk(key, start_bit, path->bits);
675
676 /*
677 * Write the chunk back to the key.
678 */
679 write_chunk(&path->key[0], start_bit, path->bits, path->chunk);
680
681#if 0
682 fprintf(stderr, "PATH ALLOC key %02x%02x start %d end %d bits %d == chunk %04x key %02x%02x\n",
683 key[0], key[1],
684 start_bit, end_bit, path->bits,
685 path->chunk, path->key[0], path->key[1]);
686#endif
687
688#ifdef TESTING
689 path->number = trie_number++;
690#endif
691
692 return path;
693}
694#endif /* WITH_PATH_COMPRESSION */
695
696#ifdef WITH_NODE_COMPRESSION
697static fr_trie_comp_t *fr_trie_comp_alloc(TALLOC_CTX *ctx, int bits)
698{
699 fr_trie_comp_t *comp;
700
701 /*
702 * For 1 && 2 bits, just allocate fr_trie_node_t.
703 */
704 if ((bits <= 2) || (bits > MAX_COMP_BITS)) {
705 fr_strerror_printf("Invalid bit size %d passed to comp alloc", bits);
706 return NULL;
707 }
708
709 comp = talloc_zero(ctx, fr_trie_comp_t);
710 if (!comp) {
711 fr_strerror_const("failed allocating comp trie");
712 return NULL;
713 }
714
715 comp->type = FR_TRIE_COMP;
716 comp->bits = bits;
717 comp->used = 0;
718
719#ifdef TESTING
720 comp->number = trie_number++;
721#endif
722 return comp;
723}
724#endif /* WITH_NODE_COMPRESSION */
725
726typedef struct {
727 uint8_t buffer[16]; /* for get_key callbacks */
731
732/** Allocate a trie
733 *
734 * @param ctx The talloc ctx.
735 * @param get_key The "get key from object" function.
736 * @param free_data Callback to free data.
737 * @return
738 * - NULL on error
739 * - fr_trie_node_t on success
740 */
741fr_trie_t *fr_trie_alloc(TALLOC_CTX *ctx, fr_trie_key_t get_key, fr_free_t free_data)
742{
743 fr_trie_user_t *user;
744 fr_trie_ctx_t *uctx;
745
746 /*
747 * The trie itself is just a user node with user data
748 * that is the get_key function.
749 */
750 user = (fr_trie_user_t *) fr_trie_user_alloc(ctx, "");
751 if (!user) return NULL;
752
753 /*
754 * Only the top-level node here can have 'user->data == NULL'
755 */
756 user->data = uctx = talloc_zero(user, fr_trie_ctx_t);
757 if (!user->data) {
758 talloc_free(user);
759 return NULL;
760 }
761
762 uctx->get_key = get_key;
763 uctx->free_data = free_data;
764
765 return (fr_trie_t *) user;
766}
767
768/* SPLIT FUNCTIONS */
769
770/** Split a node at bits
771 *
772 */
773static CC_HINT(nonnull(2)) fr_trie_node_t *trie_node_split(TALLOC_CTX *ctx, fr_trie_node_t *node, int bits)
774{
776 int i, remaining_bits;
777
778 /*
779 * Can't split zero bits, more bits than the node has, or
780 * a node which has 1 bit.
781 */
782 if ((bits == 0) || (bits >= node->bits) || (node->bits == 1)) {
783 fr_strerror_printf("invalid value for node split (%d / %d)", bits, node->bits);
784 return NULL;
785 }
786
787 split = trie_node_alloc(ctx, bits);
788 if (!split) return NULL;
789
790 remaining_bits = node->bits - bits;
791
792 /*
793 * Allocate the children. For now, just brute-force all
794 * of the children. We take a later pass at optimizing this.
795 */
796 for (i = 0; i < (1 << bits); i++) {
797 int j;
798 fr_trie_node_t *child;
799
800 child = trie_node_alloc(ctx, remaining_bits);
801 if (!child) {
803 return NULL;
804 }
805
806 for (j = 0; j < (1 << remaining_bits); j++) {
807 if (!node->trie[(i << remaining_bits) + j]) continue;
808
809 child->trie[j] = node->trie[(i << remaining_bits) + j];
810 node->trie[(i << remaining_bits) + j] = NULL; /* so we don't free it when freeing 'node' */
811 child->used++;
812 }
813
814 if (!child->used) {
815 talloc_free(child); /* no children, so no need to recurse */
816 continue;
817 }
818
819 split->trie[i] = (fr_trie_t *) child;
820 split->used++;
821 }
822
823 /*
824 * Note that we do NOT free "node". The caller still
825 * needs it for some activities.
826 */
827 return split;
828}
829
830#ifdef WITH_PATH_COMPRESSION
831static CC_HINT(nonnull(2)) fr_trie_path_t *trie_path_split(TALLOC_CTX *ctx, fr_trie_path_t *path, int start_bit, int lcp)
832{
833 fr_trie_path_t *split, *child;
834#ifdef TESTING
835 uint8_t key[2] = { 0, 0 };
836#endif
837
838 if ((lcp <= 0) || (lcp > path->bits) || (start_bit < 0)) {
839 fr_strerror_printf("invalid parameter %d %d to path split", lcp, start_bit);
840 return NULL;
841 }
842
843 MPRINT3("%.*sSPLIT start %d\n", start_bit, spaces, start_bit);
844 start_bit &= 0x07;
845
846 split = fr_trie_path_alloc(ctx, &path->key[0], start_bit, start_bit + lcp);
847 if (!split) return NULL;
848
849 child = fr_trie_path_alloc(ctx, &path->key[0], start_bit + lcp, start_bit + path->bits);
850 if (!child) {
852 return NULL;
853 }
854
855 split->trie = (fr_trie_t *) child;
856 child->trie = (fr_trie_t *) path->trie;
857
858 /*
859 * Don't free "path" until we've successfully inserted
860 * the new key.
861 */
862
863#ifdef TESTING
864 /*
865 * Check that the two chunks add up to the parent chunk.
866 */
867 fr_cond_assert(path->chunk == ((split->chunk << (path->bits - lcp)) | child->chunk));
868
869 /*
870 * Check that the two keys match the parent key.
871 */
872
873 write_chunk(&key[0], start_bit, split->bits, split->chunk);
874 write_chunk(&key[0], start_bit + split->bits, child->bits, child->chunk);
875
876 fr_cond_assert(key[0] == path->key[0]);
877 fr_cond_assert(key[1] == path->key[1]);
878
879 MPRINT3("%.*ssplit %02x%02x start %d split %d -> %02x%02x %02x%02x\n",
880 start_bit, spaces,
881 path->key[0], path->key[1],
882 start_bit, split->bits,
883 split->key[0], split->key[1],
884 child->key[0], child->key[1]);
885#endif
886
887 return split;
888}
889
890static CC_HINT(nonnull(2)) fr_trie_t *trie_key_alloc(TALLOC_CTX *ctx, uint8_t const *key, int start_bit, int end_bit, void *data)
891{
892 fr_trie_path_t *path;
893 int next_bit;
894
895 if (start_bit == end_bit) return (fr_trie_t *) fr_trie_user_alloc(ctx, data);
896
897 if (start_bit > end_bit) {
898 fr_strerror_printf("key_alloc asked for start >= end, %d >= %d", start_bit, end_bit);
899 return NULL;
900 }
901
902 /*
903 * Grab some more bits. Try to grab 16 bits at a time.
904 */
905 next_bit = start_bit + 16 - (start_bit & 0x07);
906
907 if (next_bit >= end_bit) {
908 path = fr_trie_path_alloc(ctx, key, start_bit, end_bit);
909 if (!path) return NULL;
910
911 path->trie = (fr_trie_t *) fr_trie_user_alloc(ctx, data);
912 return (fr_trie_t *) path;
913 }
914
915
916 path = fr_trie_path_alloc(ctx, key, start_bit, next_bit);
917 if (!path) return NULL;
918
919 path->trie = (fr_trie_t *) trie_key_alloc(ctx, key, next_bit, end_bit, data);
920 if (!path->trie) {
921 talloc_free(path); /* no children */
922 return NULL;
923 }
924
925 return (fr_trie_t *) path;
926}
927#else /* WITH_PATH_COMPRESSION */
928static CC_HINT(nonnull(2)) fr_trie_t *trie_key_alloc(TALLOC_CTX *ctx, uint8_t const *key, int start_bit, int end_bit, void *data)
929{
930 fr_trie_node_t *node;
931 uint16_t chunk;
932 int bits = MAX_NODE_BITS;
933
934 if (start_bit == end_bit) {
935 return (fr_trie_t *) fr_trie_user_alloc(ctx, data);
936 }
937
938 bits = end_bit - start_bit;
939 if (bits > MAX_NODE_BITS) bits = MAX_NODE_BITS;
940
941 /*
942 * We only want one edge here.
943 */
944 node = trie_node_alloc(ctx, bits);
945 if (!node) return NULL;
946
947 chunk = get_chunk(key, start_bit, node->bits);
948 node->trie[chunk] = trie_key_alloc(ctx, key, start_bit + node->bits, end_bit, data);
949 if (!node->trie[chunk]) {
950 talloc_free(node); /* no children */
951 return NULL;
952 }
953 node->used++;
954
955 return (fr_trie_t *) node;
956}
957#endif
958
959
960#if 0
961/** Split a compressed at bits
962 *
963 */
964#ifdef WITH_NODE_COMPRESSION
965static fr_trie_t *trie_comp_split(TALLOC_CTX *ctx, fr_trie_comp_t *comp, int start_bit, int bits)
966{
967 int i;
968 fr_trie_comp_t *split;
969
970 /*
971 * Can't split zero bits, more bits than the node has, or
972 * a node which has 1 bit.
973 */
974 if ((bits == 0) || (bits >= comp->bits)) {
975 fr_strerror_printf("invalid value for comp split (%d / %d)", bits, comp->bits);
976 return NULL;
977 }
978
979 split = fr_trie_comp_alloc(ctx, bits);
980 if (!split) return NULL;
981
982 if (start_bit > 7) start_bit &= 0x07;
983
984 // walk over the edges, seeing how many edges have the same before bits
985 //
986 // if all have the same bits, then split by creating a path
987 // node, and then a child split node.
988
989 /*
990 * Walk over each edge, inserting the first chunk into
991 * the new node, and the split node...
992 */
993 for (i = 0; i < comp->used; i++) {
994 int j, where;
995 uint16_t before, after;
996 uint8_t key[2];
997 fr_trie_path_t *path;
998
999 before = i >> (comp->bits - bits);
1000 after = i & ((1 << bits) - 1);
1001
1002 write_chunk(&key[0], start_bit, comp->bits, i);
1003
1004 // see if "before" was already used in the newly created node.
1005
1006 where = 0;
1007
1008 for (j = 0; j < split->used; j++) {
1009 if (before == split->index[j]) {
1010 where = j;
1011 break;
1012 }
1013 }
1014
1015 if (split->index[where]) {
1016 // the children MUST be different
1017 // create another compressed node as a child, and go from there.
1018
1019 } else {
1020 split->index[split->used] = before;
1021 path = fr_trie_path_alloc(ctx, &key[0], start_bit, start_bit + bits);
1022 if (!path) goto fail;
1023
1024 split->trie[split->used++] = (fr_trie_t *) path;
1025 path->trie = comp->trie[i];
1026 }
1027 }
1028
1029 return (fr_trie_t *) split;
1030
1031fail:
1032 for (i = 0; i < split->used; i++) {
1033 talloc_free(split->trie[i]);
1034 }
1036 return NULL;
1037}
1038#endif /* WITH_NODE_COMPRESSION */
1039#endif
1040
1041/* ADD EDGES */
1042
1043#ifdef WITH_PATH_COMPRESSION
1044/** Add an edge to a node.
1045 *
1046 * This function is so that we can abstract 2^N-way nodes, or
1047 * compressed edge nodes.
1048 */
1049static int trie_add_edge(fr_trie_t *trie, uint16_t chunk, fr_trie_t *child)
1050{
1051 fr_trie_node_t *node;
1052
1053#ifdef WITH_NODE_COMPRESSION
1054 if (trie->type == FR_TRIE_COMP) {
1055 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
1056 int i, edge;
1057
1058 if (chunk >= (1 << comp->bits)) return -1;
1059
1060 if (comp->used >= MAX_COMP_EDGES) return -1;
1061
1062 edge = comp->used;
1063 for (i = 0; i < comp->used; i++) {
1064 if (comp->index[i] < chunk) continue;
1065
1066 if (comp->index[edge] == chunk) return -1;
1067
1068 edge = i;
1069 break;
1070 }
1071
1072 if (edge == MAX_COMP_EDGES) return -1;
1073
1074 /*
1075 * Move the nodes up so that we have room for the
1076 * new edge.
1077 */
1078 for (i = edge; i < comp->used; i++) {
1079 comp->index[i + 1] = comp->index[i];
1080 comp->trie[i + 1] = comp->trie[i];
1081 }
1082
1083 comp->index[edge] = chunk;
1084 comp->trie[edge] = child;
1085
1086 comp->used++;
1087 VERIFY(comp);
1088 return 0;
1089 }
1090#endif
1091
1092 if (trie->type != FR_TRIE_NODE) return -1;
1093
1094 node = (fr_trie_node_t *) trie;
1095
1096 if (chunk >= (1 << node->bits)) return -1;
1097
1098 if (node->trie[chunk] != NULL) return -1;
1099
1100 node->used++;
1101 node->trie[chunk] = child;
1102
1103 return 0;
1104}
1105#endif
1106
1107/* MATCH FUNCTIONS */
1108
1109typedef void *(*trie_key_match_t)(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact);
1110
1111static void *trie_key_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact);
1112
1113static void *trie_user_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
1114{
1115 fr_trie_user_t *user = (fr_trie_user_t *) trie;
1116 void *data;
1117
1118 /*
1119 * We've matched the input exactly. Return the
1120 * user data.
1121 */
1122 if (start_bit == end_bit) return user->data;
1123
1124 /*
1125 * We're not at the end of the input. Go find a
1126 * deeper match. If a match is found, return
1127 * that.
1128 */
1129 data = trie_key_match(user->trie, key, start_bit, end_bit, exact);
1130 if (data) return data;
1131
1132 /*
1133 * We didn't find anything deeper in the trie,
1134 * AND we require an exact match. That's a
1135 * failure.
1136 */
1137 if (exact) {
1138 MPRINT2("no exact match at %d\n", __LINE__);
1139 return NULL;
1140 }
1141
1142 /*
1143 * Return the closest (i.e. inexact) match.
1144 */
1145 return user->data;
1146}
1147
1148static void *trie_node_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
1149{
1150 uint16_t chunk;
1151 fr_trie_node_t *node = (fr_trie_node_t *) trie;
1152
1153 chunk = get_chunk(key, start_bit, node->bits);
1154 if (!node->trie[chunk]) {
1155 MPRINT2("no match for node chunk %02x at %d\n", chunk, __LINE__);
1156 return NULL;
1157 }
1158
1159 return trie_key_match(node->trie[chunk], key, start_bit + node->bits, end_bit, exact);
1160}
1161
1162#ifdef WITH_PATH_COMPRESSION
1163static void *trie_path_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
1164{
1165 uint16_t chunk;
1166 fr_trie_path_t *path = (fr_trie_path_t *) trie;
1167
1168 chunk = get_chunk(key, start_bit, path->bits);
1169 if (chunk != path->chunk) return NULL;
1170
1171 return trie_key_match(path->trie, key, start_bit + path->bits, end_bit, exact);
1172}
1173#endif
1174
1175#ifdef WITH_NODE_COMPRESSION
1176static void *trie_comp_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
1177{
1178 int i;
1179 uint16_t chunk;
1180 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
1181
1182 chunk = get_chunk(key, start_bit, comp->bits);
1183
1184 for (i = 0; i < comp->used; i++) {
1185 if (comp->index[i] < chunk) continue;
1186
1187 if (comp->index[i] == chunk) {
1188 return trie_key_match(comp->trie[i], key, start_bit + comp->bits, end_bit, exact);
1189 }
1190
1191 /*
1192 * The edges are ordered smallest to largest. So
1193 * if the edge is larger than the chunk, NO edge
1194 * will match the chunk.
1195 */
1196 return NULL;
1197 }
1198
1199 return NULL;
1200}
1201#endif
1202
1206#ifdef WITH_PATH_COMPRESSION
1208#endif
1209#ifdef WITH_NODE_COMPRESSION
1210 [ FR_TRIE_COMP ] = trie_comp_match,
1211#endif
1212};
1213
1214
1215/** Match a key in a trie and return user ctx, if any
1216 *
1217 * The key may be LONGER than entries in the trie. In which case the
1218 * closest match is returned.
1219 *
1220 * @param trie the trie
1221 * @param key the key
1222 * @param start_bit the start bit
1223 * @param end_bit the end bit
1224 * @param exact do we return an exact match, or the shortest one.
1225 * @return
1226 * - NULL on not found
1227 * - void* user ctx on found
1228 */
1229static void *trie_key_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
1230{
1231 if (!trie) return NULL;
1232
1233 /*
1234 * We've run out of trie, so it's not a match.
1235 */
1236 if ((start_bit + trie->bits) > end_bit) {
1237 MPRINT2("%d + %d = %d > %d\n",
1238 start_bit, trie->bits, start_bit + trie->bits, end_bit);
1239#ifdef TESTING
1240 MPRINT2("no match for key too short for trie NODE-%d at %d\n", trie->number, __LINE__);
1241#endif
1242 return NULL;
1243 }
1244
1245 TRIE_TYPE_CHECK(match, NULL);
1246
1247 /*
1248 * Recursively match each type.
1249 */
1250 return trie_match_table[trie->type](trie, key, start_bit, end_bit, exact);
1251}
1252
1253/** Lookup a key in a trie and return user ctx, if any
1254 *
1255 * The key may be LONGER than entries in the trie. In which case the
1256 * closest match is returned.
1257 *
1258 * @param ft the trie
1259 * @param key the key bytes
1260 * @param keylen length in bits of the key
1261 * @return
1262 * - NULL on not found
1263 * - void* user ctx on found
1264 */
1265void *fr_trie_lookup_by_key(fr_trie_t const *ft, void const *key, size_t keylen)
1266{
1267 fr_trie_user_t *user;
1268
1269 if (keylen > MAX_KEY_BITS) return NULL;
1270
1271 if (!ft->trie) return NULL;
1272
1273 user = UNCONST(fr_trie_user_t *, ft);
1274
1275 return trie_key_match(user->trie, key, 0, keylen, false);
1276}
1277
1278/** Match a key and length in a trie and return user ctx, if any
1279 *
1280 * Only the exact match is returned.
1281 *
1282 * @param ft the trie
1283 * @param key the key bytes
1284 * @param keylen length in bits of the key
1285 * @return
1286 * - NULL on not found
1287 * - void* user ctx on found
1288 */
1289void *fr_trie_match_by_key(fr_trie_t const *ft, void const *key, size_t keylen)
1290{
1291 fr_trie_user_t *user;
1292
1293 if (keylen > MAX_KEY_BITS) return NULL;
1294
1295 if (!ft->trie) return NULL;
1296
1297 user = UNCONST(fr_trie_user_t *, ft);
1298
1299 return trie_key_match(user->trie, key, 0, keylen, true);
1300}
1301
1302/* INSERT FUNCTIONS */
1303
1304#ifdef TESTING
1305static void trie_check(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, void *data, int lineno)
1306{
1307 void *answer;
1308
1309 trie_sprint(trie, key, start_bit, lineno);
1310
1311 answer = trie_key_match(trie, key, start_bit, end_bit, true);
1312 if (!answer) {
1313 fr_strerror_printf("Failed trie check answer at %d", lineno);
1314
1315 // print out the current trie!
1316 MPRINT3("%.*sFailed to find user data %s from start %d end %d at %d\n", start_bit, spaces, data,
1317 start_bit, end_bit, lineno);
1318 fr_cond_assert(0);
1319 }
1320
1321 if (answer != data) {
1322 fr_strerror_printf("Failed trie check answer == data at %d", lineno);
1323
1324 MPRINT3("%.*sFound wrong user data %s != %s, from start %d end %d at %d\n", start_bit, spaces,
1325 answer, data, start_bit, end_bit, lineno);
1326 fr_cond_assert(0);
1327 }
1328}
1329#else
1330#define trie_check(_trie, _key, _start_bit, _end_bit, _data, _lineno)
1331#endif
1332
1333static int trie_key_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data) CC_HINT(nonnull(2,3,6));
1334
1335typedef int (*trie_key_insert_t)(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data);
1336
1337static int trie_user_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
1338{
1339 fr_trie_t *trie = *trie_p;
1340 fr_trie_user_t *user = (fr_trie_user_t *) trie;
1341
1342 MPRINT3("user insert to start %d end %d with data %s\n", start_bit, end_bit, (char *) data);
1343
1344 /*
1345 * Just insert the key into user->trie.
1346 */
1347 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1348 return trie_key_insert(ctx, &user->trie, key, start_bit, end_bit, data);
1349}
1350
1351static int trie_node_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
1352{
1353 fr_trie_t *trie = *trie_p;
1354 fr_trie_node_t *node = (fr_trie_node_t *) trie;
1355 fr_trie_t *trie_to_free = NULL;
1356 uint32_t chunk;
1357
1358 MPRINT3("%.*snode insert end %d with data %s\n",
1359 start_bit, spaces, end_bit, (char *) data);
1360
1361 /*
1362 * The current node is longer than the input bits
1363 * for the key. Split the node into a smaller
1364 * N-way node, and insert the key into the (now
1365 * fitting) node.
1366 */
1367 if ((start_bit + node->bits) > end_bit) {
1369
1370 MPRINT3("%.*snode insert splitting %d at %d start %d end %d with data %s\n",
1371 start_bit, spaces,
1372 node->bits, start_bit - end_bit,
1373 start_bit, end_bit, (char *) data);
1374
1375 split = trie_node_split(ctx, node, end_bit - start_bit);
1376 if (!split) {
1377 fr_strerror_printf("Failed splitting node at %d\n", __LINE__);
1378 return -1;
1379 }
1380
1381 trie_to_free = (fr_trie_t *) node;
1382 node = split;
1383 }
1384
1385 chunk = get_chunk(key, start_bit, node->bits);
1386
1387 /*
1388 * No existing trie, create a brand new trie from
1389 * the key.
1390 */
1391 if (!node->trie[chunk]) {
1392 node->trie[chunk] = trie_key_alloc(ctx, key, start_bit + node->bits, end_bit, data);
1393 if (!node->trie[chunk]) {
1394 fr_strerror_printf("Failed key_alloc at %d\n", __LINE__);
1395 if (trie_to_free) trie_free(trie_to_free);
1396 return -1;
1397 }
1398 node->used++;
1399
1400 } else {
1401 /*
1402 * Recurse in order to insert the key
1403 * into the current node.
1404 */
1405 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1406 if (trie_key_insert(ctx, &node->trie[chunk], key, start_bit + node->bits, end_bit, data) < 0) {
1407 MPRINT("Failed recursing at %d\n", __LINE__);
1408 if (trie_to_free) trie_free(trie_to_free);
1409 return -1;
1410 }
1411 }
1412
1413 trie_check((fr_trie_t *) node, key, start_bit, end_bit, data, __LINE__);
1414
1415 MPRINT3("%.*snode insert returning at %d\n",
1416 start_bit, spaces, __LINE__);
1417
1418 if (trie_to_free) trie_free(trie_to_free);
1419 *trie_p = (fr_trie_t *) node;
1420 VERIFY(node);
1421 return 0;
1422}
1423
1424#ifdef WITH_PATH_COMPRESSION
1425static CC_HINT(nonnull(2,3,6)) int trie_path_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
1426{
1427 fr_trie_t *trie = *trie_p;
1428 fr_trie_path_t *path = (fr_trie_path_t *) trie;
1429 uint32_t chunk;
1430 int lcp, bits;
1431 uint8_t const *key2;
1432 int start_bit2;
1433 fr_trie_t *node;
1434 fr_trie_t *child;
1435
1436 MPRINT3("%.*spath insert start %d end %d with key %02x%02x data %s\n",
1437 start_bit, spaces, start_bit, end_bit, key[0], key[1], (char *) data);
1438
1439 VERIFY(path);
1440 trie_sprint((fr_trie_t *) path, key, start_bit, __LINE__);
1441
1442 /*
1443 * The key exactly matches the path. Recurse.
1444 */
1445 if (start_bit + path->bits <= end_bit) {
1446 chunk = get_chunk(key, start_bit, path->bits);
1447
1448 /*
1449 * The chunk matches exactly. Recurse to
1450 * insert the key into the child trie.
1451 */
1452 if (chunk == path->chunk) {
1453 MPRINT3("%.*spath chunk matches %04x bits of %d\n",
1454 start_bit, spaces, chunk, path->bits);
1455 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1456 if (trie_key_insert(ctx, &path->trie, key, start_bit + path->bits, end_bit, data) < 0) {
1457 return -1;
1458 }
1459
1460 trie_check((fr_trie_t *) path, key, start_bit, end_bit, data, __LINE__);
1461
1462 MPRINT3("%.*spath returning at %d\n", start_bit, spaces, __LINE__);
1463 VERIFY(path);
1464 return 0;
1465 }
1466
1467 bits = path->bits;
1468 MPRINT3("%.*spath using %d\n", start_bit, spaces, path->bits);
1469
1470 } else {
1471 /*
1472 * Limit the number of bits we check to
1473 * the number of bits left in the key.
1474 */
1475 bits = end_bit - start_bit;
1476 MPRINT3("%.*spath limiting %d to %d\n", start_bit, spaces, path->bits, bits);
1477 }
1478
1479 /*
1480 * Figure out what part of the key we need to
1481 * look at for LCP.
1482 */
1483 key2 = key;
1484 start_bit2 = start_bit;
1485 if (start_bit2 > 7) {
1486 key2 += (start_bit2 >> 3);
1487 start_bit2 -= 8 * (start_bit2 >> 3);
1488 }
1489
1490 /*
1491 * Get the LCP. If we have one, split the path
1492 * node at the LCP. Replace the parent with the
1493 * first half of the path, and build an N-way
1494 * node for the second half.
1495 */
1496 lcp = fr_trie_key_lcp(&path->key[0], bits, key2, bits, start_bit2);
1497 MPRINT3("%.*spath lcp %d\n", start_bit, spaces, lcp);
1498
1499 /*
1500 * This should have been caught above.
1501 */
1502 if (lcp == path->bits) {
1503 fr_strerror_const("found lcp which should have been previously found");
1504 return -1;
1505 }
1506
1507 if (lcp > 0) {
1509
1510 /*
1511 * Note that "path" is still valid after this
1512 * call. We will rewrite things on the way back
1513 * up the stack.
1514 */
1515 MPRINT3("%.*spath split depth %d bits %d at lcp %d with data %s\n",
1516 start_bit, spaces, start_bit, path->bits, lcp, (char *) data);
1517
1518 MPRINT3("%.*spath key %02x%02x input key %02x%02x, offset %d\n",
1519 start_bit, spaces,
1520 path->key[0],path->key[1],
1521 key[0], key[1],
1522 start_bit2);
1523
1524 split = trie_path_split(ctx, path, start_bit2, lcp);
1525 if (!split) {
1526 fr_strerror_printf("failed path split at %d\n", __LINE__);
1527 return -1;
1528 }
1529
1530 trie_sprint((fr_trie_t *) path, key, start_bit, __LINE__);
1531 trie_sprint((fr_trie_t *) split, key, start_bit, __LINE__);
1532 trie_sprint((fr_trie_t *) split->trie, key, start_bit + split->bits, __LINE__);
1533
1534 /*
1535 * Recurse to insert the key into the child node.
1536 * Note that if "bits > MAX_NODE_BITS", we will
1537 * have to split "path" again.
1538 */
1539 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1540 if (trie_key_insert(ctx, &split->trie, key, start_bit + split->bits, end_bit, data) < 0) {
1541 talloc_free(split->trie);
1543 return -1;
1544 }
1545
1546 /*
1547 * We can't have two LCPs in a row here, as we
1548 * SHOULD have found the LCP above!
1549 */
1551 fr_cond_assert(split->trie->type != FR_TRIE_PATH);
1552
1553 trie_check((fr_trie_t *) split, key, start_bit, end_bit, data, __LINE__);
1554
1555 MPRINT3("%.*spath returning at %d\n", start_bit, spaces, __LINE__);
1556 talloc_free(path);
1557 *trie_p = (fr_trie_t *) split;
1558 VERIFY(split);
1559 return 0;
1560 }
1561
1562 /*
1563 * Else there's no common prefix. Just create an
1564 * fanout node.
1565 */
1566 /*
1567 * We only want two edges here. Try to create a
1568 * compressed N-way node if possible.
1569 */
1570#ifdef WITH_NODE_COMPRESSION
1571 if (bits > 2) {
1572 if (bits > MAX_COMP_BITS) bits = MAX_COMP_BITS;
1573
1574 MPRINT3("%.*sFanout to comp %d at depth %d data %s\n", start_bit, spaces, bits, start_bit, (char *) data);
1575 node = (fr_trie_t *) fr_trie_comp_alloc(ctx, bits);
1576 } else
1577#endif
1578 {
1579 /*
1580 * Without path compression create no more than a
1581 * 16-way node.
1582 */
1583 if (bits > MAX_NODE_BITS) bits = MAX_NODE_BITS;
1584
1585 MPRINT3("%.*sFanout to node %d at depth %d data %s\n", start_bit, spaces, bits, start_bit, (char *) data);
1586 node = (fr_trie_t *) trie_node_alloc(ctx, bits);
1587 }
1588 if (!node) return -1;
1589
1590 /*
1591 * Get the chunk from the path, and insert the child trie
1592 * into the node at that chunk.
1593 */
1594 chunk = get_chunk(&path->key[0], start_bit2, node->bits);
1595
1596 if (node->bits == path->bits) {
1597 child = path->trie;
1598
1599 } else {
1600 /*
1601 * Skip the common prefix.
1602 */
1603 child = (fr_trie_t *) fr_trie_path_alloc(ctx, &path->key[0], start_bit2 + node->bits, start_bit2 + path->bits);
1604 if (!child) {
1605 fr_strerror_printf("failed allocating path child at %d", __LINE__);
1606 return -1;
1607 }
1608
1609 /*
1610 * Patch in the child trie.
1611 */
1612 ((fr_trie_path_t *)child)->trie = path->trie;
1613
1614 VERIFY(child);
1615 }
1616
1617 trie = NULL;
1618
1619 /*
1620 * Recurse to insert the key into the second edge. If
1621 * this fails, then we haven't changed anything. So just
1622 * free memory and return.
1623 *
1624 * Note that if "bits > DEFAULT_BITS", we will have to
1625 * split "path" again.
1626 */
1627 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1628 if (trie_key_insert(ctx, &trie, key, start_bit + node->bits, end_bit, data) < 0) {
1629 talloc_free(node);
1630 if (child != path->trie) talloc_free(child);
1631 return -1;
1632 }
1633
1634 /*
1635 * Copy the first edge over to the first chunk.
1636 */
1637 if (trie_add_edge(node, chunk, child) < 0) {
1638 fr_strerror_printf("chunk failure in insert node %d at %d", node->bits, __LINE__);
1639 talloc_free(node);
1640 if (child != path->trie) talloc_free(child);
1641 return -1;
1642 }
1643
1644 /*
1645 * Copy the second edge from the new chunk.
1646 */
1647 chunk = get_chunk(key, start_bit, node->bits);
1648 if (trie_add_edge(node, chunk, trie) < 0) {
1649 fr_strerror_printf("chunk failure in insert node %d at %d", node->bits, __LINE__);
1650 talloc_free(node);
1651 trie_free(trie);
1652 return -1;
1653 }
1654
1655 trie_check((fr_trie_t *) node, key, start_bit, end_bit, data, __LINE__);
1656
1657 MPRINT3("%.*spath returning at %d\n", start_bit, spaces, __LINE__);
1658
1659 /*
1660 * Only update the answer if the insert succeeded.
1661 */
1662 *trie_p = node;
1663 talloc_free(path);
1664 VERIFY(node);
1665 return 0;
1666}
1667#endif
1668
1669#ifdef WITH_NODE_COMPRESSION
1670static CC_HINT(nonnull(2,3,6)) int trie_comp_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
1671{
1672 int i, bits;
1673 fr_trie_t *trie = *trie_p;
1674 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
1675 fr_trie_node_t *node;
1676 uint16_t chunk;
1677
1678 MPRINT3("%.*scomp insert start %d end %d with key %02x%02x data %s\n",
1679 start_bit, spaces, start_bit, end_bit, key[0], key[1], (char *) data);
1680
1681 if ((end_bit - start_bit) < comp->bits) {
1682 fr_strerror_printf("Not implemented at %d", __LINE__);
1683 return -1;
1684 }
1685
1686 chunk = get_chunk(key, start_bit, comp->bits);
1687
1688 /*
1689 * Search for a matching edge. If found, recurse and
1690 * insert the key there.
1691 */
1692 for (i = 0; i < comp->used; i++) {
1693 if (comp->index[i] < chunk) continue;
1694
1695 /*
1696 * We've found a matching chunk, recurse.
1697 */
1698 if (comp->index[i] == chunk) {
1699 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1700 if (trie_key_insert(ctx, &comp->trie[i], key, start_bit + comp->bits, end_bit, data) < 0) {
1701 MPRINT3("%.*scomp failed recursing at %d", start_bit, spaces, __LINE__);
1702 return -1;
1703 }
1704
1705 trie_check((fr_trie_t *) comp, key, start_bit, end_bit, data, __LINE__);
1706
1707 MPRINT3("%.*scomp returning at %d", start_bit, spaces, __LINE__);
1708 VERIFY(comp);
1709 return 0;
1710 }
1711
1712 /*
1713 * The chunk is larger than the current edge,
1714 * stop.
1715 */
1716 break;
1717 }
1718
1719 /*
1720 * No edge matches the chunk from the key. Insert the
1721 * child trie into a place-holder entry, so that we don't
1722 * modify the current node on failure.
1723 */
1724 if (comp->used < MAX_COMP_EDGES) {
1725 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1726 trie = NULL;
1727 if (trie_key_insert(ctx, &trie, key, start_bit + comp->bits, end_bit, data) < 0) {
1728 MPRINT3("%.*scomp failed recursing at %d", start_bit, spaces, __LINE__);
1729 return -1;
1730 }
1731 fr_cond_assert(trie != NULL);
1732
1733 if (trie_add_edge((fr_trie_t *) comp, chunk, trie) < 0) {
1734 talloc_free(trie); // @todo - there may be multiple nodes here?
1735 return -1;
1736 }
1737
1738 trie_check((fr_trie_t *) comp, key, start_bit, end_bit, data, __LINE__);
1739
1740 VERIFY(comp);
1741 return 0;
1742 }
1743
1744 /*
1745 * All edges are used. Create an N-way node.
1746 */
1747
1748 /*
1749 * @todo - limit bits by calling
1750 * trie_comp_split()?
1751 */
1752 bits = comp->bits;
1753
1754 MPRINT3("%.*scomp swapping to node bits %d at %d\n", start_bit, spaces, bits, __LINE__);
1755
1756 node = trie_node_alloc(ctx, bits);
1757 if (!node) return -1;
1758
1759 for (i = 0; i < comp->used; i++) {
1760 fr_cond_assert(node->trie[comp->index[i]] == NULL);
1761 node->trie[comp->index[i]] = comp->trie[i];
1762 }
1763 node->used = comp->used;
1764 node->used += (node->trie[chunk] == NULL); /* will get set if the recursive insert succeeds */
1765
1766 /*
1767 * Insert the new chunk, which may or may not overlap
1768 * with an existing one.
1769 */
1770 MPRINT3("%.*srecurse at %d\n", start_bit, spaces, __LINE__);
1771 if (trie_key_insert(ctx, &node->trie[chunk], key, start_bit + node->bits, end_bit, data) < 0) {
1772 MPRINT3("%.*scomp failed recursing at %d", start_bit, spaces, __LINE__);
1773 talloc_free(node);
1774 return -1;
1775 }
1776
1777 trie_check((fr_trie_t *) node, key, start_bit, end_bit, data, __LINE__);
1778
1779 MPRINT3("%.*scomp returning at %d", start_bit, spaces, __LINE__);
1780
1782 *trie_p = (fr_trie_t *) node;
1783 VERIFY(node);
1784 return 0;
1785}
1786#endif
1787
1791#ifdef WITH_PATH_COMPRESSION
1793#endif
1794#ifdef WITH_NODE_COMPRESSION
1795 [ FR_TRIE_COMP ] = trie_comp_insert,
1796#endif
1797};
1798
1799/** Insert a binary key into the trie
1800 *
1801 * The key must have at least ((start_bit + keylen) >> 3) bytes
1802 *
1803 * @param ctx the talloc ctx
1804 * @param[in,out] trie_p the trie where things are inserted
1805 * @param key the binary key
1806 * @param start_bit the start bit
1807 * @param end_bit the end bit
1808 * @param data user data to insert
1809 * @return
1810 * - <0 on error
1811 * - 0 on success
1812 */
1813static int trie_key_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
1814{
1815 fr_trie_t *trie = *trie_p;
1816
1817 /*
1818 * We've reached the end of the trie, but may still have
1819 * key bits to insert.
1820 */
1821 if (!trie) {
1822 *trie_p = trie_key_alloc(ctx, key, start_bit, end_bit, data);
1823 if (!*trie_p) return -1;
1824 return 0;
1825 }
1826
1827 MPRINT3("%.*sIN recurse at %d\n", start_bit, spaces, __LINE__);
1828 trie_sprint(trie, key, start_bit, __LINE__);
1829
1830 /*
1831 * We've reached the end of the key. Insert a user node
1832 * here, and push the remaining bits of the trie to after
1833 * the user node.
1834 */
1835 if (start_bit == end_bit) {
1836 fr_trie_user_t *user;
1837
1838 if (trie->type == FR_TRIE_USER) {
1839 fr_strerror_printf("already has a user node at %d\n", __LINE__);
1840 return -1;
1841 }
1842
1843 user = fr_trie_user_alloc(ctx, data);
1844 if (!user) return -1;
1845
1846 user->trie = trie;
1847 *trie_p = (fr_trie_t *) user;
1848 return 0;
1849 }
1850
1851 TRIE_TYPE_CHECK(insert, -1);
1852
1853#ifndef TESTING
1854 return trie_insert_table[trie->type](ctx, trie_p, key, start_bit, end_bit, data);
1855#else
1856 MPRINT3("%.*srecurse at start %d end %d with data %s\n", start_bit, spaces, start_bit, end_bit, (char *) data);
1857
1858 if (trie_insert_table[trie->type](ctx, trie_p, key, start_bit, end_bit, data) < 0) {
1859 return -1;
1860 }
1861
1862 trie_check(*trie_p, key, start_bit, end_bit, data, __LINE__);
1863
1864 return 0;
1865#endif
1866}
1867
1868/** Insert a key and user ctx into a trie
1869 *
1870 * @param ft the trie
1871 * @param key the key
1872 * @param keylen key length in bits
1873 * @param data user ctx information to associated with the key
1874 * @return
1875 * - <0 on error
1876 * - 0 on success
1877 */
1878int fr_trie_insert_by_key(fr_trie_t *ft, void const *key, size_t keylen, void const *data)
1879{
1880 void *my_data;
1881 fr_trie_user_t *user;
1882
1883 if (keylen > MAX_KEY_BITS) {
1884 fr_strerror_printf("keylen too long (%u > %d)", (unsigned int) keylen, MAX_KEY_BITS);
1885 return -1;
1886 }
1887
1888 user = (fr_trie_user_t *) ft;
1889
1890 /*
1891 * Do a lookup before insertion. If we tried to insert
1892 * the key with new nodes and then discovered a conflict,
1893 * we would not be able to undo the process. This check
1894 * ensures that the insertion can modify the trie in
1895 * place without worry.
1896 */
1897 if (trie_key_match(user->trie, key, 0, keylen, true) != NULL) {
1898 fr_strerror_const("Cannot insert due to pre-existing key");
1899 return -1;
1900 }
1901
1902 my_data = UNCONST(void *, data);
1903 MPRINT2("No match for data, inserting...\n");
1904
1905 MPRINT3("%.*srecurse STARTS at %d with %.*s=%s\n", 0, spaces, __LINE__,
1906 (int) keylen, key, my_data);
1907 return trie_key_insert(user->data, &user->trie, key, 0, keylen, my_data);
1908}
1909
1910/* REMOVE FUNCTIONS */
1911static void *trie_key_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit);
1912
1913typedef void *(*trie_key_remove_t)(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit);
1914
1915static void *trie_user_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
1916{
1917 fr_trie_t *trie = *trie_p;
1918 fr_trie_user_t *user = (fr_trie_user_t *) trie;
1919
1920 /*
1921 * We're at the end of the key, return the data
1922 * given here, and free the node that we're
1923 * removing.
1924 */
1925 if (start_bit == end_bit) {
1926 void *data = user->data;
1927
1928 *trie_p = user->trie;
1929 talloc_free(user);
1930
1931 return data;
1932 }
1933
1934 return trie_key_remove(ctx, &user->trie, key, start_bit, end_bit);
1935}
1936
1937static void *trie_node_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
1938{
1939 fr_trie_t *trie = *trie_p;
1940 fr_trie_node_t *node = (fr_trie_node_t *) trie;
1941 uint32_t chunk;
1942 void *data;
1943
1944 chunk = get_chunk(key, start_bit, node->bits);
1945 if (!node->trie[chunk]) return NULL;
1946
1947 data = trie_key_remove(ctx, &node->trie[chunk], key, start_bit + node->bits, end_bit);
1948 if (!data) return NULL;
1949
1950 /*
1951 * The trie still has a subtrie. Just return the data.
1952 */
1953 if (node->trie[chunk]) return data;
1954
1955 /*
1956 * One less used edge.
1957 */
1958 node->used--;
1959 if (node->used > 0) return data;
1960
1961 /*
1962 * @todo - if we have path compression, and
1963 * node->used==1, then create a fr_trie_path_t from the
1964 * chunk, and concatenate it (if necessary) to any
1965 * trailing path compression node.
1966 */
1967
1968 /*
1969 * Our entire node is empty. Delete it as we walk back up the trie.
1970 */
1971 *trie_p = NULL;
1972 talloc_free(node); /* no children */
1973 return data;
1974}
1975
1976#ifdef WITH_PATH_COMPRESSION
1977static void *trie_path_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
1978{
1979 fr_trie_t *trie = *trie_p;
1980 fr_trie_path_t *path = (fr_trie_path_t *) trie;
1981 uint32_t chunk;
1982 void *data;
1983
1984 chunk = get_chunk(key, start_bit, path->bits);
1985
1986 /*
1987 * No match, can't remove it.
1988 */
1989 if (path->chunk != chunk) return NULL;
1990
1991 data = trie_key_remove(ctx, &path->trie, key, start_bit + path->bits, end_bit);
1992 if (!data) return NULL;
1993
1994 /*
1995 * The trie still has a subtrie. Just return the data.
1996 */
1997 if (path->trie) return data;
1998
1999 /*
2000 * Our entire path is empty. Delete it as we walk back up the trie.
2001 */
2002 *trie_p = NULL;
2003 talloc_free(path); /* no children */
2004 return data;
2005}
2006#endif
2007
2008#ifdef WITH_NODE_COMPRESSION
2009static void *trie_comp_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
2010{
2011 int i, j;
2012 uint16_t chunk;
2013 void *data;
2014 fr_trie_comp_t *comp = *(fr_trie_comp_t **) trie_p;
2015 fr_trie_path_t *path;
2016
2017 chunk = get_chunk(key, start_bit, comp->bits);
2018
2019 MPRINT3("%.*sremove at %d\n", start_bit, spaces, __LINE__);
2020 trie_sprint(*trie_p, key, start_bit, __LINE__);
2021
2022 for (i = 0; i < comp->used; i++) {
2023 if (comp->index[i] < chunk) continue;
2024
2025 if (comp->index[i] == chunk) {
2026 break;
2027 }
2028
2029 /*
2030 * The edges are ordered smallest to largest. So
2031 * if the edge is larger than the chunk, NO edge
2032 * will match the chunk.
2033 */
2034 if (comp->index[i] > chunk) return NULL;
2035 }
2036
2037 /*
2038 * Didn't find it, fail.
2039 */
2040 if (i >= comp->used) return NULL;
2041
2042 fr_cond_assert(chunk == comp->index[i]);
2043
2044 data = trie_key_remove(ctx, &comp->trie[i], key, start_bit + comp->bits, end_bit);
2045 if (!data) return NULL;
2046
2047 /*
2048 * The trie still has a subtrie. Just return the data.
2049 */
2050 if (comp->trie[i]) {
2051 MPRINT3("%.*sremove at %d\n", start_bit, spaces, __LINE__);
2052 trie_sprint((fr_trie_t *) comp, key, start_bit, __LINE__);
2053 VERIFY(comp);
2054 return data;
2055 }
2056
2057 /*
2058 * Shrinking at the end is easy, we don't need to do
2059 * anything. For shrinking in the middle, we just copy
2060 * the entries down.
2061 */
2062 for (j = i; j < comp->used - 1; j++) {
2063 comp->index[j] = comp->index[j + 1];
2064 comp->trie[j] = comp->trie[j + 1];
2065 }
2066 comp->used--;
2067
2068 if (comp->used >= 2) {
2069 VERIFY(comp);
2070 return data;
2071 }
2072
2073 /*
2074 * Our entire path is empty. Delete it as we walk back
2075 * up the trie. We hope that this doesn't happen.
2076 */
2077 if (!comp->used) {
2078 *trie_p = NULL;
2079 talloc_free(comp); /* no children */
2080 MPRINT3("%.*sremove at %d\n", start_bit, spaces, __LINE__);
2081 return data;
2082 }
2083
2084 /*
2085 * Only one edge. Turn it back into a path node. Note
2086 * that we pass "key" here, which is wrong... that's the
2087 * key we're removing, not the key left in the node. But
2088 * we fix that later.
2089 *
2090 * @todo - check the child. If it's also a path node, try
2091 * to concatenate the nodes together.
2092 */
2093 path = fr_trie_path_alloc(ctx, key, start_bit, start_bit + comp->bits);
2094 if (!path) return data;
2095
2096 /*
2097 * Tie the new node in.
2098 */
2099 path->trie = comp->trie[0];
2100
2101 /*
2102 * Fix up the chunk and key to be the one left in the
2103 * trie, not the one which was removed.
2104 */
2105 path->chunk = comp->index[0];
2106 write_chunk(&path->key[0], start_bit & 0x07, path->bits, path->chunk);
2107
2108 *trie_p = (fr_trie_t *) path;
2110 VERIFY(path);
2111 return data;
2112}
2113#endif
2114
2118#ifdef WITH_PATH_COMPRESSION
2120#endif
2121#ifdef WITH_NODE_COMPRESSION
2122 [ FR_TRIE_COMP ] = trie_comp_remove,
2123#endif
2124};
2125
2126/** Remove a key from a trie, and return the user data.
2127 *
2128 */
2129static void *trie_key_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
2130{
2131 fr_trie_t *trie = *trie_p;
2132
2133 if (!trie) return NULL;
2134
2135 /*
2136 * We can't remove a key which is shorter than the
2137 * current trie.
2138 */
2139 if ((start_bit + trie->bits) > end_bit) return NULL;
2140
2141 TRIE_TYPE_CHECK(remove, NULL);
2142
2143 return trie_remove_table[trie->type](ctx, trie_p, key, start_bit, end_bit);
2144}
2145
2146/** Remove a key and return the associated user ctx
2147 *
2148 * The key must match EXACTLY. This is not a prefix match.
2149 *
2150 * @param ft the trie
2151 * @param key the key
2152 * @param keylen key length in bits
2153 * @return
2154 * - NULL on not found
2155 * - user ctx data on success
2156 */
2157void *fr_trie_remove_by_key(fr_trie_t *ft, void const *key, size_t keylen)
2158{
2159 fr_trie_user_t *user;
2160
2161 if (keylen > MAX_KEY_BITS) return NULL;
2162
2163 if (!ft->trie) return NULL;
2164
2165 user = (fr_trie_user_t *) ft;
2166
2167 /*
2168 * Remove the user trie, not ft->trie.
2169 */
2170 return trie_key_remove(user->data, &user->trie, key, 0, (int) keylen);
2171}
2172
2173/* WALK FUNCTIONS */
2174
2176
2177typedef int (*fr_trie_key_walk_t)(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more);
2178
2179static int trie_key_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more);
2180
2190
2191static int trie_user_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
2192{
2193 fr_trie_user_t *user = (fr_trie_user_t *) trie;
2194
2195 if (!user->trie) return 0;
2196
2197 return trie_key_walk(user->trie, cb, depth, more);
2198}
2199
2200static int trie_node_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
2201{
2202 int i, used;
2203 fr_trie_node_t *node = (fr_trie_node_t *) trie;
2204
2205 used = 0;
2206 for (i = 0; i < (1 << node->bits); i++) {
2207 if (!node->trie[i]) continue;
2208
2209 write_chunk(cb->start, depth, node->bits, (uint16_t) i);
2210 used++;
2211
2212 if (trie_key_walk(node->trie[i], cb, depth + node->bits,
2213 more || (used < node->used)) < 0) {
2214 return -1;
2215 }
2216 }
2217
2218 return 0;
2219}
2220
2221#ifdef WITH_PATH_COMPRESSION
2222static int trie_path_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
2223{
2224 fr_trie_path_t *path = (fr_trie_path_t *) trie;
2225
2226 write_chunk(cb->start, depth, path->bits, path->chunk);
2227
2228 fr_cond_assert(path->trie != NULL);
2229 return trie_key_walk(path->trie, cb, depth + path->bits, more);
2230}
2231#endif
2232
2233#ifdef WITH_NODE_COMPRESSION
2234static int trie_comp_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
2235{
2236 int i, used;
2237 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
2238
2239 used = 0;
2240 for (i = 0; i < comp->used; i++) {
2241 write_chunk(cb->start, depth, comp->bits, comp->index[i]);
2242
2243 fr_cond_assert(comp->trie[i] != NULL);
2244
2245 used++;
2246 if (trie_key_walk(comp->trie[i], cb, depth + comp->bits,
2247 more || (used < comp->used)) < 0) {
2248 return -1;
2249 }
2250 }
2251
2252 return 0;
2253}
2254#endif
2255
2259#ifdef WITH_PATH_COMPRESSION
2261#endif
2262#ifdef WITH_NODE_COMPRESSION
2263 [ FR_TRIE_COMP ] = trie_comp_walk,
2264#endif
2265};
2266
2267static int trie_key_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
2268{
2269 /*
2270 * Do the callback before anything else.
2271 */
2272 if (cb->callback(trie, cb, depth, more) < 0) return -1;
2273
2274 if (!trie) {
2275 fr_cond_assert(depth == 0);
2276 return 0;
2277 }
2278
2279 TRIE_TYPE_CHECK(walk, -1);
2280
2281 /*
2282 * No more buffer space, stop.
2283 */
2284 if ((cb->start + BYTEOF(depth + trie->bits + 8)) >= cb->end) return 0;
2285
2286 return trie_walk_table[trie->type](trie, cb, depth, more);
2287}
2288
2289#ifdef WITH_TRIE_VERIFY
2290/* VERIFY FUNCTIONS */
2291
2292typedef int (*trie_verify_t)(fr_trie_t *trie);
2293
2294static int trie_user_verify(fr_trie_t *trie)
2295{
2296 fr_trie_user_t *user = (fr_trie_user_t *) trie;
2297
2298 if (!user->data) {
2299 fr_strerror_const("user node has no user data");
2300 return -1;
2301 }
2302
2303 if (!user->trie) return 0;
2304
2305 return trie_verify(user->trie);
2306}
2307
2308static int trie_node_verify(fr_trie_t *trie)
2309{
2310 fr_trie_node_t *node = (fr_trie_node_t *) trie;
2311 int i, used;
2312
2313 if ((node->bits == 0) || (node->bits > MAX_NODE_BITS)) {
2314 fr_strerror_printf("N-way node has invalid bits %d",
2315 node->bits);
2316 return -1;
2317 }
2318
2319 if ((node->used == 0) || (node->used > (1 << node->bits))) {
2320 fr_strerror_printf("N-way node has invalid used %d for bits %d",
2321 node->used, node->bits);
2322 return -1;
2323 }
2324
2325 used = 0;
2326 for (i = 0; i < (1 << node->bits); i++) {
2327 if (!node->trie[i]) continue;
2328
2329 if (trie_verify(node->trie[i]) < 0) return -1;
2330
2331 used++;
2332 }
2333
2334 if (used != node->used) {
2335 fr_strerror_printf("N-way node has incorrect used %d when actually used %d",
2336 node->used, used);
2337 return -1;
2338 }
2339
2340 return 0;
2341}
2342
2343#ifdef WITH_PATH_COMPRESSION
2344static int trie_path_verify(fr_trie_t *trie)
2345{
2346 fr_trie_path_t *path = (fr_trie_path_t *) trie;
2347
2348 if ((path->bits == 0) || (path->bits > 16)) {
2349 fr_strerror_printf("path node has invalid bits %d",
2350 path->bits);
2351 return -1;
2352 }
2353
2354 if (!path->trie) {
2355 fr_strerror_const("path node has no child trie");
2356 return -1;
2357 }
2358
2359 return trie_verify(path->trie);
2360}
2361#endif
2362
2363#ifdef WITH_NODE_COMPRESSION
2364static int trie_comp_verify(fr_trie_t *trie)
2365{
2366 int i, used;
2367 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
2368
2369 if ((comp->bits == 0) || (comp->bits > MAX_COMP_BITS)) {
2370 fr_strerror_printf("comp node has invalid bits %d",
2371 comp->bits);
2372 return -1;
2373 }
2374
2375 used = 0;
2376 for (i = 0; i < comp->used; i++) {
2377 if (!comp->trie[i]) {
2378 fr_strerror_printf("comp node has no child trie at %d", i);
2379 return -1;
2380 }
2381
2382 if ((i + 1) < comp->used) {
2383 if (comp->index[i] >= comp->index[i + 1]) {
2384 fr_strerror_printf("comp node has inverted edges at %d (%04x >= %04x)",
2385 i, comp->index[i], comp->index[i + 1]);
2386 return -1;
2387 }
2388 }
2389
2390 if (trie_verify(comp->trie[i]) < 0) return -1;
2391 used++;
2392 }
2393
2394 if (used != comp->used) {
2395 fr_strerror_printf("Compressed node has incorrect used %d when actually used %d",
2396 comp->used, used);
2397 return -1;
2398 }
2399
2400 return 0;
2401}
2402#endif
2403
2404static trie_verify_t trie_verify_table[FR_TRIE_MAX] = {
2405 [ FR_TRIE_USER ] = trie_user_verify,
2406 [ FR_TRIE_NODE ] = trie_node_verify,
2407#ifdef WITH_PATH_COMPRESSION
2408 [ FR_TRIE_PATH ] = trie_path_verify,
2409#endif
2410#ifdef WITH_NODE_COMPRESSION
2411 [ FR_TRIE_COMP ] = trie_comp_verify,
2412#endif
2413};
2414
2415
2416/** Verify the trie nodes
2417 *
2418 */
2419static int trie_verify(fr_trie_t *trie)
2420{
2421 if (!trie) return 0;
2422
2423 TRIE_TYPE_CHECK(verify, -1);
2424
2425 return trie_verify_table[trie->type](trie);
2426}
2427#endif /* WITH_TRIE_VERIFY */
2428
2429/* MISCELLANEOUS FUNCTIONS */
2430
2431#ifdef TESTING
2432/** Dump a trie edge in canonical form.
2433 *
2434 */
2435static void trie_dump_edge(FILE *fp, fr_trie_t *trie)
2436{
2437 fr_cond_assert(trie != NULL);
2438
2439 fprintf(fp, "NODE-%d\n", trie->number);
2440 return;
2441}
2442
2443
2444typedef void (*fr_trie_dump_t)(FILE *fp, fr_trie_t *trie, char const *key, int keylen);
2445
2446static void trie_user_dump(FILE *fp, fr_trie_t *trie, char const *key, int keylen)
2447{
2448 fr_trie_user_t *user = (fr_trie_user_t *) trie;
2449 int bytes = BYTES(keylen);
2450
2451 fprintf(fp, "{ NODE-%d\n", user->number);
2452 fprintf(fp, "\ttype\tUSER\n");
2453 fprintf(fp, "\tinput\t{%d}%.*s\n", keylen, bytes, key);
2454
2455 fprintf(fp, "\tdata\t\"%s\"\n", (char const *) user->data);
2456 if (!user->trie) {
2457 fprintf(fp, "}\n\n");
2458 return;
2459 }
2460
2461 fprintf(fp, "\tnext\t");
2462 trie_dump_edge(fp, user->trie);
2463 fprintf(fp, "}\n\n");
2464}
2465
2466static void trie_node_dump(FILE *fp, fr_trie_t *trie, char const *key, int keylen)
2467{
2468 fr_trie_node_t *node = (fr_trie_node_t *) trie;
2469 int i;
2470 int bytes = BYTES(keylen);
2471
2472 fprintf(fp, "{ NODE-%d\n", node->number);
2473 fprintf(fp, "\ttype\tNODE\n");
2474 fprintf(fp, "\tinput\t{%d}%.*s\n", keylen, bytes, key);
2475
2476 fprintf(fp, "\tbits\t%d\n", node->bits);
2477 fprintf(fp, "\tused\t%d\n", node->used);
2478
2479 for (i = 0; i < (1 << node->bits); i++) {
2480 if (!node->trie[i]) continue;
2481
2482 fprintf(fp, "\t%02x\t", (int) i);
2483 trie_dump_edge(fp, node->trie[i]);
2484 }
2485 fprintf(fp, "}\n\n");
2486}
2487
2488#ifdef WITH_PATH_COMPRESSION
2489static void trie_path_dump(FILE *fp, fr_trie_t *trie, char const *key, int keylen)
2490{
2491 fr_trie_path_t *path = (fr_trie_path_t *) trie;
2492 int bytes = BYTES(keylen);
2493
2494 fprintf(fp, "{ NODE-%d\n", path->number);
2495 fprintf(fp, "\ttype\tPATH\n");
2496 fprintf(fp, "\tinput\t{%d}%.*s\n", keylen, bytes, key);
2497
2498 fprintf(fp, "\tbits\t%d\n", (int) path->bits);
2499 fprintf(fp, "\tpath\t");
2500
2501 fprintf(fp, "%02x %02x", path->key[0], path->key[1]);
2502
2503 fprintf(fp, "\n");
2504
2505 fprintf(fp, "\tnext\t");
2506 trie_dump_edge(fp, path->trie);
2507
2508 fprintf(fp, "}\n\n");
2509}
2510#endif
2511
2512#ifdef WITH_NODE_COMPRESSION
2513static void trie_comp_dump(FILE *fp, fr_trie_t *trie, char const *key, int keylen)
2514{
2515 fr_trie_comp_t *comp = (fr_trie_comp_t *) trie;
2516 int i;
2517 int bytes = BYTES(keylen);
2518
2519 fprintf(fp, "{ NODE-%d\n", comp->number);
2520 fprintf(fp, "\ttype\tCOMP\n");
2521 fprintf(fp, "\tinput\t{%d}%.*s\n", keylen, bytes, key);
2522
2523 fprintf(fp, "\tbits\t%d\n", comp->bits);
2524 fprintf(fp, "\tused\t%d\n", comp->used);
2525
2526 for (i = 0; i < comp->used; i++) {
2527 fprintf(fp, "\t%d = %02x\t", i, comp->index[i]);
2528 trie_dump_edge(fp, comp->trie[i]);
2529 }
2530 fprintf(fp, "}\n\n");
2531}
2532
2533#endif
2534
2535static fr_trie_dump_t trie_dump_table[FR_TRIE_MAX] = {
2536 [ FR_TRIE_USER ] = trie_user_dump,
2537 [ FR_TRIE_NODE ] = trie_node_dump,
2538#ifdef WITH_PATH_COMPRESSION
2539 [ FR_TRIE_PATH ] = trie_path_dump,
2540#endif
2541#ifdef WITH_NODE_COMPRESSION
2542 [ FR_TRIE_COMP ] = trie_comp_dump,
2543#endif
2544};
2545
2546
2547/** Dump the trie nodes
2548 *
2549 */
2550static int _trie_dump_cb(fr_trie_t *trie, fr_trie_callback_t *cb, int keylen, UNUSED bool more)
2551{
2552 FILE *fp = cb->ctx;
2553
2554 if (!trie) return 0;
2555
2556 TRIE_TYPE_CHECK(dump, -1);
2557
2558 trie_dump_table[trie->type](fp, trie, (char const *) cb->start, keylen);
2559 return 0;
2560}
2561
2562/** Print the strings accepted by a trie to a file
2563 *
2564 */
2565static int _trie_print_cb(fr_trie_t *trie, fr_trie_callback_t *cb, int keylen, UNUSED bool more)
2566{
2567 int bytes;
2568 FILE *fp = cb->ctx;
2569 fr_trie_user_t *user;
2570
2571 if (!trie || (trie->type != FR_TRIE_USER)) {
2572 return 0;
2573 }
2574
2575 bytes = BYTES(keylen);
2576 user = (fr_trie_user_t *) trie;
2577
2578 if ((keylen & 0x07) != 0) {
2579 fprintf(fp, "{%d}%.*s\t%s\n", keylen, bytes, cb->start, (char const *) user->data);
2580 } else {
2581 fprintf(fp, "%.*s\t%s\n", bytes, cb->start, (char const *) user->data);
2582 }
2583
2584 return 0;
2585}
2586#endif /* TESTING */
2587
2588
2589/** Implement the user-visible side of the walk callback.
2590 *
2591 */
2592static int _trie_user_cb(fr_trie_t *trie, fr_trie_callback_t *cb, int keylen, UNUSED bool more)
2593{
2594 fr_trie_user_t *user;
2595
2596 if (!trie || (trie->type != FR_TRIE_USER)) return 0;
2597
2598 user = (fr_trie_user_t *) trie;
2599
2600 /*
2601 * Call the user function with the key, key length, and data.
2602 */
2603 if (cb->user_callback(cb->start, keylen, UNCONST(void *, user->data), cb->ctx) < 0) {
2604 return -1;
2605 }
2606
2607 return 0;
2608}
2609
2610int fr_trie_walk(fr_trie_t *ft, void *ctx, fr_trie_walk_t callback)
2611{
2613 fr_trie_callback_t my_cb = {
2614 .start = buffer,
2615 .end = buffer + sizeof(buffer),
2616 .callback = _trie_user_cb,
2617 .user_callback = callback,
2618 .ctx = ctx
2619 };
2620
2621 memset(buffer, 0, sizeof(buffer));
2622
2623 /*
2624 * Call the internal walk function to do the work.
2625 */
2626 return trie_key_walk(ft->trie, &my_cb, 0, false);
2627}
2628
2629
2630/**********************************************************************/
2631
2632/*
2633 * Object API.
2634 */
2635
2636/** Find an element in the trie, returning the data.
2637 *
2638 * @param[in] ft to search in.
2639 * @param[in] data to find.
2640 * @return
2641 * - User data matching the data passed in.
2642 * - NULL if nothing matched passed data.
2643 */
2644CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2645void *fr_trie_find(fr_trie_t *ft, void const *data)
2646{
2647 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2648 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2649 uint8_t *key;
2650 size_t keylen;
2651
2652 key = &uctx->buffer[0];
2653 keylen = sizeof(uctx->buffer) * 8;
2654
2655 if (!uctx->get_key) return NULL;
2656
2657 if (uctx->get_key(&key, &keylen, data) < 0) return NULL;
2658
2659 return fr_trie_lookup_by_key(ft, key, keylen);
2660}
2661
2662/** Match an element exactly in the trie, returning the data.
2663 *
2664 * @param[in] ft to search in.
2665 * @param[in] data to find.
2666 * @return
2667 * - User data matching the data passed in.
2668 * - NULL if nothing matched passed data.
2669 */
2670CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2671void *fr_trie_match(fr_trie_t *ft, void const *data)
2672{
2673 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2674 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2675 uint8_t *key;
2676 size_t keylen;
2677
2678 key = &uctx->buffer[0];
2679 keylen = sizeof(uctx->buffer) * 8;
2680
2681 if (!uctx->get_key) return NULL;
2682
2683 if (uctx->get_key(&key, &keylen, data) < 0) return NULL;
2684
2685 return fr_trie_match_by_key(ft, key, keylen);
2686}
2687
2688/** Insert data into a trie
2689 *
2690 * @param[in] ft to insert data into.
2691 * @param[in] data to insert.
2692 * @return
2693 * - true if data was inserted.
2694 * - false if data already existed and was not inserted.
2695 */
2696CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2698{
2699 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2700 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2701 uint8_t *key;
2702 size_t keylen;
2703
2704 key = &uctx->buffer[0];
2705 keylen = sizeof(uctx->buffer) * 8;
2706
2707 if (!uctx->get_key) return false;
2708
2709 if (uctx->get_key(&key, &keylen, data) < 0) return false;
2710
2711 if (fr_trie_insert_by_key(ft, key, keylen, data) < 0) return false;
2712
2713 return true;
2714}
2715
2716/** Replace old data with new data, OR insert if there is no old
2717 *
2718 * @param[out] old data that was replaced. If this argument
2719 * is not NULL, then the old data will not
2720 * be freed, even if a free function is
2721 * configured.
2722 * @param[in] ft to insert data into.
2723 * @param[in] data to replace.
2724 * @return
2725 * - 1 if data was replaced.
2726 * - 0 if data was inserted.
2727 * - -1 if we failed to replace data
2728 */
2729CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2730int fr_trie_replace(void **old, fr_trie_t *ft, void const *data)
2731{
2732 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2733 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2734 uint8_t *key;
2735 size_t keylen;
2736 void *found;
2737
2738 key = &uctx->buffer[0];
2739 keylen = sizeof(uctx->buffer) * 8;
2740
2741 if (!uctx->get_key) return -1;
2742
2743 if (uctx->get_key(&key, &keylen, data) < 0) return -1;
2744
2745 found = trie_key_match(ft, key, 0, keylen, true); /* do exact match */
2746 if (found) {
2747 if (old) *old = found;
2748 if (fr_trie_remove_by_key(ft, key, keylen) != found) return -1;
2749 } else {
2750 if (old) *old = NULL;
2751 }
2752
2753 /*
2754 * Insert the new key.
2755 */
2756 if (fr_trie_insert_by_key(ft, key, keylen, data) < 0) return -1;
2757
2758 return found ? 1 : 0;
2759}
2760
2761/** Remove an entry, without freeing the data
2762 *
2763 * @param[in] ft to remove data from.
2764 * @param[in] data to remove.
2765 * @return
2766 * - The user data we removed.
2767 * - NULL if we couldn't find any matching data.
2768 */
2769CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2770void *fr_trie_remove(fr_trie_t *ft, void const *data)
2771{
2772 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2773 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2774 uint8_t *key;
2775 size_t keylen;
2776
2777 key = &uctx->buffer[0];
2778 keylen = sizeof(uctx->buffer) * 8;
2779
2780 if (!uctx->get_key) return NULL;
2781
2782 if (uctx->get_key(&key, &keylen, data) < 0) return NULL;
2783
2784 return fr_trie_remove_by_key(ft, key, keylen);
2785}
2786
2787/** Remove node and free data (if a free function was specified)
2788 *
2789 * @param[in] ft to remove data from.
2790 * @param[in] data to remove/free.
2791 * @return
2792 * - true if we removed data.
2793 * - false if we couldn't find any matching data.
2794 */
2795CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2797{
2798 fr_trie_user_t *user = (fr_trie_user_t *) ft;
2799 fr_trie_ctx_t *uctx = talloc_get_type_abort(user->data, fr_trie_ctx_t);
2800 uint8_t *key;
2801 size_t keylen;
2802 void *found;
2803
2804 key = &uctx->buffer[0];
2805 keylen = sizeof(uctx->buffer) * 8;
2806
2807 if (!uctx->get_key) return false;
2808
2809 if (uctx->get_key(&key, &keylen, data) < 0) return false;
2810
2811 found = fr_trie_remove_by_key(ft, key, keylen);
2812 if (!found) return false;
2813
2814 if (!uctx->free_data) return true;
2815
2816 uctx->free_data(found);
2817 return true;
2818}
2819
2820/** Return how many nodes there are in a trie
2821 *
2822 * @param[in] ft to return node count for.
2823 */
2824CC_NO_UBSAN(function) /* UBSAN: false positive - htrie call with first argument of void * trips --fsanitize=function */
2826{
2827 return 0;
2828}
2829
2830#ifdef TESTING
2831static bool print_lineno = false;
2832
2833typedef struct {
2834 char *start;
2835 char *buffer;
2836 size_t buflen;
2837} trie_sprint_ctx_t;
2838
2839
2840/** Print the strings accepted by a trie to one line
2841 */
2842static int _trie_sprint_cb(fr_trie_t *trie, fr_trie_callback_t *cb, int keylen, bool more)
2843{
2844 int bytes, len;
2845 trie_sprint_ctx_t *ctx;
2846 fr_trie_user_t *user;
2847
2848 ctx = cb->ctx;
2849
2850 if (!trie) {
2851 len = snprintf(ctx->buffer, ctx->buflen, "{}");
2852 goto done;
2853 }
2854
2855 if (trie->type != FR_TRIE_USER) return 0;
2856
2857 bytes = BYTES(keylen);
2858 user = (fr_trie_user_t *) trie;
2859
2860 if (!user->trie && !more) {
2861 len = snprintf(ctx->buffer, ctx->buflen, "%.*s=%s",
2862 bytes, cb->start, (char const *) user->data);
2863 } else {
2864 len = snprintf(ctx->buffer, ctx->buflen, "%.*s=%s,",
2865 bytes, cb->start, (char const *) user->data);
2866 }
2867
2868done:
2869 ctx->buffer += len;
2870 ctx->buflen -= len;
2871
2872 return 0;
2873}
2874
2875
2876static void trie_sprint(fr_trie_t *trie, uint8_t const *key, int start_bit, UNUSED int lineno)
2877{
2878 fr_trie_callback_t my_cb;
2879 trie_sprint_ctx_t my_sprint;
2881 char out[8192];
2882
2883 /*
2884 * Initialize the buffer
2885 */
2886 memset(buffer, 0, sizeof(buffer));
2887 memset(out, 0, sizeof(out));
2888 if (key) {
2889 memcpy(buffer, key, BYTES(start_bit) + 1);
2890 }
2891
2892 /*
2893 * Where the output data goes.
2894 */
2895 my_sprint.start = out;
2896 my_sprint.buffer = out;
2897 my_sprint.buflen = sizeof(out);
2898
2899 /*
2900 * Where the keys are built.
2901 */
2902 my_cb.start = buffer;
2903 my_cb.end = buffer + sizeof(buffer);
2904 my_cb.callback = _trie_sprint_cb;
2905 my_cb.user_callback = NULL;
2906 my_cb.ctx = &my_sprint;
2907
2908 /*
2909 * Call the internal walk function to do the work.
2910 */
2911 (void) trie_key_walk(trie, &my_cb, start_bit, false);
2912
2913 MPRINT3("%.*s%s at %d\n", start_bit, spaces, out, lineno);
2914}
2915
2916
2917/** Parse a string into bits + key
2918 *
2919 * The format is one of:
2920 *
2921 * - string such as "abcdef"
2922 * - string prefixed with a bit length, {4}a
2923 */
2924static int arg2key(char *arg, char **key, int *length)
2925{
2926 char *p;
2927 int bits, size;
2928
2929 if (*arg != '{') {
2930 *key = arg;
2931 *length = BITSOF(strlen(arg));
2932 return 0;
2933 }
2934
2935 p = strchr(arg, '}');
2936 if (!p) {
2937 MPRINT("Failed to find end '}' for {bits}\n");
2938 return -1;
2939 }
2940
2941 bits = BITSOF(strlen(p + 1));
2942 if (!bits) {
2943 MPRINT("No key found in in '%s'\n", arg);
2944 return -1;
2945 }
2946
2947 size = atoi(arg + 1); /* ignore end character... */
2948 if (size > bits) {
2949 MPRINT("Length '%d' is longer than bits in key %s",
2950 size, p + 1);
2951 }
2952
2953 *key = p + 1;
2954 *length = size;
2955
2956 return 0;
2957}
2958
2959/** Our TALLOC_CTX for the data we put into the trie.
2960 *
2961 * Most people don't need to do this, they can just insert their own
2962 * data.
2963 */
2964static void *data_ctx = NULL;
2965
2966/** Insert a key + data into a trie.
2967 *
2968 */
2969static int command_insert(fr_trie_t *ft, UNUSED int argc, char **argv, UNUSED char *out, UNUSED size_t outlen)
2970{
2971 int bits;
2972 void *data;
2973 char *key;
2974
2975 if (arg2key(argv[0], &key, &bits) < 0) {
2976 return -1;
2977 }
2978
2979 /*
2980 * This has to stick around in between command
2981 * invocations.
2982 */
2983 data = talloc_strdup(data_ctx, argv[1]);
2984 if (!data) {
2985 MPRINT("OOM\n");
2986 return -1;
2987 }
2988
2989 if (fr_trie_insert_by_key(ft, key, bits, data) < 0) {
2990 MPRINT("Failed inserting key %s=%s - %s\n", key, argv[1], fr_strerror());
2991 return -1;
2992 }
2993
2994 return 0;
2995}
2996
2997/** Verify a trie recursively
2998 *
2999 * For sanity reasons, this command runs but doesn't do anything if
3000 * the code is built with no trie verification.
3001 */
3002static int command_verify(fr_trie_t *ft, UNUSED int argc, UNUSED char **argv, UNUSED char *out, UNUSED size_t outlen)
3003{
3004 fr_cond_assert(ft != NULL);
3005
3006 /*
3007 * The top-level node may have a NULL talloc ctx, which
3008 * is OK. So we skip that.
3009 */
3010 if (ft->type != FR_TRIE_USER) {
3011 fprintf(stderr, "Verify failed: trie is malformed\n");
3012 return -1;
3013 }
3014
3015 if (trie_verify(ft->trie) < 0) {
3016 fprintf(stderr, "Verify failed: %s\n", fr_strerror());
3017 return -1;
3018 }
3019
3020 return 0;
3021}
3022
3023/** Print the keys accepted by a trie
3024 *
3025 * The strings are printed to stdout.
3026 *
3027 * @todo - allow printing to a file.
3028 */
3029static int command_keys(fr_trie_t *ft, UNUSED int argc, UNUSED char **argv, char *out, size_t outlen)
3030{
3031 fr_trie_callback_t my_cb;
3032
3033 my_cb.start = (uint8_t *) out;
3034 my_cb.end = (uint8_t *) (out + outlen);
3035 my_cb.callback = _trie_print_cb;
3036 my_cb.user_callback = NULL;
3037 my_cb.ctx = stdout;
3038
3039 /*
3040 * Call the internal walk function to do the work.
3041 */
3042 return trie_key_walk(ft->trie, &my_cb, 0, false);
3043}
3044
3045
3046/** Dump the trie in internal format
3047 *
3048 * The information is printed to stdout.
3049 *
3050 * For sanity reasons, this command runs but doesn't do anything if
3051 * the code is built with no trie dumping.
3052 *
3053 * @todo - allow printing to a file.
3054 */
3055static int command_dump(fr_trie_t *ft, UNUSED int argc, UNUSED char **argv, char *out, size_t outlen)
3056{
3057 fr_trie_callback_t my_cb;
3058
3059 my_cb.start = (uint8_t *) out;
3060 my_cb.end = (uint8_t *) (out + outlen);
3061 my_cb.callback = _trie_dump_cb;
3062 my_cb.user_callback = NULL;
3063 my_cb.ctx = stdout;
3064
3065 /*
3066 * Call the internal walk function to do the work.
3067 */
3068 return trie_key_walk(ft->trie, &my_cb, 0, false);
3069}
3070
3071
3072/** Clear the entire trie without caring what's in it.
3073 *
3074 */
3075static int command_clear(fr_trie_t *ft, UNUSED int argc, UNUSED char **argv, UNUSED char *out, UNUSED size_t outlen)
3076{
3077 if (!ft->trie) return 0;
3078
3079 trie_free(ft->trie);
3080 ft->trie = NULL;
3081
3082 /*
3083 * Clean up our internal data ctx, too.
3084 */
3085 talloc_free(data_ctx);
3086 data_ctx = talloc_init_const("data_ctx");
3087
3088 return 0;
3089}
3090
3091
3092/** Turn on line number debugging.
3093 *
3094 * @todo - add general "debug" functionality.
3095 */
3096static int command_lineno(UNUSED fr_trie_t *ft, UNUSED int argc, char **argv, UNUSED char *out, UNUSED size_t outlen)
3097{
3098 if (strcmp(argv[0], "true") == 0) {
3099 print_lineno = true;
3100 } else {
3101 print_lineno = false;
3102 }
3103
3104 return 0;
3105}
3106
3107
3108/** Match an exact key + length
3109 *
3110 * Normally, the "lookup" returns the longest prefix match, so that
3111 * *long* key lookups can return *short* matches.
3112 *
3113 * In some cases, we want to know if an exact key is in the trie.
3114 * For those cases, we use this function.
3115 */
3116static int command_match(fr_trie_t *ft, UNUSED int argc, char **argv, char *out, size_t outlen)
3117{
3118 int bits;
3119 void *answer;
3120 char *key;
3121
3122 if (arg2key(argv[0], &key, &bits) < 0) {
3123 return -1;
3124 }
3125
3126 answer = trie_key_match(ft->trie, (uint8_t *) key, 0, bits, true);
3127 if (!answer) {
3128 strlcpy(out, "{}", outlen);
3129 return 0;
3130 }
3131
3132 strlcpy(out, answer, outlen);
3133
3134 return 0;
3135}
3136
3137
3138/** Look up a key and return user ctx data.
3139 *
3140 * This is done by longest prefix match, not exact match.
3141 */
3142static int command_lookup(fr_trie_t *ft, UNUSED int argc, char **argv, char *out, size_t outlen)
3143{
3144 int bits;
3145 void *answer;
3146 char *key;
3147
3148 if (arg2key(argv[0], &key, &bits) < 0) {
3149 return -1;
3150 }
3151
3152 answer = fr_trie_lookup_by_key(ft, key, bits);
3153 if (!answer) {
3154 strlcpy(out, "{}", outlen);
3155 return 0;
3156 }
3157
3158 strlcpy(out, answer, outlen);
3159
3160 return 0;
3161}
3162
3163
3164/** Remove a key from the trie.
3165 *
3166 * The key has to match exactly.
3167 */
3168static int command_remove(fr_trie_t *ft, UNUSED int argc, char **argv, char *out, size_t outlen)
3169{
3170 int bits;
3171 void *answer;
3172 char *key;
3173
3174 if (arg2key(argv[0], &key, &bits) < 0) {
3175 return -1;
3176 }
3177
3178 answer = fr_trie_remove_by_key(ft, key, bits);
3179 if (!answer) {
3180 MPRINT("Could not remove key %s\n", key);
3181 return -1;
3182 }
3183
3184 strlcpy(out, answer, outlen);
3185
3186 talloc_free(answer);
3187
3188 /*
3189 * We now try to find an exact match. i.e. we don't want
3190 * to find a shorter prefix.
3191 */
3192 answer = trie_key_match(ft->trie, (uint8_t *) key, 0, bits, true);
3193 if (answer) {
3194 MPRINT("Still in trie after 'remove' for key %s, found data %s\n", key, (char const *) answer);
3195 return -1;
3196 }
3197
3198 return 0;
3199}
3200
3201
3202/** Remove a key from the trie.
3203 *
3204 * Try to remove a key, but don't error if we can't.
3205 */
3206static int command_try_to_remove(fr_trie_t *ft, UNUSED int argc, char **argv, char *out, size_t outlen)
3207{
3208 int bits;
3209 void *answer;
3210 char *key;
3211
3212 if (arg2key(argv[0], &key, &bits) < 0) {
3213 return -1;
3214 }
3215
3216 answer = fr_trie_remove_by_key(ft, key, bits);
3217 if (!answer) {
3218 strlcpy(out, ".", outlen);
3219 return 0;
3220 }
3221
3222 strlcpy(out, answer, outlen);
3223
3224 talloc_free(answer);
3225
3226 /*
3227 * We now try to find an exact match. i.e. we don't want
3228 * to find a shorter prefix.
3229 */
3230 answer = trie_key_match(ft->trie, (uint8_t *) key, 0, bits, true);
3231 if (answer) {
3232 MPRINT("Still in trie after 'remove' for key %s, found data %s\n", key, (char const *) answer);
3233 return -1;
3234 }
3235
3236 return 0;
3237}
3238
3239
3240/** Print a trie to a string
3241 *
3242 * The trie is printed one one line. If the trie contains keys which
3243 * are not on a byte boundary, well... too bad. It gets printed
3244 * terribly.
3245 */
3246static int command_print(fr_trie_t *ft, UNUSED int argc, UNUSED char **argv, char *out, size_t outlen)
3247{
3248 fr_trie_callback_t my_cb;
3249 trie_sprint_ctx_t my_sprint;
3251
3252 /*
3253 * Where the output data goes.
3254 */
3255 my_sprint.start = out;
3256 my_sprint.buffer = out;
3257 my_sprint.buflen = outlen;
3258
3259 /*
3260 * Where the keys are built.
3261 */
3262 my_cb.start = buffer;
3263 my_cb.end = buffer + sizeof(buffer);
3264 my_cb.callback = _trie_sprint_cb;
3265 my_cb.user_callback = NULL;
3266 my_cb.ctx = &my_sprint;
3267
3268 memset(buffer, 0, sizeof(buffer));
3269
3270 /*
3271 * Call the internal walk function to do the work.
3272 */
3273 return trie_key_walk(ft->trie, &my_cb, 0, false);
3274}
3275
3276
3277/** Do insert / lookup / remove all at once.
3278 *
3279 * Sometimes it's more useful to do insert / lookup / remove for
3280 * simple keys.
3281 */
3282static int command_path(fr_trie_t *ft, int argc, char **argv, char *out, size_t outlen)
3283{
3284 void *data;
3285 void *answer;
3286
3287 data = talloc_strdup(ft, argv[1]); /* has to be malloc'd data, sorry */
3288 if (!data) {
3289 MPRINT("OOM\n");
3290 return -1;
3291 }
3292
3293 if (fr_trie_insert_by_key(ft, argv[0], BITSOF(strlen(argv[0])), data) < 0) {
3294 MPRINT("Could not insert key %s=%s - %s\n", argv[0], argv[1], fr_strerror());
3295 return -1;
3296 }
3297
3298 answer = fr_trie_lookup_by_key(ft, argv[0], BITSOF(strlen(argv[0])));
3299 if (!answer) {
3300 MPRINT("Could not look up key %s\n", argv[0]);
3301 return -1;
3302 }
3303
3304 if (answer != data) {
3305 MPRINT("Expected to find %s, got %s\n", argv[1], (char const *) answer);
3306 return -1;
3307 }
3308
3309 /*
3310 * Call the command 'print' to print out the key.
3311 */
3312 (void) command_print(ft, argc, argv, out, outlen);
3313
3314 answer = fr_trie_remove_by_key(ft, (uint8_t const *) argv[0], BITSOF(strlen(argv[0])));
3315 if (!answer) {
3316 MPRINT("Could not remove key %s\n", argv[0]);
3317 return -1;
3318 }
3319
3320 if (answer != data) {
3321 MPRINT("Expected to remove %s, got %s\n", argv[1], (char const *) answer);
3322 return -1;
3323 }
3324
3325 talloc_free(answer);
3326
3327 return 0;
3328}
3329
3330
3331/** Return the longest common prefix of two bit strings.
3332 *
3333 */
3334static int command_lcp(UNUSED fr_trie_t *ft, int argc, char **argv, char *out, size_t outlen)
3335{
3336 int lcp;
3337 int keylen1, keylen2;
3338 int start_bit;
3339 uint8_t const *key1, *key2;
3340
3341 if (argc == 2) {
3342 key1 = (uint8_t const *) argv[0];
3343 keylen1 = BITSOF(strlen(argv[0]));
3344
3345 key2 = (uint8_t const *) argv[1];
3346 keylen2 = BITSOF(strlen(argv[1]));
3347 start_bit = 0;
3348
3349 } else if (argc == 5) {
3350 key1 = (uint8_t const *) argv[0];
3351 keylen1 = atoi(argv[1]);
3352 if ((keylen1 < 0) || (keylen1 > (int) BITSOF(strlen(argv[0])))) {
3353 MPRINT("length of key1 %s is larger than string length %ld\n",
3354 argv[1], BITSOF(strlen(argv[0])));
3355 return -1;
3356 }
3357
3358 key2 = (uint8_t const *) argv[2];
3359 keylen2 = atoi(argv[3]);
3360 if ((keylen2 < 0) || (keylen2 > (int) BITSOF(strlen(argv[2])))) {
3361 MPRINT("length of key2 %s is larger than string length %ld\n",
3362 argv[3], BITSOF(strlen(argv[2])));
3363 return -1;
3364 }
3365
3366 start_bit = atoi(argv[4]);
3367 if ((start_bit < 0) || (start_bit > 7)) {
3368 MPRINT("start_bit has invalid value %s\n", argv[4]);
3369 return -1;
3370 }
3371
3372 } else {
3373 MPRINT("Invalid number of arguments\n");
3374 return -1;
3375 }
3376
3377 lcp = fr_trie_key_lcp(key1, keylen1, key2, keylen2, start_bit);
3378
3379 snprintf(out, outlen, "%d", lcp);
3380 return 0;
3381}
3382
3383/** Get chunks from raw data
3384 *
3385 */
3386static int command_chunk(UNUSED fr_trie_t *ft, UNUSED int argc, char **argv, char *out, size_t outlen)
3387{
3388 int start_bit, num_bits;
3389 uint16_t chunk;
3390
3391 start_bit = atoi(argv[1]);
3392 num_bits = atoi(argv[2]);
3393
3394 chunk = get_chunk((uint8_t const *) argv[0], start_bit, num_bits);
3395
3396 snprintf(out, outlen, "%04x", chunk);
3397 return 0;
3398}
3399
3400/** A function to parse a trie command line.
3401 *
3402 */
3403typedef int (*fr_trie_function_t)(fr_trie_t *ft, int argc, char **argv, char *out, size_t outlen);
3404
3405/** Data structure which holds the trie command name, function, etc.
3406 *
3407 */
3408typedef struct {
3409 char const *name;
3410 fr_trie_function_t function;
3411 int min_argc;
3412 int max_argc;
3413 bool output;
3414} fr_trie_command_t;
3415
3416
3417/** The trie commands for debugging.
3418 *
3419 */
3420static fr_trie_command_t commands[] = {
3421 { "lcp", command_lcp, 2, 5, true },
3422 { "chunk", command_chunk, 3, 3, true },
3423 { "path", command_path, 2, 2, true },
3424 { "insert", command_insert, 2, 2, false },
3425 { "match", command_match, 1, 1, true },
3426 { "lookup", command_lookup, 1, 1, true },
3427 { "remove", command_remove, 1, 1, true },
3428 { "-remove", command_try_to_remove, 1, 1, true },
3429 { "print", command_print, 0, 0, true },
3430 { "dump", command_dump, 0, 0, false },
3431 { "keys", command_keys, 0, 0, false },
3432 { "verify", command_verify, 0, 0, false },
3433 { "lineno", command_lineno, 1, 1, false },
3434 { "clear", command_clear, 0, 0, false },
3435 { .name = NULL }
3436};
3437
3438#define MAX_ARGC (16)
3439int main(int argc, char **argv)
3440{
3441 int lineno = 0;
3442 int ret = 0;
3443 fr_trie_t *ft;
3444 FILE *fp;
3445 int my_argc;
3446 char *my_argv[MAX_ARGC];
3447 char buffer[8192];
3448 char output[8192];
3449
3450 if (argc < 2) {
3451 fprintf(stderr, "Please specify filename\n");
3452 fr_exit_now(EXIT_SUCCESS);
3453 }
3454
3455 fp = fopen(argv[1], "r");
3456 if (!fp) {
3457 fprintf(stderr, "Failed opening %s: %s\n", argv[1], fr_syserror(errno));
3458 fr_exit_now(1);
3459 }
3460
3461 /*
3462 * Tell us if we leaked memory.
3463 */
3464 talloc_enable_null_tracking();
3465
3466 data_ctx = talloc_init_const("data_ctx");
3467
3468 ft = fr_trie_alloc(NULL, NULL, NULL);
3469 if (!ft) {
3470 fprintf(stderr, "Failed creating trie\n");
3471 fr_exit_now(1);
3472 }
3473
3474 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
3475 int i, cmd;
3476 char *p;
3477
3478 lineno++;
3479
3480 /*
3481 * Remove comments.
3482 */
3483 for (p = buffer; *p != '\0'; p++) {
3484 if (*p == '#') {
3485 *p = '\0';
3486 break;
3487 }
3488 }
3489
3490 /*
3491 * Skip leading whitespace.
3492 */
3493 p = buffer;
3495
3496 /*
3497 * Skip (now) blank lines.
3498 */
3499 if (!*p) continue;
3500
3501 my_argc = fr_dict_str_to_argv(p, my_argv, MAX_ARGC);
3502
3503 cmd = -1;
3504 for (i = 0; commands[i].name != NULL; i++) {
3505 if (strcmp(my_argv[0], commands[i].name) != 0) continue;
3506
3507 cmd = i;
3508 break;
3509 }
3510
3511 if (cmd < 0) {
3512 fprintf(stderr, "Unknown command '%s' at line %d\n",
3513 my_argv[0], lineno);
3514 ret = 1;
3515 break;
3516 }
3517
3518 /*
3519 * argv[0] is the command.
3520 * argv[argc-1] is the output.
3521 */
3522 if (((commands[cmd].min_argc + 1 + commands[cmd].output) > my_argc) ||
3523 ((commands[cmd].max_argc + 1 + commands[cmd].output) < my_argc)) {
3524 fprintf(stderr, "Invalid number of arguments to %s at line %d. Expected %d, got %d\n",
3525 my_argv[0], lineno, commands[cmd].min_argc + 1, my_argc - 1);
3526 fr_exit_now(1);
3527 }
3528
3529 if (print_lineno) {
3530 printf("%d ", lineno);
3531 fflush(stdout);
3532 }
3533
3534 MPRINT3("[%d] %s\n", lineno, my_argv[0]);
3535 if (commands[cmd].function(ft, my_argc - 1 - commands[cmd].output, &my_argv[1], output, sizeof(output)) < 0) {
3536 fprintf(stderr, "Failed running %s at line %d\n",
3537 my_argv[0], lineno);
3538 fr_exit_now(1);
3539 }
3540
3541 if (!commands[cmd].output) continue;
3542
3543 if (strcmp(output, my_argv[my_argc - 1]) != 0) {
3544 fprintf(stderr, "Failed running %s at line %d: Expected '%s' got '%s'\n",
3545 my_argv[0], lineno, my_argv[my_argc - 1], output);
3546 fr_exit_now(1);
3547 }
3548 }
3549
3550 fclose(fp);
3551
3552 trie_free(ft);
3553 talloc_free(data_ctx);
3554
3555 talloc_report_full(NULL, stdout); /* Print details of any leaked memory */
3556 talloc_disable_null_tracking(); /* Cleanup talloc null tracking context */
3557
3558 return ret;
3559}
3560#endif
static int const char char buffer[256]
Definition acutest.h:578
log_entry msg
Definition acutest.h:796
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:487
#define DIAG_ON(_x)
Definition build.h:462
#define CC_NO_UBSAN(_sanitize)
Definition build.h:430
#define UNUSED
Definition build.h:317
#define DIAG_OFF(_x)
Definition build.h:461
static int split(char **input, char **output, bool syntax_string)
Definition command.c:394
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
int main(int argc, char **argv)
Definition dhcpclient.c:531
int fr_dict_str_to_argv(char *str, char **argv, int max_argc)
talloc_free(hp)
unsigned short uint16_t
unsigned int uint32_t
unsigned char bool
unsigned char uint8_t
static size_t used
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
void(* fr_free_t)(void *)
Definition misc.h:39
static bool done
Definition radclient.c:83
static const char * spaces
Definition radict.c:178
static int8_t comp(void const *a, void const *b)
Definition rbmonkey.c:13
static char const * name
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:36
static void xor(char *out, char *in1, char *in2, int n)
Definition smbdes.c:183
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
fr_table_elem_name_t name
Definition table.h:66
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:123
#define talloc_strdup(_ctx, _str)
Definition talloc.h:145
void * fr_trie_remove_by_key(fr_trie_t *ft, void const *key, size_t keylen)
Remove a key and return the associated user ctx.
Definition trie.c:2157
int(* fr_trie_key_walk_t)(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
Definition trie.c:2177
#define MPRINT(...)
Internal sanity checks for debugging.
Definition trie.c:119
fr_trie_t * fr_trie_alloc(TALLOC_CTX *ctx, fr_trie_key_t get_key, fr_free_t free_data)
Allocate a trie.
Definition trie.c:741
static trie_key_match_t trie_match_table[FR_TRIE_MAX]
Definition trie.c:1203
static fr_trie_user_t * fr_trie_user_alloc(TALLOC_CTX *ctx, void const *data)
Definition trie.c:613
bool fr_trie_delete(fr_trie_t *ft, void const *data)
Remove node and free data (if a free function was specified)
Definition trie.c:2796
#define trie_check(_trie, _key, _start_bit, _end_bit, _data, _lineno)
Definition trie.c:1330
static void * trie_node_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
Definition trie.c:1937
void * fr_trie_remove(fr_trie_t *ft, void const *data)
Remove an entry, without freeing the data.
Definition trie.c:2770
static trie_key_remove_t trie_remove_table[FR_TRIE_MAX]
Definition trie.c:2115
bool fr_trie_insert(fr_trie_t *ft, void const *data)
Insert data into a trie.
Definition trie.c:2697
#define MAX_KEY_BITS
Definition trie.c:95
static void * trie_key_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
Match a key in a trie and return user ctx, if any.
Definition trie.c:1229
unsigned int fr_trie_num_elements(UNUSED fr_trie_t *ft)
Return how many nodes there are in a trie.
Definition trie.c:2825
static fr_trie_key_walk_t trie_walk_table[FR_TRIE_MAX]
Definition trie.c:2256
#define TRIE_HEADER
Definition trie.c:480
uint16_t chunk
Definition trie.c:510
int fr_trie_replace(void **old, fr_trie_t *ft, void const *data)
Replace old data with new data, OR insert if there is no old.
Definition trie.c:2730
static int trie_key_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
Definition trie.c:2267
#define BYTEOF(_x)
Definition trie.c:137
#define MAX_KEY_BYTES
Definition trie.c:94
static uint16_t get_chunk(uint8_t const *key, uint32_t start_bit, uint32_t num_bits)
Return a chunk of a key (in the low bits) for use in 2^N node de-indexing.
Definition trie.c:337
fr_free_t free_data
Definition trie.c:729
fr_trie_t * trie[]
Definition trie.c:494
static void * trie_key_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
Remove a key from a trie, and return the user data.
Definition trie.c:2129
static void * trie_path_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
Definition trie.c:1163
static fr_trie_path_t * fr_trie_path_alloc(TALLOC_CTX *ctx, uint8_t const *key, int start_bit, int end_bit)
Definition trie.c:634
fr_trie_t * trie
Definition trie.c:508
int fr_trie_walk(fr_trie_t *ft, void *ctx, fr_trie_walk_t callback)
Definition trie.c:2610
#define BYTES(_x)
Definition trie.c:138
#define MPRINT3(...)
Definition trie.c:121
void * fr_trie_lookup_by_key(fr_trie_t const *ft, void const *key, size_t keylen)
Lookup a key in a trie and return user ctx, if any.
Definition trie.c:1265
fr_trie_walk_t user_callback
Definition trie.c:2188
static int _trie_user_cb(fr_trie_t *trie, fr_trie_callback_t *cb, int keylen, UNUSED bool more)
Implement the user-visible side of the walk callback.
Definition trie.c:2592
uint8_t * start
Definition trie.c:2182
static uint8_t xor2lcp[256]
Definition trie.c:172
static void * trie_user_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
Definition trie.c:1915
static fr_trie_path_t * trie_path_split(TALLOC_CTX *ctx, fr_trie_path_t *path, int start_bit, int lcp)
Definition trie.c:831
void * data
Definition trie.c:501
static int trie_path_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
Definition trie.c:1425
static int trie_node_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
Definition trie.c:1351
fr_trie_key_walk_t callback
Definition trie.c:2187
#define trie_sprint(_trie, _key, _start_bit, _lineno)
Definition trie.c:122
#define VERIFY(_x)
Definition trie.c:130
static void * trie_path_remove(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
Definition trie.c:1977
static void * trie_user_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
Definition trie.c:1113
#define BITSOF(_x)
Definition trie.c:136
static trie_key_insert_t trie_insert_table[FR_TRIE_MAX]
Definition trie.c:1788
static fr_trie_node_t * trie_node_split(TALLOC_CTX *ctx, fr_trie_node_t *node, int bits)
Split a node at bits.
Definition trie.c:773
static int trie_node_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
Definition trie.c:2200
uint8_t buffer[16]
Definition trie.c:727
void * fr_trie_match(fr_trie_t *ft, void const *data)
Match an element exactly in the trie, returning the data.
Definition trie.c:2671
fr_trie_type_t
Definition trie.c:454
@ FR_TRIE_PATH
Definition trie.c:458
@ FR_TRIE_NODE
Definition trie.c:463
@ FR_TRIE_INVALID
Definition trie.c:455
@ FR_TRIE_USER
Definition trie.c:456
#define FR_TRIE_MAX
Definition trie.c:466
#define TRIE_TYPE_CHECK(_x, _r)
Definition trie.c:481
#define MPRINT2(...)
Definition trie.c:120
static fr_trie_t * trie_key_alloc(TALLOC_CTX *ctx, uint8_t const *key, int start_bit, int end_bit, void *data)
Definition trie.c:890
static void write_chunk(uint8_t *out, int start_bit, int num_bits, uint16_t chunk)
Write a chunk to an output buffer.
Definition trie.c:409
static int fr_trie_key_lcp(uint8_t const *key1, int keylen1, uint8_t const *key2, int keylen2, int start_bit)
Get the longest prefix of the two keys.
Definition trie.c:227
fr_trie_t * trie
Definition trie.c:500
static int trie_user_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
Definition trie.c:2191
void * fr_trie_find(fr_trie_t *ft, void const *data)
Find an element in the trie, returning the data.
Definition trie.c:2645
void * fr_trie_match_by_key(fr_trie_t const *ft, void const *key, size_t keylen)
Match a key and length in a trie and return user ctx, if any.
Definition trie.c:1289
static int trie_add_edge(fr_trie_t *trie, uint16_t chunk, fr_trie_t *child)
Add an edge to a node.
Definition trie.c:1049
static int trie_key_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data))
Insert a binary key into the trie.
Definition trie.c:1813
TRIE_HEADER
Definition trie.c:485
uint8_t key[2]
Definition trie.c:511
fr_trie_t * trie
Definition trie.c:487
#define MAX_NODE_BITS
Definition trie.c:98
static fr_trie_node_t * trie_node_alloc(TALLOC_CTX *ctx, int bits)
Definition trie.c:528
uint8_t const * end
Definition trie.c:2183
int fr_trie_insert_by_key(fr_trie_t *ft, void const *key, size_t keylen, void const *data)
Insert a key and user ctx into a trie.
Definition trie.c:1878
fr_trie_key_t get_key
Definition trie.c:728
void *(* trie_key_match_t)(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
Definition trie.c:1109
static void * trie_node_match(fr_trie_t *trie, uint8_t const *key, int start_bit, int end_bit, bool exact)
Definition trie.c:1148
static int trie_path_walk(fr_trie_t *trie, fr_trie_callback_t *cb, int depth, bool more)
Definition trie.c:2222
void *(* trie_key_remove_t)(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit)
Definition trie.c:1913
static uint8_t start_bit_mask[8]
Definition trie.c:150
static int trie_user_insert(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
Definition trie.c:1337
static uint8_t used_bit_mask[8]
Definition trie.c:155
int(* trie_key_insert_t)(TALLOC_CTX *ctx, fr_trie_t **trie_p, uint8_t const *key, int start_bit, int end_bit, void *data)
Definition trie.c:1335
static void trie_free(fr_trie_t *trie)
Free a fr_trie_t.
Definition trie.c:562
int(* fr_trie_key_t)(uint8_t **out, size_t *outlen, void const *data)
Definition trie.h:57
int(* fr_trie_walk_t)(uint8_t const *key, size_t keylen, void *data, void *uctx)
Walk over a trie.
Definition trie.h:44
static fr_table_ptr_sorted_t commands[]
static void command_print(void)
static size_t command_match(command_result_t *result, command_file_ctx_t *cc, char *data, size_t data_used, char *in, size_t inlen)
Compare the data buffer to an expected value.
static size_t command_clear(command_result_t *result, UNUSED command_file_ctx_t *cc, char *data, size_t UNUSED data_used, UNUSED char *in, UNUSED size_t inlen)
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1340
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030