core/sm_trie.cpp
author Scott Ehlert <ds@alliedmods.net>
Thu May 03 03:45:53 2007 +0000 (2007-05-03)
changeset 729 110c14c99545
parent 653 ef60870067ff
child 1057 de136462a416
permissions -rw-r--r--
Various pointless things of no importance:
- Renamed ISourceMod::GetModPath() to GetGamePath()
- Renamed ISourceMod::GetModFolderName() to GetGameFolderName()
- Various spelling corrections (why did I do this?)
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <string.h>
    16 #include <stdlib.h>
    17 #include <assert.h>
    18 #include "sm_trie.h"
    19 
    20 /**
    21  * Double Array Trie algorithm, based on:
    22  * An Efficient Implementation of Trie Structures, by
    23  *  Jun-ichi Aoe and Katsushi Maromoto, and Takashi Sato
    24  * from Software - Practice and Experience, Vol. 22(9), 695-721 (September 1992)
    25  *
    26  *  A Trie is a simple data structure which stores strings as DFAs, with each 
    27  * transition state being a string entry.  For example, observe the following strings:
    28  *
    29  * BAILOPAN, BAT, BACON, BACK
    30  *  These transition as the follow production rules:
    31  *  B -> ...                  B
    32  *       A -> ...             BA
    33  *            I -> ...        BAI
    34  *                 LOPAN      BAILOPAN
    35  *            T -> ...        BAT
    36  *            C ->            BAC
    37  *                 O -> ...   BACO
    38  *                      N     BACON
    39  *                 K          BACK
    40  *
    41  *  The standard implementation for this - using lists - gives a slow linear lookup, somewhere between
    42  * O(N+M) or O(log n).  A faster implementation is proposed in the paper above, which is based on compacting
    43  * the transition states into two arrays.  In the paper's implementation, two arrays are used, and thus it is
    44  * called the "Double Array" algorithm.  However, the CHECK array's size is maintained the same as BASE, 
    45  * so they can be combined into one structure.  The array seems complex at first, but is very simple: it is a
    46  * tree structure flattened out into a single vector.  I am calling this implementation the Flat Array Trie.
    47  *
    48  *  BASE[] is an array where each member is a node in the Trie.  The node can either be UNUSED (empty), an ARC
    49  * (containing an offset to the next set of ARCs), or a TERMINATOR (contains the rest of a string).
    50  * Each node has an index which must be interpreted based on the node type.  If the node is a TERMINATOR, then the
    51  * index is an index into a string table, to find the rest of the string.  
    52  *  If the node is an ARC, the index is another index into BASE.  For each possible token that can follow the
    53  * current token, the value of those tokens can be added to the index given in the ARC.  Thus, given a current
    54  * position and the next desired token, the current arc will jump to another arc which can contain either:
    55  *   1) An invalid production (collision, no entry exists)
    56  *   2) An empty production (no entry exists)
    57  *   3) Another arc label (the string ends here or continues into more productions)
    58  *   4) A TERMINATOR (the string ends here and contains an unused set of productions)
    59  * 
    60  *  So, given current offset N (starting at N=1), jumping to token C means the next offset will be:
    61  *      offs = BASE[n] + C
    62  *  Thus, the next node will be at:
    63  *      BASE[BASE[n] + C]
    64  * 
    65  *  This allows each ARC to specify the base offset for any of its ARC children, like a tree.  Each node specifies
    66  * its parent ARC -- so if an invalid offset is specified, the parent will not match, and thus no such derived 
    67  * string exists.
    68  *
    69  *  This means that arrays can be laid out "sparsely," maximizing their usage.  Note that N need not be related to
    70  * 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
    71  * insertion comes with a small deal of complexity.  To insert a new set of tokens T, the algorithm finds a new 
    72  * BASE index N such that BASE[N+T[i]] is unused for each T[i].  Thus, indirection is not necessarily linear; 
    73  * traversing a chain of ARC nodes can _and will_ jump around BASE.
    74  *
    75  *  Of course, given this level of flexibility in the array organization, there are collisions.  This is largely 
    76  * where insertions become slow, as the old chain must be relocated before the new one is used.  Relocation means 
    77  * finding one or more new base indexes, and this means traversing BASE until an acceptable index is found, such 
    78  * that each offset is unused (see description in previous paragraph).
    79  *
    80  *  However, it is not insertion time we are concerned about.  The "trie" name comes from reTRIEval.  We are only
    81  * concerned with lookup and deletion.  Both lookup and deletion are O(k), where k is relative to the length of the 
    82  * input string.  Note that it is best case O(1) and worst case O(k).  Deleting the entire trie is always O(1).
    83  */
    84 
    85 /**
    86  * Optimization ideas for the future:
    87  * 1) Store a reference count for each arc, with the count of sub-children.
    88  *    This could let us break out of "children searches" for case 4 easily.
    89  * 2) Use a 'free list' so we can easily search the trie for free points.
    90  *    This would drastically speed up x_check*
    91  */
    92 
    93 enum NodeType
    94 {
    95 	Node_Unused = 0,		/* Node is not being used (sparse) */
    96 	Node_Arc,				/* Node is part of an arc and does not terminate */
    97 	Node_Term,				/* Node is a terminator */
    98 };
    99 
   100 struct TrieNode
   101 {
   102 	/**
   103 	 * For Node_Arc, this index stores the 'base' offset to the next arc chain.
   104 	 *   I.e. to jump from this arc to character C, it will be at base[idx+C].
   105 	 * For Node_Term, this is an index into the string table.
   106 	 */
   107 	unsigned int idx;
   108 
   109 	/**
   110 	 * This contains the prior arc that we must have come from.
   111 	 * For example, if arc 63 has a base jump of index 12, and we want to see if
   112 	 * there is a valid character C, the parent of 12+C must be 63.
   113 	 */
   114 	unsigned int parent;
   115 
   116 	void *value;			/* Value associated with this node */
   117 	NodeType mode;			/* Current usage type of the node */
   118 	bool valset;			/* Whether or not a value is set */
   119 };
   120 
   121 struct Trie
   122 {
   123 	TrieNode *base;			/* Base array for the sparse tables */
   124 	char *stringtab;		/* String table pointer */
   125 	unsigned int baseSize;	/* Size of the base array, in members */
   126 	unsigned int stSize;	/* Size of the string table, in bytes */
   127 	unsigned int tail;		/* Current unused offset into the string table */
   128 };
   129 
   130 inline unsigned char charval(char c)
   131 {
   132 	return (unsigned char)c;
   133 }
   134 
   135 bool sm_trie_grow(Trie *trie)
   136 {
   137 	/* The current # of nodes in the tree is trie->baseSize + 1 */
   138 	unsigned int curSize = trie->baseSize;
   139 	unsigned int newSize = curSize * 2;
   140 
   141 	//:TODO: Make functions calling this return failure if this fails 
   142 	trie->base = (TrieNode *)realloc(trie->base, (newSize + 1) * sizeof(TrieNode));
   143 	if (!trie->base)
   144 	{
   145 		return false;
   146 	}
   147 
   148 	memset(&trie->base[curSize+1], 0, (newSize - curSize) * sizeof(TrieNode));
   149 	trie->baseSize = newSize;
   150 
   151 	return true;
   152 }
   153 
   154 unsigned int x_check(Trie *trie, char c, unsigned int start=1)
   155 {
   156 	TrieNode *base = trie->base;
   157 	unsigned char _c = charval(c);
   158 	unsigned int to_check = trie->baseSize - _c;
   159 	for (unsigned int i=start; i<=to_check; i++)
   160 	{
   161 		if (base[i+_c].mode == Node_Unused)
   162 		{
   163 			return i;
   164 		}
   165 	}
   166 
   167 	sm_trie_grow(trie);
   168 
   169 	return x_check(trie, c, to_check+1);
   170 }
   171 
   172 unsigned int x_check2(Trie *trie, char c1, char c2, unsigned int start=1)
   173 {
   174 	TrieNode *base = trie->base;
   175 	unsigned char _c1 = charval(c1);
   176 	unsigned char _c2 = charval(c2);
   177 	unsigned int to_check = trie->baseSize - (_c1 > _c2 ? _c1 : _c2);
   178 	for (unsigned int i=start; i<=to_check; i++)
   179 	{
   180 		if (base[i+_c1].mode == Node_Unused
   181 			&& base[i+_c2].mode == Node_Unused)
   182 		{
   183 			return i;
   184 		}
   185 	}
   186 
   187 	sm_trie_grow(trie);
   188 
   189 	return x_check2(trie, c1, c2, to_check+1);
   190 }
   191 
   192 unsigned int x_check_multi(Trie *trie, 
   193 							unsigned int offsets[], 
   194 							unsigned int count,
   195 							unsigned int start=1)
   196 {
   197 	TrieNode *base = trie->base;
   198 	TrieNode *cur;
   199 	unsigned int to_check = trie->baseSize;
   200 	unsigned int highest = 0;
   201 
   202 	for (unsigned int i=0; i<count; i++)
   203 	{
   204 		if (offsets[i] > highest)
   205 		{
   206 			highest = offsets[i];
   207 		}
   208 	}
   209 
   210 	to_check -= highest;
   211 
   212 	for (unsigned int i=start; i<=to_check; i++)
   213 	{
   214 		bool okay = true;
   215 		for (unsigned int j=0; j<count; j++)
   216 		{
   217 			cur = &base[i+offsets[j]];
   218 			if (cur->mode != Node_Unused)
   219 			{
   220 				okay = false;
   221 				break;
   222 			}
   223 		}
   224 		if (okay)
   225 		{
   226 			return i;
   227 		}
   228 	}
   229 
   230 	sm_trie_grow(trie);
   231 
   232 	return x_check_multi(trie, offsets, count, to_check+1);
   233 }
   234 
   235 unsigned int x_addstring(Trie *trie, const char *ptr)
   236 {
   237 	size_t len = strlen(ptr) + 1;
   238 
   239 	if (trie->tail + len >= trie->stSize)
   240 	{		
   241 		while (trie->tail + len >= trie->stSize)
   242 		{
   243 			trie->stSize *= 2;
   244 		}
   245 		trie->stringtab = (char *)realloc(trie->stringtab, trie->stSize);
   246 	}
   247 
   248 	unsigned int tail = trie->tail;
   249 	strcpy(&trie->stringtab[tail], ptr);
   250 	trie->tail += len;
   251 
   252 	return tail;
   253 }
   254 
   255 Trie *sm_trie_create()
   256 {
   257 	Trie *t = new Trie;
   258 
   259 	t->base = (TrieNode *)malloc(sizeof(TrieNode) * (256 + 1));
   260 	t->stringtab = (char *)malloc(sizeof(char) * 256);
   261 	t->baseSize = 256;
   262 	t->stSize = 256;
   263 
   264 	sm_trie_clear(t);
   265 
   266 	return t;
   267 }
   268 
   269 void sm_trie_clear(Trie *trie)
   270 {
   271 	trie->tail = 0;
   272 
   273 	memset(trie->base, 0, sizeof(TrieNode) * (trie->baseSize + 1));
   274 	memset(trie->stringtab, 0, sizeof(char) * trie->stSize);
   275 
   276 	/* Sentinel root node */
   277 	trie->base[1].idx = 1;
   278 	trie->base[1].mode = Node_Arc;
   279 	trie->base[1].parent = 1;
   280 }
   281 
   282 void sm_trie_destroy(Trie *trie)
   283 {
   284 	free(trie->base);
   285 	free(trie->stringtab);
   286 	delete trie;
   287 }
   288 
   289 bool sm_trie_delete(Trie *trie, const char *key)
   290 {
   291 	unsigned int lastidx = 1;		/* the last node index */
   292 	unsigned int curidx;			/* current node index */
   293 	const char *keyptr = key;		/* input stream at current token */
   294 	TrieNode *node = NULL;			/* current node being processed */
   295 	TrieNode *base = trie->base;
   296 
   297 	if (!*key)
   298 	{
   299 		return false;
   300 	}
   301 
   302 	/* Start traversing at the root node */
   303 	do
   304 	{
   305 		/* Find where the next character is, then advance */
   306 		curidx = base[lastidx].idx;
   307 		node = &base[curidx];
   308 		curidx += charval(*keyptr);
   309 		node = &base[curidx];
   310 		keyptr++;
   311 
   312 		/* Check if this slot is supposed to be empty or is a collision */
   313 		if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx)
   314 		{
   315 			return false;
   316 		} else if (node->mode == Node_Term) {
   317 			char *term = &trie->stringtab[node->idx];
   318 			if (strcmp(keyptr, term) == 0)
   319 			{
   320 				break;
   321 			}
   322 		}
   323 		lastidx = curidx;
   324 	} while (*keyptr != '\0');
   325 
   326 	assert(node != NULL);
   327 
   328 	if (!node->valset)
   329 	{
   330 		return false;
   331 	}
   332 
   333 	node->valset = false;
   334 	node->value = NULL;
   335 
   336 	return true;
   337 }
   338 
   339 bool sm_trie_retrieve(Trie *trie, const char *key, void **value)
   340 {
   341 	unsigned int lastidx = 1;		/* the last node index */
   342 	unsigned int curidx;			/* current node index */
   343 	const char *keyptr = key;		/* input stream at current token */
   344 	TrieNode *node = NULL;			/* current node being processed */
   345 	TrieNode *base = trie->base;
   346 
   347 	if (!*key)
   348 	{
   349 		return false;
   350 	}
   351 
   352 	/* Start traversing at the root node */
   353 	do
   354 	{
   355 		/* Find where the next character is, then advance */
   356 		curidx = base[lastidx].idx;
   357 		node = &base[curidx];
   358 		curidx += charval(*keyptr);
   359 		node = &base[curidx];
   360 		keyptr++;
   361 
   362 		/* Check if this slot is supposed to be empty or is a collision */
   363 		if ((curidx > trie->baseSize) || node->mode == Node_Unused || node->parent != lastidx)
   364 		{
   365 			return false;
   366 		} else if (node->mode == Node_Term) {
   367 			char *term = &trie->stringtab[node->idx];
   368 			if (strcmp(keyptr, term) == 0)
   369 			{
   370 				break;
   371 			}
   372 		}
   373 		lastidx = curidx;
   374 	} while (*keyptr != '\0');
   375 
   376 	assert(node != NULL);
   377 
   378 	if (!node->valset)
   379 	{
   380 		return false;
   381 	}
   382 
   383 	if (value)
   384 	{
   385 		*value = node->value;
   386 	}
   387 
   388 	return true;
   389 }
   390 
   391 bool sm_trie_insert(Trie *trie, const char *key, void *value)
   392 {
   393 	unsigned int lastidx = 1;		/* the last node index */
   394 	unsigned int curidx;			/* current node index */
   395 	const char *keyptr = key;		/* input stream at current token */
   396 	TrieNode *node = NULL;			/* current node being processed */
   397 	TrieNode *basenode = NULL;		/* current base node being processed */
   398 	unsigned int q;					/* temporary var for x_check results */
   399 	TrieNode *base = trie->base;
   400 	unsigned int curoffs;			/* current offset */
   401 
   402 	/* Do not handle empty strings for simplicity */
   403 	if (!*key)
   404 	{
   405 		return false;
   406 	}
   407 
   408 	/* Start traversing at the root node (1) */
   409 	do
   410 	{
   411 		/* Find where the next character is, then advance */
   412 		curidx = base[lastidx].idx;
   413 		basenode = &base[curidx];
   414 		curoffs = charval(*keyptr);
   415 		curidx += curoffs;
   416 		node = &base[curidx];
   417 		keyptr++;
   418 
   419 		/* Check if this slot is supposed to be empty.  If so, we need to handle CASES 1/2:
   420 		 * Insertion without collisions
   421 		 */
   422 		if ( (curidx > trie->baseSize) || (node->mode == Node_Unused) )
   423 		{
   424 			if (curidx > trie->baseSize)
   425 			{
   426 				if (!sm_trie_grow(trie))
   427 				{
   428 					return false;
   429 				}
   430 				node = &trie->base[curidx];
   431 			}
   432 			node->parent = lastidx;
   433 			if (*keyptr == '\0')
   434 			{
   435 				node->mode = Node_Arc;
   436 			} else {
   437 				node->idx = x_addstring(trie, keyptr);
   438 				node->mode = Node_Term;
   439 			}
   440 			node->valset = true;
   441 			node->value = value;
   442 			
   443 			return true;
   444 		} else if (node->parent != lastidx) {
   445 			/* Collision! We have to split up the tree here.  CASE 4:
   446 			 * Insertion when a new word is inserted with a collision.
   447 			 * NOTE: This is the hardest case to handle.  All below examples are based on:
   448 			 * BACHELOR, BADGE, inserting BABY.
   449 			 * The problematic production here is A -> B, where B is already being used.
   450 			 *
   451 			 * This process has to rotate one half of the 'A' arc.  We generate two lists:
   452 			 *  Outgoing Arcs - Anything leaving this 'A'
   453 			 *  Incoming Arcs - Anything going to this 'A'
   454 			 * Whichever list is smaller will be moved.  Note that this works because the intersection
   455 			 * affects both arc chains, and moving one will make the slot available to either.
   456 			 */
   457 			TrieNode *cur;
   458 			
   459 			/* Find every node arcing from the last node.
   460 			 * I.e. for BACHELOR, BADGE, BABY,
   461 			 * The arcs leaving A will be C and D, but our current node is B -> *.
   462 			 * Thus, we use the last index (A) to find the base for arcs leaving A.
   463 			 */
   464 			unsigned int outgoing_base = base[lastidx].idx;
   465 			unsigned int outgoing_list[256];
   466 			unsigned int outgoing_count = 0;	/* count the current index here */
   467 			cur = &base[outgoing_base] + 1;
   468 			unsigned int outgoing_limit = 255;
   469 
   470 			if (outgoing_base + outgoing_limit > trie->baseSize)
   471 			{
   472 				outgoing_limit = trie->baseSize - outgoing_base;
   473 			}
   474 
   475 			for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
   476 			{
   477 				if (cur->mode == Node_Unused || cur->parent != lastidx)
   478 				{
   479 					continue;
   480 				}
   481 				outgoing_list[outgoing_count++] = i;
   482 			}
   483 			outgoing_list[outgoing_count++] = curidx - outgoing_base;
   484 
   485 			/* Now we need to find all the arcs leaving our parent...
   486 			 * Note: the inconsistency is the base of our parent.  
   487 			 */
   488 			assert(base[node->parent].mode == Node_Arc);
   489 			unsigned int incoming_list[256];
   490 			unsigned int incoming_base = base[node->parent].idx;
   491 			unsigned int incoming_count = 0;
   492 			unsigned int incoming_limit = 255;
   493 			cur = &base[incoming_base] + 1;
   494 
   495 			if (incoming_base + incoming_limit > trie->baseSize)
   496 			{
   497 				incoming_limit = trie->baseSize - incoming_base;
   498 			}
   499 
   500 			assert(incoming_limit > 0 && incoming_limit <= 255);
   501 
   502 			for (unsigned int i=1; i<=255; i++,cur++)
   503 			{
   504 				if (cur->mode == Node_Arc || cur->mode == Node_Term)
   505 				{
   506 					if (cur->parent == node->parent)
   507 					{
   508 						incoming_list[incoming_count++] = i;
   509 					}
   510 				}
   511 			}
   512 
   513 			if (incoming_count < outgoing_count + 1)
   514 			{
   515 				unsigned int q = x_check_multi(trie, incoming_list, incoming_count);
   516 
   517 				base = trie->base;
   518 				node = &base[curidx];
   519 
   520 				/* If we're incoming, we need to modify our parent */
   521 				base[node->parent].idx = q;
   522 
   523 				/* For each node in the "to move" list,
   524 				 * Relocate the node's info to the new position.
   525 				 */
   526 				unsigned int idx, newidx, oldidx;
   527 				for (unsigned int i=0; i<incoming_count; i++)
   528 				{
   529 					idx = incoming_list[i];
   530 					newidx = q + idx;
   531 					oldidx = incoming_base + idx;
   532 					if (oldidx == lastidx)
   533 					{
   534 						/* Important! Make sure we're not invalidating our sacred lastidx */
   535 						lastidx = newidx;
   536 					}
   537 					base[newidx] = base[oldidx];
   538 					assert(base[base[newidx].parent].mode == Node_Arc);
   539 					memset(&base[oldidx], 0, sizeof(TrieNode));
   540 					/* If we are not a terminator, we have children we must take care of */
   541 					if (base[newidx].mode == Node_Arc)
   542 					{
   543 						TrieNode *check_base = &base[base[newidx].idx] + 1;
   544 						for (unsigned int j=1; j<=255; j++, check_base++)
   545 						{
   546 							if (check_base->parent == oldidx)
   547 							{
   548 								check_base->parent = newidx;
   549 							}
   550 						}
   551 					}
   552 				}
   553 			} else {
   554 				unsigned int q = x_check_multi(trie, outgoing_list, outgoing_count);
   555 
   556 				base = trie->base;
   557 				node = &base[curidx];
   558 
   559 				/* If we're outgoing, we need to modify our own base */
   560 				base[lastidx].idx = q;
   561 
   562 				/* Take the last index (curidx) out of the list.  Technically we are not moving this,
   563 				 * since it's already being used by something else.  
   564 				 */
   565 				outgoing_count--;
   566 
   567 				/* For each node in the "to move" list,
   568 				 * Relocate the node's info to the new position.
   569 				 */
   570 				unsigned int idx, newidx, oldidx;
   571 				for (unsigned int i=0; i<outgoing_count; i++)
   572 				{
   573 					idx = outgoing_list[i];
   574 					newidx = q + idx;
   575 					oldidx = outgoing_base + idx;
   576 					if (oldidx == lastidx)
   577 					{
   578 						/* Important! Make sure we're not invalidating our sacred lastidx */
   579 						lastidx = newidx;
   580 					}
   581 					base[newidx] = base[oldidx];
   582 					assert(base[base[newidx].parent].mode == Node_Arc);
   583 					memset(&base[oldidx], 0, sizeof(TrieNode));
   584 					/* If we are not a terminator, we have children we must take care of */
   585 					if (base[newidx].mode == Node_Arc)
   586 					{
   587 						TrieNode *check_base = &base[base[newidx].idx] + 1;
   588 						for (unsigned int j=1; j<=255; j++, check_base++)
   589 						{
   590 							if (check_base->parent == oldidx)
   591 							{
   592 								check_base->parent = newidx;
   593 							}
   594 						}
   595 					}
   596 				}
   597 
   598 				/* Take the invisible node and use it as our new node */
   599 				node = &base[q + outgoing_list[outgoing_count]];
   600 			}
   601 
   602 			/* We're finally done! */
   603 			node->parent = lastidx;
   604 			if (*keyptr == '\0')
   605 			{
   606 				node->mode = Node_Arc;
   607 			} else {
   608 				node->idx = x_addstring(trie, keyptr);
   609 				node->mode = Node_Term;
   610 			}
   611 			node->valset = true;
   612 			node->value = value;
   613 
   614 			return true;
   615 		} else {
   616 			/* See what's in the next node - special case if terminator! */
   617 			if (node->mode == Node_Term)
   618 			{
   619 				/* If we're a terminator, we need to handle CASE 3:
   620 				 * Insertion when a terminating collision occurs
   621 				 */
   622 				char *term = &trie->stringtab[node->idx];
   623 				/* Do an initial browsing to make sure they're not the same string */
   624 				if (strcmp(keyptr, term) == 0)
   625 				{
   626 					if (!node->valset)
   627 					{
   628 						node->valset = true;
   629 						node->value = value;
   630 						return true;
   631 					}
   632 					/* Same string.  We can't insert. */
   633 					return false;
   634 				}
   635 				/* For each matching character pair, we need to disband the terminator.
   636 				 * This splits the similar prefix into a single arc path.
   637 				 * First, save the old values so we can move them to a new node.
   638 				 * Next, for each loop:
   639 				 *  Take the current (invalid) node, and point it to the next arc base.
   640 				 *  Set the current node to the node at the next arc.
   641 				 */
   642 				void *oldvalue = node->value;
   643 				bool oldvalset = node->valset;
   644 				if (*term == *keyptr)
   645 				{
   646 				while (*term == *keyptr)
   647 				{
   648 					/* Find the next free slot in the check array.
   649 					 * This is the "vector base" essentially
   650 					 */
   651 					q = x_check(trie, *term);
   652 						base = trie->base;
   653 						node = &base[curidx];
   654 					/* Point the node to the next new base */
   655 					node->idx = q;
   656 					node->mode = Node_Arc;
   657 					node->valset = false;
   658 					/* Advance the input stream and local variables */
   659 					lastidx = curidx;
   660 					curidx = q + charval(*term);
   661 					node = &base[curidx];
   662 					/* Make sure the new current node has its parent set. */
   663 					node->parent = lastidx;
   664 					node->mode = Node_Arc;	/* Just in case we run x_check again */
   665 					*term = '\0';	/* Unmark the string table here */
   666 					term++;
   667 					keyptr++;
   668 					}
   669 				} else {
   670 					node->valset = false;
   671 				}
   672 				/* We're done inserting new pairs.  If one of them is exhausted,
   673 				 * we take special shortcuts.
   674 				 */
   675 				if (*term == '\0')				//EX: BADGERHOUSE added over B -> ADGER.  
   676 				{
   677 					/* First backpatch the current node - it ends the newly split terminator.
   678 					 * In the example, this would mean the node is the production from R -> ?
   679 					 * This node ends the old BADGER, so we set it here.
   680 					 */
   681 					node->valset = oldvalset;
   682 					node->value = oldvalue;
   683 
   684 					/* The terminator was split up, but pieces of keyptr remain.
   685 					 * We need to generate a new production, in this example, R -> H,
   686 					 * with H being a terminator to OUSE.  Thus we get:
   687 					 * B,A,D,G,E,R*,H*->OUSE (* = value set).
   688 					 * NOTE: parent was last set at the end of the while loop.
   689 					 */
   690 					/* Get the new base and apply re-basing */
   691 					q = x_check(trie, *keyptr);
   692 					base = trie->base;
   693 					node = &base[curidx];
   694 
   695 					node->idx = q;
   696 					node->mode = Node_Arc;
   697 					lastidx = curidx;
   698 					/* Finish the final node */
   699 					curidx = q + charval(*keyptr);
   700 					node = &trie->base[curidx];
   701 					keyptr++;
   702 					/* Optimize - don't add to string table if there's nothing more to eat */
   703 					if (*keyptr == '\0')
   704 					{
   705 						node->mode = Node_Arc;
   706 					} else {
   707 						node->idx = x_addstring(trie, keyptr);
   708 						node->mode = Node_Term;
   709 					}
   710 					node->parent = lastidx;
   711 					node->valset = true;
   712 					node->value = value;
   713 				} else if (*keyptr == '\0') {	//EX: BADGER added over B -> ADGERHOUSE
   714 					/* First backpatch the current node - it ends newly split input string.
   715 					 * This is the exact opposite of the above procedure.
   716 					 */
   717 					node->valset = true;
   718 					node->value = value;
   719 
   720 					/* Get the new base and apply re-basing */
   721 					q = x_check(trie, *term);
   722 					base = trie->base;
   723 					node = &base[curidx];
   724 
   725 					node->idx = q;
   726 					node->mode = Node_Arc;
   727 					lastidx = curidx;
   728 					/* Finish the final node */
   729 					curidx = q + charval(*term);
   730 					node = &trie->base[curidx];
   731 					term++;
   732 					/* Optimize - don't add to string table if there's nothing more to eat */
   733 					if (*term == '\0')
   734 					{
   735 						node->mode = Node_Arc;
   736 					} else {
   737 						node->idx = (term - trie->stringtab); /* Already in the string table! */
   738 						node->mode = Node_Term;
   739 					}
   740 					node->parent = lastidx;
   741 					node->valset = oldvalset;
   742 					node->value = oldvalue;
   743 				} else {
   744 					/* Finally, we have to create two new nodes instead of just one. */
   745 					node->mode = Node_Arc;
   746 					
   747 					/* Get the new base and apply re-basing */
   748 					q = x_check2(trie, *keyptr, *term);
   749 					base = trie->base;
   750 					node = &base[curidx];
   751 
   752 					node->idx = q;
   753 					lastidx = curidx;
   754 
   755 					/* Re-create the old terminated node */
   756 					curidx = q + charval(*term);
   757 					node = &trie->base[curidx];
   758 					term++;
   759 					node->valset = oldvalset;
   760 					node->value = oldvalue;
   761 					node->parent = lastidx;
   762 					if (*term == '\0')
   763 					{
   764 						node->mode = Node_Arc;
   765 					} else {
   766 						node->mode = Node_Term;
   767 						node->idx = (term - trie->stringtab); /* Already in the string table! */
   768 					}
   769 
   770 					/* Create the new keyed input node */
   771 					curidx = q + charval(*keyptr);
   772 					node = &trie->base[curidx];
   773 					keyptr++;
   774 					node->valset = true;
   775 					node->value = value;
   776 					node->parent = lastidx;
   777 					if (*keyptr == '\0')
   778 					{
   779 						node->mode = Node_Arc;
   780 					} else {
   781 						node->mode = Node_Term;
   782 						node->idx = x_addstring(trie, keyptr);
   783 					}
   784 				}
   785 
   786 				/* Phew! */
   787 				return true;
   788 			} else {
   789 				assert(node->mode == Node_Arc);
   790 			}
   791 		}
   792 		lastidx = curidx;
   793 	} while (*keyptr != '\0');
   794 
   795 	assert(node);
   796 
   797 	/* If we've exhausted the string and we have a valid reached node,
   798 	 * the production rule already existed.  Make sure it's valid to set first.
   799 	 */
   800 
   801 	/* We have to be an Arc.  If the last result was anything else, we would have returned a new 
   802 	 * production earlier.
   803 	 */
   804 	assert(node->mode == Node_Arc);
   805 
   806 	if (!node->valset)
   807 	{
   808 		/* Insert is only possible if we have no production */
   809 		node->valset = true;
   810 		node->value = value;
   811 		return true;
   812 	}
   813 
   814 	return false;
   815 }