1.1 --- a/core/sm_trie.cpp Wed Aug 29 02:43:07 2007 +0000
1.2 +++ b/core/sm_trie.cpp Wed Aug 29 05:20:48 2007 +0000
1.3 @@ -30,788 +30,58 @@
1.4 */
1.5
1.6 #include <string.h>
1.7 -#include <stdlib.h>
1.8 #include <assert.h>
1.9 #include "sm_trie.h"
1.10 -
1.11 -/**
1.12 - * Double Array Trie algorithm, based on:
1.13 - * An Efficient Implementation of Trie Structures, by
1.14 - * Jun-ichi Aoe and Katsushi Maromoto, and Takashi Sato
1.15 - * from Software - Practice and Experience, Vol. 22(9), 695-721 (September 1992)
1.16 - *
1.17 - * A Trie is a simple data structure which stores strings as DFAs, with each
1.18 - * transition state being a string entry. For example, observe the following strings:
1.19 - *
1.20 - * BAILOPAN, BAT, BACON, BACK
1.21 - * These transition as the follow production rules:
1.22 - * B -> ... B
1.23 - * A -> ... BA
1.24 - * I -> ... BAI
1.25 - * LOPAN BAILOPAN
1.26 - * T -> ... BAT
1.27 - * C -> BAC
1.28 - * O -> ... BACO
1.29 - * N BACON
1.30 - * K BACK
1.31 - *
1.32 - * The standard implementation for this - using lists - gives a slow linear lookup, somewhere between
1.33 - * O(N+M) or O(log n). A faster implementation is proposed in the paper above, which is based on compacting
1.34 - * the transition states into two arrays. In the paper's implementation, two arrays are used, and thus it is
1.35 - * called the "Double Array" algorithm. However, the CHECK array's size is maintained the same as BASE,
1.36 - * so they can be combined into one structure. The array seems complex at first, but is very simple: it is a
1.37 - * tree structure flattened out into a single vector. I am calling this implementation the Flat Array Trie.
1.38 - *
1.39 - * BASE[] is an array where each member is a node in the Trie. The node can either be UNUSED (empty), an ARC
1.40 - * (containing an offset to the next set of ARCs), or a TERMINATOR (contains the rest of a string).
1.41 - * Each node has an index which must be interpreted based on the node type. If the node is a TERMINATOR, then the
1.42 - * index is an index into a string table, to find the rest of the string.
1.43 - * If the node is an ARC, the index is another index into BASE. For each possible token that can follow the
1.44 - * current token, the value of those tokens can be added to the index given in the ARC. Thus, given a current
1.45 - * position and the next desired token, the current arc will jump to another arc which can contain either:
1.46 - * 1) An invalid production (collision, no entry exists)
1.47 - * 2) An empty production (no entry exists)
1.48 - * 3) Another arc label (the string ends here or continues into more productions)
1.49 - * 4) A TERMINATOR (the string ends here and contains an unused set of productions)
1.50 - *
1.51 - * So, given current offset N (starting at N=1), jumping to token C means the next offset will be:
1.52 - * offs = BASE[n] + C
1.53 - * Thus, the next node will be at:
1.54 - * BASE[BASE[n] + C]
1.55 - *
1.56 - * This allows each ARC to specify the base offset for any of its ARC children, like a tree. Each node specifies
1.57 - * its parent ARC -- so if an invalid offset is specified, the parent will not match, and thus no such derived
1.58 - * string exists.
1.59 - *
1.60 - * This means that arrays can be laid out "sparsely," maximizing their usage. Note that N need not be related to
1.61 - * the range of tokens (1-256). I.e., a base index does not have to be at 1, 256, 512, et cetera. This is because
1.62 - * insertion comes with a small deal of complexity. To insert a new set of tokens T, the algorithm finds a new
1.63 - * BASE index N such that BASE[N+T[i]] is unused for each T[i]. Thus, indirection is not necessarily linear;
1.64 - * traversing a chain of ARC nodes can _and will_ jump around BASE.
1.65 - *
1.66 - * Of course, given this level of flexibility in the array organization, there are collisions. This is largely
1.67 - * where insertions become slow, as the old chain must be relocated before the new one is used. Relocation means
1.68 - * finding one or more new base indexes, and this means traversing BASE until an acceptable index is found, such
1.69 - * that each offset is unused (see description in previous paragraph).
1.70 - *
1.71 - * However, it is not insertion time we are concerned about. The "trie" name comes from reTRIEval. We are only
1.72 - * concerned with lookup and deletion. Both lookup and deletion are O(k), where k is relative to the length of the
1.73 - * input string. Note that it is best case O(1) and worst case O(k). Deleting the entire trie is always O(1).
1.74 - */
1.75 -
1.76 -/**
1.77 - * Optimization ideas for the future:
1.78 - * 1) Store a reference count for each arc, with the count of sub-children.
1.79 - * This could let us break out of "children searches" for case 4 easily.
1.80 - * 2) Use a 'free list' so we can easily search the trie for free points.
1.81 - * This would drastically speed up x_check*
1.82 - */
1.83 -
1.84 -struct TrieNode
1.85 -{
1.86 - /**
1.87 - * For Node_Arc, this index stores the 'base' offset to the next arc chain.
1.88 - * I.e. to jump from this arc to character C, it will be at base[idx+C].
1.89 - * For Node_Term, this is an index into the string table.
1.90 - */
1.91 - unsigned int idx;
1.92 -
1.93 - /**
1.94 - * This contains the prior arc that we must have come from.
1.95 - * For example, if arc 63 has a base jump of index 12, and we want to see if
1.96 - * there is a valid character C, the parent of 12+C must be 63.
1.97 - */
1.98 - unsigned int parent;
1.99 -
1.100 - void *value; /* Value associated with this node */
1.101 - NodeType mode; /* Current usage type of the node */
1.102 - bool valset; /* Whether or not a value is set */
1.103 -};
1.104 +#include "sm_trie_tpl.h"
1.105
1.106 struct Trie
1.107 {
1.108 - TrieNode *base; /* Base array for the sparse tables */
1.109 - char *stringtab; /* String table pointer */
1.110 - unsigned int baseSize; /* Size of the base array, in members */
1.111 - unsigned int stSize; /* Size of the string table, in bytes */
1.112 - unsigned int tail; /* Current unused offset into the string table */
1.113 + KTrie<void *> k;
1.114 };
1.115
1.116 -inline unsigned char charval(char c)
1.117 -{
1.118 - return (unsigned char)c;
1.119 -}
1.120 -
1.121 -bool sm_trie_grow(Trie *trie)
1.122 -{
1.123 - /* The current # of nodes in the tree is trie->baseSize + 1 */
1.124 - unsigned int curSize = trie->baseSize;
1.125 - unsigned int newSize = curSize * 2;
1.126 -
1.127 - //:TODO: Make functions calling this return failure if this fails
1.128 - trie->base = (TrieNode *)realloc(trie->base, (newSize + 1) * sizeof(TrieNode));
1.129 - if (!trie->base)
1.130 - {
1.131 - return false;
1.132 - }
1.133 -
1.134 - memset(&trie->base[curSize+1], 0, (newSize - curSize) * sizeof(TrieNode));
1.135 - trie->baseSize = newSize;
1.136 -
1.137 - return true;
1.138 -}
1.139 -
1.140 -unsigned int x_check(Trie *trie, char c, unsigned int start=1)
1.141 -{
1.142 - TrieNode *base = trie->base;
1.143 - unsigned char _c = charval(c);
1.144 - unsigned int to_check = trie->baseSize - _c;
1.145 - for (unsigned int i=start; i<=to_check; i++)
1.146 - {
1.147 - if (base[i+_c].mode == Node_Unused)
1.148 - {
1.149 - return i;
1.150 - }
1.151 - }
1.152 -
1.153 - sm_trie_grow(trie);
1.154 -
1.155 - return x_check(trie, c, to_check+1);
1.156 -}
1.157 -
1.158 -unsigned int x_check2(Trie *trie, char c1, char c2, unsigned int start=1)
1.159 -{
1.160 - TrieNode *base = trie->base;
1.161 - unsigned char _c1 = charval(c1);
1.162 - unsigned char _c2 = charval(c2);
1.163 - unsigned int to_check = trie->baseSize - (_c1 > _c2 ? _c1 : _c2);
1.164 - for (unsigned int i=start; i<=to_check; i++)
1.165 - {
1.166 - if (base[i+_c1].mode == Node_Unused
1.167 - && base[i+_c2].mode == Node_Unused)
1.168 - {
1.169 - return i;
1.170 - }
1.171 - }
1.172 -
1.173 - sm_trie_grow(trie);
1.174 -
1.175 - return x_check2(trie, c1, c2, to_check+1);
1.176 -}
1.177 -
1.178 -unsigned int x_check_multi(Trie *trie,
1.179 - unsigned int offsets[],
1.180 - unsigned int count,
1.181 - unsigned int start=1)
1.182 -{
1.183 - TrieNode *base = trie->base;
1.184 - TrieNode *cur;
1.185 - unsigned int to_check = trie->baseSize;
1.186 - unsigned int highest = 0;
1.187 -
1.188 - for (unsigned int i=0; i<count; i++)
1.189 - {
1.190 - if (offsets[i] > highest)
1.191 - {
1.192 - highest = offsets[i];
1.193 - }
1.194 - }
1.195 -
1.196 - to_check -= highest;
1.197 -
1.198 - for (unsigned int i=start; i<=to_check; i++)
1.199 - {
1.200 - bool okay = true;
1.201 - for (unsigned int j=0; j<count; j++)
1.202 - {
1.203 - cur = &base[i+offsets[j]];
1.204 - if (cur->mode != Node_Unused)
1.205 - {
1.206 - okay = false;
1.207 - break;
1.208 - }
1.209 - }
1.210 - if (okay)
1.211 - {
1.212 - return i;
1.213 - }
1.214 - }
1.215 -
1.216 - sm_trie_grow(trie);
1.217 -
1.218 - return x_check_multi(trie, offsets, count, to_check+1);
1.219 -}
1.220 -
1.221 -unsigned int x_addstring(Trie *trie, const char *ptr)
1.222 -{
1.223 - size_t len = strlen(ptr) + 1;
1.224 -
1.225 - if (trie->tail + len >= trie->stSize)
1.226 - {
1.227 - while (trie->tail + len >= trie->stSize)
1.228 - {
1.229 - trie->stSize *= 2;
1.230 - }
1.231 - trie->stringtab = (char *)realloc(trie->stringtab, trie->stSize);
1.232 - }
1.233 -
1.234 - unsigned int tail = trie->tail;
1.235 - strcpy(&trie->stringtab[tail], ptr);
1.236 - trie->tail += len;
1.237 -
1.238 - return tail;
1.239 -}
1.240 -
1.241 Trie *sm_trie_create()
1.242 {
1.243 - Trie *t = new Trie;
1.244 -
1.245 - t->base = (TrieNode *)malloc(sizeof(TrieNode) * (256 + 1));
1.246 - t->stringtab = (char *)malloc(sizeof(char) * 256);
1.247 - t->baseSize = 256;
1.248 - t->stSize = 256;
1.249 -
1.250 - sm_trie_clear(t);
1.251 -
1.252 - return t;
1.253 -}
1.254 -
1.255 -void sm_trie_clear(Trie *trie)
1.256 -{
1.257 - trie->tail = 0;
1.258 -
1.259 - memset(trie->base, 0, sizeof(TrieNode) * (trie->baseSize + 1));
1.260 - memset(trie->stringtab, 0, sizeof(char) * trie->stSize);
1.261 -
1.262 - /* Sentinel root node */
1.263 - trie->base[1].idx = 1;
1.264 - trie->base[1].mode = Node_Arc;
1.265 - trie->base[1].parent = 1;
1.266 + return new Trie;
1.267 }
1.268
1.269 void sm_trie_destroy(Trie *trie)
1.270 {
1.271 - free(trie->base);
1.272 - free(trie->stringtab);
1.273 delete trie;
1.274 }
1.275
1.276 -TrieNode *sm_trie_internal_retrieve(Trie *trie, const char *key)
1.277 +bool sm_trie_insert(Trie *trie, const char *key, void *value)
1.278 {
1.279 - unsigned int lastidx = 1; /* the last node index */
1.280 - unsigned int curidx; /* current node index */
1.281 - const char *keyptr = key; /* input stream at current token */
1.282 - TrieNode *node = NULL; /* current node being processed */
1.283 - TrieNode *base = trie->base;
1.284 -
1.285 - if (!*key)
1.286 - {
1.287 - return NULL;
1.288 - }
1.289 -
1.290 - /* Start traversing at the root node */
1.291 - do
1.292 - {
1.293 - /* Find where the next character is, then advance */
1.294 - curidx = base[lastidx].idx;
1.295 - node = &base[curidx];
1.296 - curidx += charval(*keyptr);
1.297 - node = &base[curidx];
1.298 - keyptr++;
1.299 -
1.300 - /* Check if this slot is supposed to be empty or is a collision */
1.301 - if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx)
1.302 - {
1.303 - return NULL;
1.304 - } else if (node->mode == Node_Term) {
1.305 - char *term = &trie->stringtab[node->idx];
1.306 - if (strcmp(keyptr, term) == 0)
1.307 - {
1.308 - break;
1.309 - }
1.310 - }
1.311 - lastidx = curidx;
1.312 - } while (*keyptr != '\0');
1.313 -
1.314 - return node;
1.315 + return trie->k.insert(key, value);
1.316 }
1.317
1.318 -bool sm_trie_delete(Trie *trie, const char *key)
1.319 +bool sm_trie_replace(Trie *trie, const char *key, void *value)
1.320 {
1.321 - TrieNode *node = sm_trie_internal_retrieve(trie, key);
1.322 - if (!node || !node->valset)
1.323 - {
1.324 - return false;
1.325 - }
1.326 -
1.327 - node->value = NULL;
1.328 - node->valset = false;
1.329 -
1.330 - return true;
1.331 + return trie->k.replace(key, value);
1.332 }
1.333
1.334 bool sm_trie_retrieve(Trie *trie, const char *key, void **value)
1.335 {
1.336 - TrieNode *node = sm_trie_internal_retrieve(trie, key);
1.337 - if (!node || !node->valset)
1.338 + void **pValue = trie->k.retrieve(key);
1.339 +
1.340 + if (!pValue)
1.341 {
1.342 return false;
1.343 }
1.344
1.345 if (value)
1.346 {
1.347 - *value = node->value;
1.348 + *value = *pValue;
1.349 }
1.350
1.351 return true;
1.352 }
1.353
1.354 -bool sm_trie_insert(Trie *trie, const char *key, void *value)
1.355 +bool sm_trie_delete(Trie *trie, const char *key)
1.356 {
1.357 - unsigned int lastidx = 1; /* the last node index */
1.358 - unsigned int curidx; /* current node index */
1.359 - const char *keyptr = key; /* input stream at current token */
1.360 - TrieNode *node = NULL; /* current node being processed */
1.361 - TrieNode *basenode = NULL; /* current base node being processed */
1.362 - unsigned int q; /* temporary var for x_check results */
1.363 - TrieNode *base = trie->base;
1.364 - unsigned int curoffs; /* current offset */
1.365 -
1.366 - /* Do not handle empty strings for simplicity */
1.367 - if (!*key)
1.368 - {
1.369 - return false;
1.370 - }
1.371 -
1.372 - /* Start traversing at the root node (1) */
1.373 - do
1.374 - {
1.375 - /* Find where the next character is, then advance */
1.376 - curidx = base[lastidx].idx;
1.377 - basenode = &base[curidx];
1.378 - curoffs = charval(*keyptr);
1.379 - curidx += curoffs;
1.380 - node = &base[curidx];
1.381 - keyptr++;
1.382 -
1.383 - /* Check if this slot is supposed to be empty. If so, we need to handle CASES 1/2:
1.384 - * Insertion without collisions
1.385 - */
1.386 - if ( (curidx > trie->baseSize) || (node->mode == Node_Unused) )
1.387 - {
1.388 - if (curidx > trie->baseSize)
1.389 - {
1.390 - if (!sm_trie_grow(trie))
1.391 - {
1.392 - return false;
1.393 - }
1.394 - node = &trie->base[curidx];
1.395 - }
1.396 - node->parent = lastidx;
1.397 - if (*keyptr == '\0')
1.398 - {
1.399 - node->mode = Node_Arc;
1.400 - } else {
1.401 - node->idx = x_addstring(trie, keyptr);
1.402 - node->mode = Node_Term;
1.403 - }
1.404 - node->valset = true;
1.405 - node->value = value;
1.406 -
1.407 - return true;
1.408 - } else if (node->parent != lastidx) {
1.409 - /* Collision! We have to split up the tree here. CASE 4:
1.410 - * Insertion when a new word is inserted with a collision.
1.411 - * NOTE: This is the hardest case to handle. All below examples are based on:
1.412 - * BACHELOR, BADGE, inserting BABY.
1.413 - * The problematic production here is A -> B, where B is already being used.
1.414 - *
1.415 - * This process has to rotate one half of the 'A' arc. We generate two lists:
1.416 - * Outgoing Arcs - Anything leaving this 'A'
1.417 - * Incoming Arcs - Anything going to this 'A'
1.418 - * Whichever list is smaller will be moved. Note that this works because the intersection
1.419 - * affects both arc chains, and moving one will make the slot available to either.
1.420 - */
1.421 - TrieNode *cur;
1.422 -
1.423 - /* Find every node arcing from the last node.
1.424 - * I.e. for BACHELOR, BADGE, BABY,
1.425 - * The arcs leaving A will be C and D, but our current node is B -> *.
1.426 - * Thus, we use the last index (A) to find the base for arcs leaving A.
1.427 - */
1.428 - unsigned int outgoing_base = base[lastidx].idx;
1.429 - unsigned int outgoing_list[256];
1.430 - unsigned int outgoing_count = 0; /* count the current index here */
1.431 - cur = &base[outgoing_base] + 1;
1.432 - unsigned int outgoing_limit = 255;
1.433 -
1.434 - if (outgoing_base + outgoing_limit > trie->baseSize)
1.435 - {
1.436 - outgoing_limit = trie->baseSize - outgoing_base;
1.437 - }
1.438 -
1.439 - for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
1.440 - {
1.441 - if (cur->mode == Node_Unused || cur->parent != lastidx)
1.442 - {
1.443 - continue;
1.444 - }
1.445 - outgoing_list[outgoing_count++] = i;
1.446 - }
1.447 - outgoing_list[outgoing_count++] = curidx - outgoing_base;
1.448 -
1.449 - /* Now we need to find all the arcs leaving our parent...
1.450 - * Note: the inconsistency is the base of our parent.
1.451 - */
1.452 - assert(base[node->parent].mode == Node_Arc);
1.453 - unsigned int incoming_list[256];
1.454 - unsigned int incoming_base = base[node->parent].idx;
1.455 - unsigned int incoming_count = 0;
1.456 - unsigned int incoming_limit = 255;
1.457 - cur = &base[incoming_base] + 1;
1.458 -
1.459 - if (incoming_base + incoming_limit > trie->baseSize)
1.460 - {
1.461 - incoming_limit = trie->baseSize - incoming_base;
1.462 - }
1.463 -
1.464 - assert(incoming_limit > 0 && incoming_limit <= 255);
1.465 -
1.466 - for (unsigned int i=1; i<=incoming_limit; i++,cur++)
1.467 - {
1.468 - if (cur->mode == Node_Arc || cur->mode == Node_Term)
1.469 - {
1.470 - if (cur->parent == node->parent)
1.471 - {
1.472 - incoming_list[incoming_count++] = i;
1.473 - }
1.474 - }
1.475 - }
1.476 -
1.477 - if (incoming_count < outgoing_count + 1)
1.478 - {
1.479 - unsigned int q = x_check_multi(trie, incoming_list, incoming_count);
1.480 -
1.481 - base = trie->base;
1.482 - node = &base[curidx];
1.483 -
1.484 - /* If we're incoming, we need to modify our parent */
1.485 - base[node->parent].idx = q;
1.486 -
1.487 - /* For each node in the "to move" list,
1.488 - * Relocate the node's info to the new position.
1.489 - */
1.490 - unsigned int idx, newidx, oldidx;
1.491 - for (unsigned int i=0; i<incoming_count; i++)
1.492 - {
1.493 - idx = incoming_list[i];
1.494 - newidx = q + idx;
1.495 - oldidx = incoming_base + idx;
1.496 - if (oldidx == lastidx)
1.497 - {
1.498 - /* Important! Make sure we're not invalidating our sacred lastidx */
1.499 - lastidx = newidx;
1.500 - }
1.501 - base[newidx] = base[oldidx];
1.502 - assert(base[base[newidx].parent].mode == Node_Arc);
1.503 - memset(&base[oldidx], 0, sizeof(TrieNode));
1.504 - /* If we are not a terminator, we have children we must take care of */
1.505 - if (base[newidx].mode == Node_Arc)
1.506 - {
1.507 - TrieNode *check_base = &base[base[newidx].idx] + 1;
1.508 - outgoing_limit = (base + trie->baseSize + 1) - check_base;
1.509 - if (outgoing_limit > 255)
1.510 - {
1.511 - outgoing_limit = 255;
1.512 - }
1.513 - for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
1.514 - {
1.515 - if (check_base->parent == oldidx)
1.516 - {
1.517 - check_base->parent = newidx;
1.518 - }
1.519 - }
1.520 - }
1.521 - }
1.522 - } else {
1.523 - unsigned int q = x_check_multi(trie, outgoing_list, outgoing_count);
1.524 -
1.525 - base = trie->base;
1.526 - node = &base[curidx];
1.527 -
1.528 - /* If we're outgoing, we need to modify our own base */
1.529 - base[lastidx].idx = q;
1.530 -
1.531 - /* Take the last index (curidx) out of the list. Technically we are not moving this,
1.532 - * since it's already being used by something else.
1.533 - */
1.534 - outgoing_count--;
1.535 -
1.536 - /* For each node in the "to move" list,
1.537 - * Relocate the node's info to the new position.
1.538 - */
1.539 - unsigned int idx, newidx, oldidx;
1.540 - for (unsigned int i=0; i<outgoing_count; i++)
1.541 - {
1.542 - idx = outgoing_list[i];
1.543 - newidx = q + idx;
1.544 - oldidx = outgoing_base + idx;
1.545 - if (oldidx == lastidx)
1.546 - {
1.547 - /* Important! Make sure we're not invalidating our sacred lastidx */
1.548 - lastidx = newidx;
1.549 - }
1.550 - base[newidx] = base[oldidx];
1.551 - assert(base[base[newidx].parent].mode == Node_Arc);
1.552 - memset(&base[oldidx], 0, sizeof(TrieNode));
1.553 - /* If we are not a terminator, we have children we must take care of */
1.554 - if (base[newidx].mode == Node_Arc)
1.555 - {
1.556 - TrieNode *check_base = &base[base[newidx].idx] + 1;
1.557 - outgoing_limit = (base + trie->baseSize + 1) - check_base;
1.558 - if (outgoing_limit > 255)
1.559 - {
1.560 - outgoing_limit = 255;
1.561 - }
1.562 - for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
1.563 - {
1.564 - if (check_base->parent == oldidx)
1.565 - {
1.566 - check_base->parent = newidx;
1.567 - }
1.568 - }
1.569 - }
1.570 - }
1.571 -
1.572 - /* Take the invisible node and use it as our new node */
1.573 - node = &base[q + outgoing_list[outgoing_count]];
1.574 - }
1.575 -
1.576 - /* We're finally done! */
1.577 - node->parent = lastidx;
1.578 - if (*keyptr == '\0')
1.579 - {
1.580 - node->mode = Node_Arc;
1.581 - } else {
1.582 - node->idx = x_addstring(trie, keyptr);
1.583 - node->mode = Node_Term;
1.584 - }
1.585 - node->valset = true;
1.586 - node->value = value;
1.587 -
1.588 - return true;
1.589 - } else {
1.590 - /* See what's in the next node - special case if terminator! */
1.591 - if (node->mode == Node_Term)
1.592 - {
1.593 - /* If we're a terminator, we need to handle CASE 3:
1.594 - * Insertion when a terminating collision occurs
1.595 - */
1.596 - char *term = &trie->stringtab[node->idx];
1.597 - /* Do an initial browsing to make sure they're not the same string */
1.598 - if (strcmp(keyptr, term) == 0)
1.599 - {
1.600 - if (!node->valset)
1.601 - {
1.602 - node->valset = true;
1.603 - node->value = value;
1.604 - return true;
1.605 - }
1.606 - /* Same string. We can't insert. */
1.607 - return false;
1.608 - }
1.609 - /* For each matching character pair, we need to disband the terminator.
1.610 - * This splits the similar prefix into a single arc path.
1.611 - * First, save the old values so we can move them to a new node.
1.612 - * Next, for each loop:
1.613 - * Take the current (invalid) node, and point it to the next arc base.
1.614 - * Set the current node to the node at the next arc.
1.615 - */
1.616 - void *oldvalue = node->value;
1.617 - bool oldvalset = node->valset;
1.618 - if (*term == *keyptr)
1.619 - {
1.620 - while (*term == *keyptr)
1.621 - {
1.622 - /* Find the next free slot in the check array.
1.623 - * This is the "vector base" essentially
1.624 - */
1.625 - q = x_check(trie, *term);
1.626 - base = trie->base;
1.627 - node = &base[curidx];
1.628 - /* Point the node to the next new base */
1.629 - node->idx = q;
1.630 - node->mode = Node_Arc;
1.631 - node->valset = false;
1.632 - /* Advance the input stream and local variables */
1.633 - lastidx = curidx;
1.634 - curidx = q + charval(*term);
1.635 - node = &base[curidx];
1.636 - /* Make sure the new current node has its parent set. */
1.637 - node->parent = lastidx;
1.638 - node->mode = Node_Arc; /* Just in case we run x_check again */
1.639 - *term = '\0'; /* Unmark the string table here */
1.640 - term++;
1.641 - keyptr++;
1.642 - }
1.643 - } else {
1.644 - node->valset = false;
1.645 - }
1.646 - /* We're done inserting new pairs. If one of them is exhausted,
1.647 - * we take special shortcuts.
1.648 - */
1.649 - if (*term == '\0') //EX: BADGERHOUSE added over B -> ADGER.
1.650 - {
1.651 - /* First backpatch the current node - it ends the newly split terminator.
1.652 - * In the example, this would mean the node is the production from R -> ?
1.653 - * This node ends the old BADGER, so we set it here.
1.654 - */
1.655 - node->valset = oldvalset;
1.656 - node->value = oldvalue;
1.657 -
1.658 - /* The terminator was split up, but pieces of keyptr remain.
1.659 - * We need to generate a new production, in this example, R -> H,
1.660 - * with H being a terminator to OUSE. Thus we get:
1.661 - * B,A,D,G,E,R*,H*->OUSE (* = value set).
1.662 - * NOTE: parent was last set at the end of the while loop.
1.663 - */
1.664 - /* Get the new base and apply re-basing */
1.665 - q = x_check(trie, *keyptr);
1.666 - base = trie->base;
1.667 - node = &base[curidx];
1.668 -
1.669 - node->idx = q;
1.670 - node->mode = Node_Arc;
1.671 - lastidx = curidx;
1.672 - /* Finish the final node */
1.673 - curidx = q + charval(*keyptr);
1.674 - node = &trie->base[curidx];
1.675 - keyptr++;
1.676 - /* Optimize - don't add to string table if there's nothing more to eat */
1.677 - if (*keyptr == '\0')
1.678 - {
1.679 - node->mode = Node_Arc;
1.680 - } else {
1.681 - node->idx = x_addstring(trie, keyptr);
1.682 - node->mode = Node_Term;
1.683 - }
1.684 - node->parent = lastidx;
1.685 - node->valset = true;
1.686 - node->value = value;
1.687 - } else if (*keyptr == '\0') { //EX: BADGER added over B -> ADGERHOUSE
1.688 - /* First backpatch the current node - it ends newly split input string.
1.689 - * This is the exact opposite of the above procedure.
1.690 - */
1.691 - node->valset = true;
1.692 - node->value = value;
1.693 -
1.694 - /* Get the new base and apply re-basing */
1.695 - q = x_check(trie, *term);
1.696 - base = trie->base;
1.697 - node = &base[curidx];
1.698 -
1.699 - node->idx = q;
1.700 - node->mode = Node_Arc;
1.701 - lastidx = curidx;
1.702 - /* Finish the final node */
1.703 - curidx = q + charval(*term);
1.704 - node = &trie->base[curidx];
1.705 - term++;
1.706 - /* Optimize - don't add to string table if there's nothing more to eat */
1.707 - if (*term == '\0')
1.708 - {
1.709 - node->mode = Node_Arc;
1.710 - } else {
1.711 - node->idx = (term - trie->stringtab); /* Already in the string table! */
1.712 - node->mode = Node_Term;
1.713 - }
1.714 - node->parent = lastidx;
1.715 - node->valset = oldvalset;
1.716 - node->value = oldvalue;
1.717 - } else {
1.718 - /* Finally, we have to create two new nodes instead of just one. */
1.719 - node->mode = Node_Arc;
1.720 -
1.721 - /* Get the new base and apply re-basing */
1.722 - q = x_check2(trie, *keyptr, *term);
1.723 - base = trie->base;
1.724 - node = &base[curidx];
1.725 -
1.726 - node->idx = q;
1.727 - lastidx = curidx;
1.728 -
1.729 - /* Re-create the old terminated node */
1.730 - curidx = q + charval(*term);
1.731 - node = &trie->base[curidx];
1.732 - term++;
1.733 - node->valset = oldvalset;
1.734 - node->value = oldvalue;
1.735 - node->parent = lastidx;
1.736 - if (*term == '\0')
1.737 - {
1.738 - node->mode = Node_Arc;
1.739 - } else {
1.740 - node->mode = Node_Term;
1.741 - node->idx = (term - trie->stringtab); /* Already in the string table! */
1.742 - }
1.743 -
1.744 - /* Create the new keyed input node */
1.745 - curidx = q + charval(*keyptr);
1.746 - node = &trie->base[curidx];
1.747 - keyptr++;
1.748 - node->valset = true;
1.749 - node->value = value;
1.750 - node->parent = lastidx;
1.751 - if (*keyptr == '\0')
1.752 - {
1.753 - node->mode = Node_Arc;
1.754 - } else {
1.755 - node->mode = Node_Term;
1.756 - node->idx = x_addstring(trie, keyptr);
1.757 - }
1.758 - }
1.759 -
1.760 - /* Phew! */
1.761 - return true;
1.762 - } else {
1.763 - assert(node->mode == Node_Arc);
1.764 - }
1.765 - }
1.766 - lastidx = curidx;
1.767 - } while (*keyptr != '\0');
1.768 -
1.769 - assert(node);
1.770 -
1.771 - /* If we've exhausted the string and we have a valid reached node,
1.772 - * the production rule already existed. Make sure it's valid to set first.
1.773 - */
1.774 -
1.775 - /* We have to be an Arc. If the last result was anything else, we would have returned a new
1.776 - * production earlier.
1.777 - */
1.778 - assert(node->mode == Node_Arc);
1.779 -
1.780 - if (!node->valset)
1.781 - {
1.782 - /* Insert is only possible if we have no production */
1.783 - node->valset = true;
1.784 - node->value = value;
1.785 - return true;
1.786 - }
1.787 -
1.788 - return false;
1.789 + return trie->k.remove(key);
1.790 }
1.791
1.792 -bool sm_trie_replace(Trie *trie, const char *key, void *value)
1.793 +void sm_trie_clear(Trie *trie)
1.794 {
1.795 - TrieNode *node = sm_trie_internal_retrieve(trie, key);
1.796 - if (!node)
1.797 - {
1.798 - return sm_trie_insert(trie, key, value);
1.799 - }
1.800 -
1.801 - node->value = value;
1.802 - node->valset = true;
1.803 -
1.804 - return true;
1.805 + trie->k.clear();
1.806 }