2 * UCW Library -- Byte-based trie
4 * (c) 2008 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 * This is not a normal header file, it's a generator of tries.
12 * Each time you include it with parameters set in the corresponding
13 * preprocessor macros, it generates a trie with the parameters given.
15 * You need to specify:
17 * [*] TRIE_PREFIX(x) macro to add a name prefix (used on all global names
18 * defined by the trie generator).
20 * [*] TRIE_NODE_TYPE data type where a node dwells (usually a struct).
21 * TRIE_NODE_KEY(node) macro to return the pointer to the key (default=&x)
22 * TRIE_NODE_LEN(node) macro to return the length of the key (default=str_len(TRIE_NODE_KEY(node)))
23 * TRIE_LEN_TYPE integer type large enough to hold length of any inserted string (default=u32).
24 * TRIE_REV work with reversed strings.
27 * TRIE_WANT_CLEANUP cleanup()
29 * TRIE_WANT_FIND node *find(char *str)
30 * TRIE_WANT_FIND_BUF node *find_buf(byte *ptr, uns len)
31 * TRIE_WANT_ADD add(*node)
32 * TRIE_WANT_REPLACE node *replace(*node)
33 * TRIE_WANT_DELETE delete(char *str)
34 * TRIE_WANT_DELETE_BUF delete_buf(byte *ptr, uns len)
35 * TRIE_WANT_REMOVE remove(*node)
37 * TRIE_WANT_AUDIT audit()
40 * Be warned that the implementation uses alloca() in several macros,
41 * so if you use automatic variable-length arrays in the same function,
42 * all the hell may be unleashed.
47 #ifndef _SHERLOCK_UCW_TRIE_H
48 #define _SHERLOCK_UCW_TRIE_H
50 #include <ucw/eltpool.h>
51 #include <ucw/hashfunc.h>
55 #define TRIE_FLAG_DEG 0x01ff // mask for edge degree (0-256)
56 #define TRIE_FLAG_HASH 0x0200 // sons are stored in a hash table
57 #define TRIE_FLAG_NODE 0x0400 // edge contains inserted data
64 #error Undefined mandatory macro TRIE_PREFIX
66 #define P(x) TRIE_PREFIX(x)
68 #ifndef TRIE_NODE_TYPE
69 #error Undefined mandatory macro TRIE_NODE_TYPE
71 typedef TRIE_NODE_TYPE P(node_t);
74 #define TRIE_NODE_KEY(node) ((char *)&(node))
78 #define TRIE_NODE_LEN(node) (str_len(TRIE_NODE_KEY(node)))
82 #define TRIE_LEN_TYPE u32
84 typedef TRIE_LEN_TYPE P(len_t);
86 #ifndef TRIE_ELTPOOL_SIZE
87 #define TRIE_ELTPOOL_SIZE 1024
90 #ifndef TRIE_HASH_THRESHOLD
91 #define TRIE_HASH_THRESHOLD (6 - sizeof(P(len_t)))
94 #ifndef TRIE_BUCKET_RANK
95 #define TRIE_BUCKET_RANK (2U + (sizeof(void *) > 4))
97 #define TRIE_BUCKET_SIZE (1U << TRIE_BUCKET_RANK)
98 #define TRIE_BUCKET_MASK (TRIE_BUCKET_SIZE - 1)
99 enum { P(bucket_rank) = TRIE_BUCKET_RANK };
101 #define TRIE_COMPILE_ASSERT(x, y) typedef char TRIE_PREFIX(x##_compile_assert)[!!(y)-1]
102 TRIE_COMPILE_ASSERT(len_type, sizeof(P(len_t)) <= sizeof(uns));
103 TRIE_COMPILE_ASSERT(hash_threshold, TRIE_HASH_THRESHOLD >= 2);
104 TRIE_COMPILE_ASSERT(bucket_size, TRIE_BUCKET_RANK >= 1 && TRIE_BUCKET_MASK < sizeof(void *));
107 #define TRIE_DBG(x...) msg(L_DEBUG, "TRIE: " x)
109 #define TRIE_DBG(x...) do{}while(0)
112 /*** Solve dependencies ***/
114 #if !defined(TRIE_WANT_DO_FIND) && (defined(TRIE_WANT_FIND) || defined(TRIE_WANT_FIND_BUF))
115 #define TRIE_WANT_DO_FIND
118 #if !defined(TRIE_WANT_DO_LOOKUP) && (defined(TRIE_WANT_ADD) || defined(TRIE_WANT_REPLACE))
119 #define TRIE_WANT_DO_LOOKUP
122 #if !defined(TRIE_WANT_DO_DELETE) && (defined(TRIE_WANT_DELETE) || defined(TRIE_WANT_DELETE_BUF) || defined(TRIE_WANT_REMOVE))
123 #define TRIE_WANT_DO_DELETE
126 #if !defined(TRIE_WANT_DO_LOOKUP)
127 #error You must request at least one method for inserting nodes
130 /*** Data structures ***/
133 struct P(edge) *root; // root edge or NULL
134 struct eltpool *epool[TRIE_HASH_THRESHOLD + 1]; // eltpools for edges with array of sons
135 struct eltpool *hpool[9]; // eltpools for edges with hash table
139 u16 flags; // TRIE_FLAG_x
141 byte trans[TRIE_HASH_THRESHOLD]; // transition characters (!TRIE_FLAG_HASH)
143 byte hash_rank; // logarithmic hash size (TRIE_FLAG_HASH)
144 byte hash_deleted; // number of deleted items
147 P(len_t) len; // sum of all ancestor edges with their trasition
148 // characters plus the length of the current edge
150 P(node_t) *node; // inserted data (TRIE_FLAG_NODE)
151 struct P(edge) *leaf; // reference to a descendant with data (!TRIE_FLAG_NODE)
153 struct P(edge) *son[0]; // array of sons (!TRIE_FLAG_HASH)
154 // or hash table (TRIE_FLAG_HASH)
159 #define TA struct P(trie) *trie
164 static struct P(trie) P(trie);
172 /*** Memory management ***/
177 TRIE_DBG("Initializing");
178 bzero(&T, sizeof(T));
179 for (uns i = 0; i < ARRAY_SIZE(T.epool); i++)
181 uns size = sizeof(struct P(edge)) + i * sizeof(void *);
182 T.epool[i] = ep_new(size, MAX(TRIE_ELTPOOL_SIZE / size, 1));
184 for (uns i = 0; i < ARRAY_SIZE(T.hpool); i++)
186 uns size = sizeof(struct P(edge)) + ((sizeof(void *) << TRIE_BUCKET_RANK) << i);
187 T.hpool[i] = ep_new(size, MAX(TRIE_ELTPOOL_SIZE / size, 1));
191 #ifdef TRIE_WANT_CLEANUP
195 TRIE_DBG("Cleaning up");
196 for (uns i = 0; i < ARRAY_SIZE(T.epool); i++)
197 ep_delete(T.epool[i]);
198 for (uns i = 0; i < ARRAY_SIZE(T.hpool); i++)
199 ep_delete(T.hpool[i]);
203 static struct P(edge) *
204 P(edge_alloc)(TAC uns flags)
206 struct P(edge) *edge;
207 if (flags & TRIE_FLAG_HASH)
209 uns rank = 0, deg = flags & TRIE_FLAG_DEG;
210 while ((TRIE_BUCKET_MASK << rank) < deg * 2) // 25-50% density
212 ASSERT(rank < ARRAY_SIZE(T.hpool));
213 edge = ep_alloc(T.hpool[rank]);
214 edge->hash_rank = rank;
215 edge->hash_deleted = 0;
216 bzero(edge->son, (sizeof(void *) << TRIE_BUCKET_RANK) << rank);
219 edge = ep_alloc(T.epool[flags & TRIE_FLAG_DEG]);
221 TRIE_DBG("Allocated edge %p, flags=0x%x", edge, flags);
226 P(edge_free)(TAC struct P(edge) *edge)
228 TRIE_DBG("Freeing edge %p, flags=0x%x", edge, edge->flags);
229 if (edge->flags & TRIE_FLAG_HASH)
230 ep_free(T.hpool[edge->hash_rank], edge);
232 ep_free(T.epool[edge->flags & TRIE_FLAG_DEG], edge);
235 /*** Manipulation with strings ***/
238 P(str_get)(P(node_t) *node)
240 return TRIE_NODE_KEY((*node));
244 P(str_len)(P(node_t) *node)
246 return TRIE_NODE_LEN((*node));
250 P(str_char)(byte *ptr, uns len UNUSED, uns pos)
255 return ptr[len - pos - 1];
260 P(str_prefix)(byte *ptr, uns len UNUSED, uns prefix UNUSED)
265 return ptr + len - prefix;
270 P(str_suffix)(byte *ptr, uns len UNUSED, uns suffix UNUSED)
273 return ptr + len - suffix;
280 P(common_prefix)(byte *ptr1, uns len1, byte *ptr2, uns len2)
282 uns l = MIN(len1, len2), i;
283 for (i = 0; i < l; i++)
284 if (P(str_char)(ptr1, len1, i) != P(str_char)(ptr2, len2, i))
294 return hash_u32(c) >> 16;
297 static inline struct P(edge) **
298 P(hash_find)(struct P(edge) *edge, uns c)
300 uns mask = (TRIE_BUCKET_SIZE << edge->hash_rank) - 1;
301 for (uns i = P(hash_func)(c); ; i++)
302 if (((i &= mask) & TRIE_BUCKET_MASK) && (uintptr_t)edge->son[i] != 1)
305 else if (((byte *)&edge->son[i & ~TRIE_BUCKET_MASK])[i & TRIE_BUCKET_MASK] == c)
306 return &edge->son[i];
309 static inline struct P(edge) **
310 P(hash_insert)(struct P(edge) *edge, uns c)
312 uns mask = (TRIE_BUCKET_SIZE << edge->hash_rank) - 1;
313 for (uns i = P(hash_func)(c); ; i++)
314 if (((i &= mask) & TRIE_BUCKET_MASK) && (uintptr_t)edge->son[i] <= 1)
316 edge->hash_deleted -= (uintptr_t)edge->son[i];
318 ((byte *)&edge->son[i & ~TRIE_BUCKET_MASK])[i & TRIE_BUCKET_MASK] = c;
319 return &edge->son[i];
323 #ifdef TRIE_WANT_DO_DELETE
325 P(hash_delete)(struct P(edge) *edge, uns c)
327 uns mask = (TRIE_BUCKET_SIZE << edge->hash_rank) - 1;
328 for (uns i = P(hash_func)(c); ; i++)
329 if (((i &= mask) & TRIE_BUCKET_MASK) && (uintptr_t)edge->son[i] > 1 &&
330 ((byte *)&edge->son[i & ~TRIE_BUCKET_MASK])[i & TRIE_BUCKET_MASK] == c)
332 edge->hash_deleted++;
333 edge->son[i] = (void *)1;
339 #define TRIE_HASH_FOR_ALL(xedge, xtrans, xson) do { \
340 struct P(edge) *_edge = (xedge); \
341 for (uns _i = (TRIE_BUCKET_SIZE << _edge->hash_rank); _i--; ) \
342 if ((_i & TRIE_BUCKET_MASK) && (uintptr_t)_edge->son[_i] > 1) { \
343 UNUSED uns xtrans = ((byte *)&_edge->son[_i & ~TRIE_BUCKET_MASK])[_i & TRIE_BUCKET_MASK]; \
344 UNUSED struct P(edge) *xson = _edge->son[_i]; \
346 #define TRIE_HASH_END_FOR }while(0);}}while(0)
349 P(hash_realloc)(TAC struct P(edge) **ref)
351 struct P(edge) *old = *ref, *edge = *ref = P(edge_alloc)(TTC old->flags);
352 TRIE_DBG("Reallocating hash table");
353 edge->node = old->node;
354 edge->len = old->len;
355 TRIE_HASH_FOR_ALL(old, trans, son)
356 *P(hash_insert)(edge, trans) = son;
358 P(edge_free)(TTC old);
361 /*** Finding/inserting/deleting sons ***/
363 static struct P(edge) **
364 P(son_find)(struct P(edge) *edge, uns c)
366 if (edge->flags & TRIE_FLAG_HASH)
367 return P(hash_find)(edge, c);
369 for (uns i = edge->flags & TRIE_FLAG_DEG; i--; )
370 if (edge->trans[i] == c)
371 return &edge->son[i];
375 static struct P(edge) **
376 P(son_insert)(TAC struct P(edge) **ref, uns c)
378 struct P(edge) *old = *ref, *edge;
379 uns deg = old->flags & TRIE_FLAG_DEG;
380 if (old->flags & TRIE_FLAG_HASH)
383 if ((deg + 1 + old->hash_deleted) * 4 > (TRIE_BUCKET_MASK << old->hash_rank) * 3) // >75% density
385 P(hash_realloc)(TTC ref);
393 if (deg < TRIE_HASH_THRESHOLD)
395 TRIE_DBG("Growing array");
396 edge = P(edge_alloc)(TTC old->flags + 1);
397 memcpy((byte *)edge + sizeof(edge->flags), (byte *)old + sizeof(edge->flags),
398 sizeof(*old) - sizeof(edge->flags) + deg * sizeof(*old->son));
399 edge->trans[deg] = c;
400 edge->son[deg] = NULL;
401 P(edge_free)(TTC old);
403 return &edge->son[deg];
407 TRIE_DBG("Growing array to hash table");
408 edge = P(edge_alloc)(TTC (old->flags + 1) | TRIE_FLAG_HASH);
409 edge->node = old->node;
410 edge->len = old->len;
411 for (uns i = 0; i < deg; i++)
412 *P(hash_insert)(edge, old->trans[i]) = old->son[i];
413 P(edge_free)(TTC old);
417 return P(hash_insert)(edge, c);
420 #ifdef TRIE_WANT_DO_DELETE
422 P(son_delete)(TAC struct P(edge) **ref, uns c)
424 struct P(edge) *old = *ref, *edge;
425 uns deg = old->flags & TRIE_FLAG_DEG;
427 if (old->flags & TRIE_FLAG_HASH)
429 P(hash_delete)(old, c);
432 if (deg <= TRIE_HASH_THRESHOLD / 2)
434 TRIE_DBG("Reducing hash table to array");
435 edge = P(edge_alloc)(TTC old->flags & ~TRIE_FLAG_HASH);
437 TRIE_HASH_FOR_ALL(old, trans, son)
438 edge->trans[k] = trans;
444 else if (deg * 6 >= (TRIE_BUCKET_MASK << old->hash_rank)) // >= 16%
448 P(hash_realloc)(TTC ref);
455 TRIE_DBG("Reducing array");
456 edge = P(edge_alloc)(TTC old->flags - 1);
458 for (uns i = 0; i < deg; i++)
459 if (old->trans[i] != c)
461 edge->trans[j] = old->trans[i];
462 edge->son[j] = old->son[i];
465 ASSERT(j == deg - 1);
467 edge->node = old->node;
468 edge->len = old->len;
469 P(edge_free)(TTC old);
474 #ifdef TRIE_WANT_DO_DELETE
475 static struct P(edge) *
476 P(son_any)(struct P(edge) *edge)
478 ASSERT(edge->flags & TRIE_FLAG_DEG);
479 if (!(edge->flags & TRIE_FLAG_HASH))
482 for (uns i = 0; ; i++)
483 if ((i & TRIE_BUCKET_MASK) && (uintptr_t)edge->son[i] > 1)
488 /*** Find/insert/delete ***/
490 #ifdef TRIE_WANT_DO_FIND
491 static struct P(edge) *
492 P(do_find)(TAC byte *ptr, uns len)
494 TRIE_DBG("do_find('%.*s')", len, ptr);
495 struct P(edge) **ref = &T.root, *edge;
498 if (!(edge = *ref) || edge->len > len)
500 else if (edge->len == len)
501 return ((edge->flags & TRIE_FLAG_NODE) && !memcmp(ptr, P(str_get)(edge->node), len)) ? edge : NULL;
503 while (ref = P(son_find)(edge, P(str_char)(ptr, len, edge->len)));
508 static struct P(edge) *
509 P(do_lookup)(TAC byte *ptr, uns len)
511 TRIE_DBG("do_lookup('%.*s')", len, ptr);
512 struct P(edge) **ref, *edge, *leaf, *newleaf;
513 uns prefix, elen, trans, pos;
516 if (!(edge = T.root))
518 TRIE_DBG("Creating first edge");
519 edge = T.root = P(edge_alloc)(TTC TRIE_FLAG_NODE);
526 while (edge->len < len && (ref = P(son_find)(edge, P(str_char)(ptr, len, edge->len))))
528 if (!(edge->flags & TRIE_FLAG_NODE))
530 ASSERT(edge->flags & TRIE_FLAG_NODE);
531 eptr = P(str_get)(edge->node);
533 prefix = P(common_prefix)(ptr, len, eptr, elen);
534 if (prefix == len && prefix == elen)
536 TRIE_DBG("The longest common prefix is '%.*s'", prefix, P(str_prefix)(ptr, len, prefix));
540 TRIE_DBG("Creating a new leaf");
541 newleaf = P(edge_alloc)(TTC TRIE_FLAG_NODE);
542 newleaf->node = NULL;
554 leaf = (edge->flags & TRIE_FLAG_NODE) ? edge : edge->leaf;
555 TRIE_DBG("Splitting edge '%.*s'", leaf->len, P(str_get)(leaf->node));
556 trans = P(str_char)(P(str_get)(leaf->node), leaf->len, prefix);
559 edge = P(edge_alloc)(TTC 1 | TRIE_FLAG_NODE);
562 edge->trans[0] = trans;
568 edge = P(edge_alloc)(TTC 2);
571 edge->trans[0] = trans;
573 edge->trans[1] = P(str_char)(ptr, len, prefix);
575 return edge->son[1] = newleaf;
580 TRIE_DBG("Adding the node to an already existing edge");
581 edge->flags |= TRIE_FLAG_NODE;
585 if (!(edge->flags & TRIE_FLAG_NODE) && newleaf)
586 edge->leaf = newleaf;
587 trans = P(str_char)(ptr, len, pos);
589 ref = P(son_find)(edge, trans);
591 ref = P(son_insert)(TTC ref, trans);
594 return *ref = newleaf;
597 #ifdef TRIE_WANT_DO_DELETE
599 P(do_delete)(TAC byte *ptr, uns len)
601 TRIE_DBG("do_delete('%.*s')", len, ptr);
602 struct P(edge) **ref = &T.root, **pref = NULL, *edge, *parent, *leaf, *pold = NULL;
605 if (!(edge = *ref) || edge->len > len)
607 else if (edge->len == len)
608 if ((edge->flags & TRIE_FLAG_NODE) && !memcmp(ptr, P(str_get)(edge->node), len))
613 if (!(ref = P(son_find)(edge, P(str_char)(ptr, len, edge->len))))
617 P(node_t) *node = edge->node;
618 uns deg = edge->flags & TRIE_FLAG_DEG;
624 TRIE_DBG("Deleting last edge");
626 P(edge_free)(TTC edge);
631 TRIE_DBG("Deleting a leaf");
633 P(son_delete)(TTC pref, P(str_char)(ptr, len, pold->len));
635 if ((parent->flags & (TRIE_FLAG_DEG | TRIE_FLAG_NODE)) <= 1)
637 ASSERT((parent->flags & (TRIE_FLAG_DEG | TRIE_FLAG_HASH)) == 1);
638 TRIE_DBG("... and its parent");
639 leaf = *pref = parent->son[0];
640 P(edge_free)(TTC parent);
642 else if (parent->flags & TRIE_FLAG_NODE)
645 leaf = P(son_any)(parent);
647 P(edge_free)(TTC edge);
651 TRIE_DBG("Deleting internal edge");
652 ASSERT(!(edge->flags & TRIE_FLAG_HASH));
653 leaf = *ref = edge->son[0];
654 P(edge_free)(TTC edge);
658 TRIE_DBG("Deleting node, but leaving edge");
659 leaf = P(son_any)(edge);
660 if (!(leaf->flags & TRIE_FLAG_NODE))
663 edge->flags &= ~TRIE_FLAG_NODE;
666 TRIE_DBG("Updating leaf pointers");
667 if (!(leaf->flags & TRIE_FLAG_NODE))
669 ASSERT(leaf->flags & TRIE_FLAG_NODE);
670 for (ref = &T.root; ref && (*ref)->len < len; ref = P(son_find)(*ref, P(str_char)(ptr, len, (*ref)->len)))
671 if ((*ref)->leaf == edge || (*ref)->leaf == pold)
677 #ifdef TRIE_WANT_FIND
678 static inline P(node_t) *
679 P(find)(TAC char *str)
681 struct P(edge) *edge = P(do_find)(TTC str, str_len(str));
682 return edge ? edge->node : NULL;
686 #ifdef TRIE_WANT_FIND_BUF
687 static inline P(node_t) *
688 P(find_buf)(TAC byte *ptr, uns len)
690 struct P(edge) *edge = P(do_find)(TTC ptr, len);
691 return edge ? edge->node : NULL;
697 P(add)(TAC P(node_t) *node)
699 struct P(edge) *edge = P(do_lookup)(TTC P(str_get)(node), P(str_len)(node));
705 #ifdef TRIE_WANT_REPLACE
706 static inline P(node_t) *
707 P(replace)(TAC P(node_t) *node)
709 struct P(edge) *edge = P(do_lookup)(TTC P(str_get)(node), P(str_len)(node));
710 P(node_t) *over = edge->node;
716 #ifdef TRIE_WANT_DELETE
717 static inline P(node_t) *
718 P(delete)(TAC char *str)
720 return P(do_delete)(TTC str, str_len(str));
724 #ifdef TRIE_WANT_DELETE_BUF
725 static inline P(node_t) *
726 P(delete_buf)(TAC byte *ptr, uns len)
728 return P(do_delete)(TTC ptr, len);
732 #ifdef TRIE_WANT_REMOVE
734 P(remove)(TAC P(node_t) *node)
736 if (unlikely(P(do_delete)(TTC P(str_get)(node), P(str_len)(node)) != node))
741 /*** Traversing prefixes and subtrees ***/
745 // for all matched edges until the first >=xlen (including)
746 #define TRIE_FOR_PREFIX_EDGES(px, xtrie, xptr, xlen, xedge) \
749 byte *_ptr = (xptr); \
751 struct px##trie *_trie = (xtrie); \
752 struct px##edge *xedge, **_ref; \
753 if (!(xedge = _trie->root)) \
755 while (xedge->len < _len && (_ref = px##son_find(xedge, px##str_char(_ptr, _len, xedge->len)))) \
757 if (!(xedge->flags & TRIE_FLAG_NODE)) \
758 xedge = xedge->leaf; \
759 uns _prefix = px##common_prefix(_ptr, _len, px##str_get(xedge->node), xedge->len); \
760 for (_ref = &_trie->root; _ref && ((xedge = *_ref)->len <= _prefix || _prefix == _len); \
761 _ref = (xedge->len < _prefix) ? px##son_find(xedge, px##str_char(_ptr, _len, xedge->len)) : NULL) \
763 #define TRIE_END_PREFIX_EDGES \
768 // for entire subtree starting in the xstart edge
769 #define TRIE_FOR_SUBTREE_EDGES(px, xstart, xedge) \
772 struct { struct px##edge *edge; uns pos; } \
773 *_sbuf = alloca(sizeof(*_sbuf) * 16), \
774 *_sptr = _sbuf, *_send = _sbuf + 16; \
775 struct px##edge *_next = (xstart), *xedge; \
776 while (xedge = _next) \
778 if (xedge->flags & TRIE_FLAG_DEG) \
780 if (_sptr == _send) \
782 uns stack_size = _sptr - _sbuf; \
783 _sptr = alloca(sizeof(*_sptr) * (stack_size * 2)); \
784 memcpy(_sptr, _sbuf, sizeof(*_sptr) * stack_size); \
786 _send = _sptr + stack_size * 2; \
788 _sptr->edge = xedge; \
789 _sptr->pos = (xedge->flags & TRIE_FLAG_HASH) ? \
790 (1U << px##bucket_rank) << xedge->hash_rank : \
791 (xedge->flags & TRIE_FLAG_DEG); \
796 if (_sptr == _sbuf) \
801 _next = (--_sptr)->edge; \
802 uns pos = --(_sptr->pos); \
803 uns flags = _next->flags; \
804 _next = _next->son[pos]; \
807 if (!(flags & TRIE_FLAG_HASH) || \
808 ((pos & ((1U << px##bucket_rank) - 1)) && \
809 (uintptr_t)_next > 1)) \
812 #define TRIE_END_SUBTREE_EDGES \
817 #define TRIE_FOR_SUBTREE(px, xstart, xnode) \
818 TRIE_FOR_SUBTREE_EDGES(px, xstart, _edge) \
819 if (_edge->flags & TRIE_FLAG_NODE) \
821 px##node_t *xnode = _edge->node;
822 #define TRIE_END_SUBTREE \
824 TRIE_END_SUBTREE_EDGES;
826 #define TRIE_FOR_ALL_EDGES(px, xtrie, xedge) TRIE_FOR_SUBTREE_EDGES(px, (xtrie)->root, xedge)
827 #define TRIE_END_ALL_EDGES TRIE_END_SUBTREE_EDGES
829 #define TRIE_FOR_ALL(px, xtrie, xnode) TRIE_FOR_SUBTREE(px, (xtrie)->root, xnode)
830 #define TRIE_END_ALL TRIE_END_SUBTREE
834 /*** Check consistency ***/
836 #ifdef TRIE_WANT_AUDIT
842 TRIE_FOR_ALL_EDGES(TRIE_PREFIX(), &T, edge)
845 uns deg = edge->flags & TRIE_FLAG_DEG;
847 struct P(edge) * leaf = (edge->flags & TRIE_FLAG_NODE) ? edge : edge->leaf;
850 ASSERT(leaf->flags & TRIE_FLAG_NODE);
851 ASSERT(leaf->len > edge->len);
854 TRIE_DBG("Checking edge %p, %s=%p, flags=0x%x, key='%.*s'",
855 edge, (edge->flags & TRIE_FLAG_NODE) ? "node" : "leaf", edge->node, edge->flags,
856 edge->len, P(str_prefix)(P(str_get)(leaf->node), leaf->len, edge->len));
857 ASSERT(deg >= 2 || (edge->flags & TRIE_FLAG_NODE));
858 if (edge->flags & TRIE_FLAG_HASH)
860 ASSERT(deg > 1 && deg <= 256);
861 uns count = 0, deleted = 0;
862 for (uns i = TRIE_BUCKET_SIZE << edge->hash_rank; i--; )
863 if (i & TRIE_BUCKET_MASK)
864 if ((uintptr_t)edge->son[i] == 1)
866 else if (edge->son[i])
868 ASSERT(edge->son[i]->len > edge->len);
871 ASSERT(count == deg);
872 ASSERT(deleted == edge->hash_deleted);
876 ASSERT(deg <= TRIE_HASH_THRESHOLD);
877 for (uns i = 0; i < deg; i++)
878 ASSERT(edge->son[i]->len > edge->len);
883 TRIE_DBG("Found %u edges", count);
890 #ifdef TRIE_WANT_STATS
899 P(stats)(TAC struct P(stats) *stats)
901 bzero(stats, sizeof(*stats));
902 for (uns i = 0; i < ARRAY_SIZE(T.epool); i++)
903 stats->small_size += ep_total_size(T.epool[i]);
904 for (uns i = 0; i < ARRAY_SIZE(T.hpool); i++)
905 stats->hash_size += ep_total_size(T.hpool[i]);
906 stats->total_size = stats->small_size + stats->hash_size + sizeof(T);
912 struct P(stats) stats;
913 P(stats)(TTC &stats);
914 return stats.total_size;
919 /*** Clean up local macros ***/
929 #undef TRIE_NODE_TYPE
935 #undef TRIE_ELTPOOL_SIZE
936 #undef TRIE_HASH_THRESHOLD
937 #undef TRIE_BUCKET_RANK
938 #undef TRIE_BUCKET_SIZE
939 #undef TRIE_BUCKET_MASK
942 #undef TRIE_HASH_FOR_ALL
943 #undef TRIE_HASH_END_FOR
945 #undef TRIE_WANT_CLEANUP
947 #undef TRIE_WANT_DO_FIND
948 #undef TRIE_WANT_DO_LOOKUP
949 #undef TRIE_WANT_DO_DELETE
951 #undef TRIE_WANT_FIND
952 #undef TRIE_WANT_FIND_BUF
954 #undef TRIE_WANT_ADD_OVER
955 #undef TRIE_WANT_DELETE
956 #undef TRIE_WANT_DELETE_BUF
957 #undef TRIE_WANT_REMOVE
959 #undef TRIE_WANT_AUDIT