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