public/sm_trie_tpl.h
author Nicholas Hastings <skamonkey@gmail.com>
Mon Jun 14 09:32:50 2010 -0400 (2010-06-14)
changeset 3041 a0587dee87ae
parent 2471 194be299b700
child 4056 5d02b16b8d9e
permissions -rw-r--r--
Fixed outputname null pointer crash in sdktools (bug 4422, r=fyren)
dvander@1385
     1
/**
dvander@1385
     2
 * vim: set ts=4 :
dvander@1385
     3
 * =============================================================================
dvander@1385
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@1385
     6
 * =============================================================================
dvander@1385
     7
 *
dvander@1385
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@1385
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@1385
    10
 * Free Software Foundation.
dvander@1385
    11
 * 
dvander@1385
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@1385
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@1385
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@1385
    15
 * details.
dvander@1385
    16
 *
dvander@1385
    17
 * You should have received a copy of the GNU General Public License along with
dvander@1385
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1385
    19
 *
dvander@1385
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@1385
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@1385
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@1385
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@1385
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@1385
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@1385
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@1385
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@1385
    28
 *
dvander@1385
    29
 * Version: $Id$
dvander@1385
    30
 */
dvander@1385
    31
dvander@1385
    32
#ifndef _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
dvander@1385
    33
#define _INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_
dvander@1385
    34
dvander@1389
    35
#include <new>
dvander@1389
    36
#include <string.h>
dvander@2471
    37
#include <stdlib.h>
dvander@1389
    38
#include <assert.h>
dvander@1389
    39
dvander@1389
    40
enum NodeType
dvander@1389
    41
{
dvander@1389
    42
	Node_Unused = 0,		/* Node is not being used (sparse) */
dvander@1389
    43
	Node_Arc,				/* Node is part of an arc and does not terminate */
dvander@1389
    44
	Node_Term,				/* Node is a terminator */
dvander@1389
    45
};
dvander@1385
    46
dvander@1385
    47
/**
dvander@1392
    48
 * @brief Trie class for storing key/value pairs, based on double array tries.
dvander@1389
    49
 * @file sm_trie_tpl.h
dvander@1389
    50
 *
dvander@1389
    51
 * For full works cited and implementation overview, there is a big comment 
dvander@1389
    52
 * block at the bottom of this file.
dvander@1385
    53
 */
dvander@1385
    54
dvander@1385
    55
template <typename K>
dvander@1385
    56
class KTrie
dvander@1385
    57
{
dvander@1389
    58
	class KTrieNode;
dvander@1385
    59
public:
dvander@1389
    60
	/**
dvander@1389
    61
	 * @brief Clears all set objects in the trie.
dvander@1389
    62
	 */
dvander@1385
    63
	void clear()
dvander@1385
    64
	{
dvander@1385
    65
		run_destructors();
dvander@1385
    66
		internal_clear();
dvander@1385
    67
	}
dvander@1389
    68
dvander@1389
    69
	/**
dvander@1389
    70
	 * @brief Removes a key from the trie.
dvander@1389
    71
	 *
dvander@1389
    72
	 * @param key		Key to remove.
dvander@1389
    73
	 * @return			True on success, false if key was never set.
dvander@1389
    74
	 */
dvander@1385
    75
	bool remove(const char *key)
dvander@1385
    76
	{
dvander@1385
    77
		KTrieNode *node = internal_retrieve(key);
dvander@1385
    78
		if (!node || !node->valset)
dvander@1385
    79
		{
dvander@1385
    80
			return false;
dvander@1385
    81
		}
dvander@1385
    82
dvander@1385
    83
		node->value.~K();
dvander@1385
    84
		node->valset = false;
dvander@1385
    85
dvander@1829
    86
		m_numElements--;
dvander@1829
    87
dvander@1385
    88
		return true;
dvander@1385
    89
	}
dvander@1389
    90
dvander@1389
    91
	/**
dvander@1389
    92
	 * @brief Retrieves a pointer to the object stored at a given key.
dvander@1389
    93
	 *
dvander@1389
    94
	 * @param key		Key to retrieve.
dvander@1389
    95
	 * @return			Pointer to object, or NULL if key was not found or not set.
dvander@1389
    96
	 */
dvander@1385
    97
	K * retrieve(const char *key)
dvander@1385
    98
	{
dvander@1385
    99
		KTrieNode *node = internal_retrieve(key);
dvander@1385
   100
		if (!node || !node->valset)
dvander@1385
   101
		{
dvander@1385
   102
			return NULL;
dvander@1385
   103
		}
dvander@1385
   104
		return &node->value;
dvander@1385
   105
	}
dvander@1389
   106
dvander@1389
   107
	/**
dvander@1389
   108
	 * @brief Inserts or updates the object stored at a key.
dvander@1389
   109
	 *
dvander@1389
   110
	 * @param key		Key to update or insert.
dvander@1389
   111
	 * @param obj		Object to store at the key.
dvander@1389
   112
	 * @return			True on success, false on failure.
dvander@1389
   113
	 */
dvander@1385
   114
	bool replace(const char *key, const K & obj)
dvander@1385
   115
	{
dvander@1385
   116
		KTrieNode *prev_node = internal_retrieve(key);
dvander@1385
   117
		if (!prev_node)
dvander@1385
   118
		{
dvander@1385
   119
			return insert(key, obj);
dvander@1385
   120
		}
dvander@1385
   121
dvander@1385
   122
		if (prev_node->valset)
dvander@1385
   123
		{
dvander@1385
   124
			prev_node->value.~K();
dvander@1385
   125
		}
dvander@1385
   126
dvander@1385
   127
		new (&prev_node->value) K(obj);
dvander@1385
   128
dvander@1385
   129
		return true;
dvander@1385
   130
	}
dvander@1389
   131
dvander@1389
   132
	/**
dvander@1389
   133
	 * @brief Inserts an object at a key.
dvander@1389
   134
	 *
dvander@1389
   135
	 * @param key		Key to insert at.
dvander@1389
   136
	 * @param obj		Object to store at the key.
dvander@1389
   137
	 * @return			True on success, false if the key is already set or 
dvander@1389
   138
	 *					insertion otherwise failed.
dvander@1389
   139
	 */
dvander@1385
   140
	bool insert(const char *key, const K & obj)
dvander@1385
   141
	{
dvander@1385
   142
		unsigned int lastidx = 1;		/* the last node index */
dvander@1385
   143
		unsigned int curidx;			/* current node index */
dvander@1385
   144
		const char *keyptr = key;		/* input stream at current token */
dvander@1385
   145
		KTrieNode *node = NULL;			/* current node being processed */
dvander@1385
   146
		KTrieNode *basenode = NULL;		/* current base node being processed */
dvander@1385
   147
		unsigned int q;					/* temporary var for x_check results */
dvander@1385
   148
		unsigned int curoffs;			/* current offset */
dvander@1385
   149
dvander@1667
   150
		/**
dvander@1667
   151
		 * Empty strings are a special case, since there are no productions.  We could 
dvander@1667
   152
		 * probably rework it to use BASE[0] but this hack is easier.
dvander@1667
   153
		 */
dvander@1667
   154
		if (*key == '\0')
dvander@1385
   155
		{
dvander@1667
   156
			if (m_empty != NULL && m_empty->valset)
dvander@1667
   157
			{
dvander@1667
   158
				return false;
dvander@1667
   159
			}
dvander@1667
   160
dvander@1667
   161
			if (m_empty == NULL)
dvander@1667
   162
			{
dvander@1667
   163
				m_empty = (KTrieNode *)malloc(sizeof(KTrieNode));
dvander@1667
   164
			}
dvander@1667
   165
dvander@1667
   166
			m_empty->valset = true;
dvander@1667
   167
			new (&m_empty->value) K(obj);
dvander@1667
   168
dvander@1829
   169
			m_numElements++;
dvander@1829
   170
dvander@1667
   171
			return true;
dvander@1385
   172
		}
dvander@1385
   173
dvander@1385
   174
		/* Start traversing at the root node (1) */
dvander@1385
   175
		do
dvander@1385
   176
		{
dvander@1385
   177
			/* Find where the next character is, then advance */
dvander@1385
   178
			curidx = m_base[lastidx].idx;
dvander@1385
   179
			basenode = &m_base[curidx];
dvander@1385
   180
			curoffs = charval(*keyptr);
dvander@1385
   181
			curidx += curoffs;
dvander@1385
   182
			node = &m_base[curidx];
dvander@1385
   183
			keyptr++;
dvander@1385
   184
dvander@1385
   185
			/* Check if this slot is supposed to be empty.  If so, we need to handle CASES 1/2:
dvander@1386
   186
			 * Insertion without collisions
dvander@1386
   187
			 */
dvander@1385
   188
			if ( (curidx > m_baseSize) || (node->mode == Node_Unused) )
dvander@1385
   189
			{
dvander@1385
   190
				if (curidx > m_baseSize)
dvander@1385
   191
				{
dvander@1385
   192
					if (!grow())
dvander@1385
   193
					{
dvander@1385
   194
						return false;
dvander@1385
   195
					}
dvander@1385
   196
					node = &m_base[curidx];
dvander@1385
   197
				}
dvander@1385
   198
				node->parent = lastidx;
dvander@1385
   199
				if (*keyptr == '\0')
dvander@1385
   200
				{
dvander@1385
   201
					node->mode = Node_Arc;
dvander@1579
   202
				}
dvander@1579
   203
				else
dvander@1579
   204
				{
dvander@1385
   205
					node->idx = x_addstring(keyptr);
dvander@1385
   206
					node->mode = Node_Term;
dvander@1385
   207
				}
dvander@1385
   208
				node->valset = true;
dvander@1385
   209
				new (&node->value) K(obj);
dvander@1385
   210
dvander@1829
   211
				m_numElements++;
dvander@1829
   212
dvander@1385
   213
				return true;
dvander@1579
   214
			}
dvander@1579
   215
			else if (node->parent != lastidx)
dvander@1579
   216
			{
dvander@1385
   217
				/* Collision! We have to split up the tree here.  CASE 4:
dvander@1385
   218
				 * Insertion when a new word is inserted with a collision.
dvander@1385
   219
				 * NOTE: This is the hardest case to handle.  All below examples are based on:
dvander@1385
   220
				 * BACHELOR, BADGE, inserting BABY.
dvander@1385
   221
				 * The problematic production here is A -> B, where B is already being used.
dvander@1385
   222
			     *
dvander@1385
   223
				 * This process has to rotate one half of the 'A' arc.  We generate two lists:
dvander@1385
   224
				 *  Outgoing Arcs - Anything leaving this 'A'
dvander@1385
   225
				 *  Incoming Arcs - Anything going to this 'A'
dvander@1385
   226
				 * Whichever list is smaller will be moved.  Note that this works because the intersection
dvander@1385
   227
				 * affects both arc chains, and moving one will make the slot available to either.
dvander@1385
   228
				 */
dvander@1385
   229
				KTrieNode *cur;
dvander@1385
   230
dvander@1385
   231
				/* Find every node arcing from the last node.
dvander@1385
   232
				 * I.e. for BACHELOR, BADGE, BABY,
dvander@1385
   233
				 * The arcs leaving A will be C and D, but our current node is B -> *.
dvander@1385
   234
				 * Thus, we use the last index (A) to find the base for arcs leaving A.
dvander@1385
   235
				 */
dvander@1385
   236
				unsigned int outgoing_base = m_base[lastidx].idx;
dvander@1385
   237
				unsigned int outgoing_list[256];
dvander@1385
   238
				unsigned int outgoing_count = 0;	/* count the current index here */
dvander@1385
   239
				cur = &m_base[outgoing_base] + 1;
dvander@1385
   240
				unsigned int outgoing_limit = 255;
dvander@1385
   241
dvander@1385
   242
				if (outgoing_base + outgoing_limit > m_baseSize)
dvander@1385
   243
				{
dvander@1385
   244
					outgoing_limit = m_baseSize - outgoing_base;
dvander@1385
   245
				}
dvander@1385
   246
dvander@1385
   247
				for (unsigned int i=1; i<=outgoing_limit; i++,cur++)
dvander@1385
   248
				{
dvander@1385
   249
					if (cur->mode == Node_Unused || cur->parent != lastidx)
dvander@1385
   250
					{
dvander@1385
   251
						continue;
dvander@1385
   252
					}
dvander@1385
   253
					outgoing_list[outgoing_count++] = i;
dvander@1385
   254
				}
dvander@1385
   255
				outgoing_list[outgoing_count++] = curidx - outgoing_base;
dvander@1385
   256
dvander@1385
   257
				/* Now we need to find all the arcs leaving our parent...
dvander@1385
   258
				 * Note: the inconsistency is the base of our parent.  
dvander@1385
   259
				 */
dvander@1385
   260
				assert(m_base[node->parent].mode == Node_Arc);
dvander@1385
   261
				unsigned int incoming_list[256];
dvander@1385
   262
				unsigned int incoming_base = m_base[node->parent].idx;
dvander@1385
   263
				unsigned int incoming_count = 0;
dvander@1385
   264
				unsigned int incoming_limit = 255;
dvander@1385
   265
				cur = &m_base[incoming_base] + 1;
dvander@1385
   266
dvander@1385
   267
				if (incoming_base + incoming_limit > m_baseSize)
dvander@1385
   268
				{
dvander@1385
   269
					incoming_limit = m_baseSize - incoming_base;
dvander@1385
   270
				}
dvander@1385
   271
dvander@1385
   272
				assert(incoming_limit > 0 && incoming_limit <= 255);
dvander@1385
   273
dvander@1385
   274
				for (unsigned int i=1; i<=incoming_limit; i++,cur++)
dvander@1385
   275
				{
dvander@1385
   276
					if (cur->mode == Node_Arc || cur->mode == Node_Term)
dvander@1385
   277
					{
dvander@1385
   278
						if (cur->parent == node->parent)
dvander@1385
   279
						{
dvander@1385
   280
							incoming_list[incoming_count++] = i;
dvander@1385
   281
						}
dvander@1385
   282
					}
dvander@1385
   283
				}
dvander@1385
   284
dvander@1385
   285
				if (incoming_count < outgoing_count + 1)
dvander@1385
   286
				{
dvander@1385
   287
					unsigned int q = x_check_multi(incoming_list, incoming_count);
dvander@1385
   288
dvander@1385
   289
					node = &m_base[curidx];
dvander@1385
   290
dvander@1385
   291
					/* If we're incoming, we need to modify our parent */
dvander@1385
   292
					m_base[node->parent].idx = q;
dvander@1385
   293
dvander@1385
   294
					/* For each node in the "to move" list,
dvander@1385
   295
					 * Relocate the node's info to the new position.
dvander@1385
   296
					 */
dvander@1385
   297
					unsigned int idx, newidx, oldidx;
dvander@1385
   298
					for (unsigned int i=0; i<incoming_count; i++)
dvander@1385
   299
					{
dvander@1385
   300
						idx = incoming_list[i];
dvander@1385
   301
						newidx = q + idx;
dvander@1385
   302
						oldidx = incoming_base + idx;
dvander@1385
   303
						if (oldidx == lastidx)
dvander@1385
   304
						{
dvander@1385
   305
							/* Important! Make sure we're not invalidating our sacred lastidx */
dvander@1385
   306
							lastidx = newidx;
dvander@1385
   307
						}
dvander@1385
   308
						/* Fully copy the node */
dvander@1385
   309
						memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
dvander@1385
   310
						if (m_base[oldidx].valset)
dvander@1385
   311
						{
dvander@1385
   312
							new (&m_base[newidx].value) K(m_base[oldidx].value);
dvander@1385
   313
							m_base[oldidx].value.~K();
dvander@1385
   314
						}
dvander@1385
   315
						assert(m_base[m_base[newidx].parent].mode == Node_Arc);
dvander@1385
   316
						/* Erase old data */
dvander@1385
   317
						memset(&m_base[oldidx], 0, sizeof(KTrieNode));
dvander@1385
   318
						/* If we are not a terminator, we have children we must take care of */
dvander@1385
   319
						if (m_base[newidx].mode == Node_Arc)
dvander@1385
   320
						{
dvander@1385
   321
							KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
dvander@1385
   322
							outgoing_limit = (m_base + m_baseSize + 1) - check_base;
dvander@1385
   323
							if (outgoing_limit > 255)
dvander@1385
   324
							{
dvander@1385
   325
								outgoing_limit = 255;
dvander@1385
   326
							}
dvander@1385
   327
							for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
dvander@1385
   328
							{
dvander@1385
   329
								if (check_base->parent == oldidx)
dvander@1385
   330
								{
dvander@1385
   331
									check_base->parent = newidx;
dvander@1385
   332
								}
dvander@1385
   333
							}
dvander@1385
   334
						}
dvander@1385
   335
					}
dvander@1579
   336
				}
dvander@1579
   337
				else
dvander@1579
   338
				{
dvander@1385
   339
					unsigned int q = x_check_multi(outgoing_list, outgoing_count);
dvander@1385
   340
dvander@1385
   341
					node = &m_base[curidx];
dvander@1385
   342
dvander@1385
   343
					/* If we're outgoing, we need to modify our own base */
dvander@1385
   344
					m_base[lastidx].idx = q;
dvander@1385
   345
dvander@1385
   346
					/* Take the last index (curidx) out of the list.  Technically we are not moving this,
dvander@1385
   347
					 * since it's already being used by something else.  
dvander@1385
   348
					 */
dvander@1385
   349
					outgoing_count--;
dvander@1385
   350
dvander@1385
   351
					/* For each node in the "to move" list,
dvander@1385
   352
					 * Relocate the node's info to the new position.
dvander@1385
   353
					 */
dvander@1385
   354
					unsigned int idx, newidx, oldidx;
dvander@1385
   355
					for (unsigned int i=0; i<outgoing_count; i++)
dvander@1385
   356
					{
dvander@1385
   357
						idx = outgoing_list[i];
dvander@1385
   358
						newidx = q + idx;
dvander@1385
   359
						oldidx = outgoing_base + idx;
dvander@1385
   360
						if (oldidx == lastidx)
dvander@1385
   361
						{
dvander@1385
   362
							/* Important! Make sure we're not invalidating our sacred lastidx */
dvander@1385
   363
							lastidx = newidx;
dvander@1385
   364
						}
dvander@1385
   365
						/* Fully copy the node */
dvander@1385
   366
						memcpy(&m_base[newidx], &m_base[oldidx], sizeof(KTrieNode));
dvander@1385
   367
						if (m_base[oldidx].valset)
dvander@1385
   368
						{
dvander@1385
   369
							new (&m_base[newidx].value) K(m_base[oldidx].value);
dvander@1385
   370
							m_base[oldidx].value.~K();
dvander@1385
   371
						}
dvander@1385
   372
						assert(m_base[m_base[newidx].parent].mode == Node_Arc);
dvander@1385
   373
						/* Erase old data */
dvander@1385
   374
						memset(&m_base[oldidx], 0, sizeof(KTrieNode));
dvander@1385
   375
						/* If we are not a terminator, we have children we must take care of */
dvander@1385
   376
						if (m_base[newidx].mode == Node_Arc)
dvander@1385
   377
						{
dvander@1385
   378
							KTrieNode *check_base = &m_base[m_base[newidx].idx] + 1;
dvander@1385
   379
							outgoing_limit = (m_base + m_baseSize + 1) - check_base;
dvander@1385
   380
							if (outgoing_limit > 255)
dvander@1385
   381
							{
dvander@1385
   382
								outgoing_limit = 255;
dvander@1385
   383
							}
dvander@1385
   384
							for (unsigned int j=1; j<=outgoing_limit; j++, check_base++)
dvander@1385
   385
							{
dvander@1385
   386
								if (check_base->parent == oldidx)
dvander@1385
   387
								{
dvander@1385
   388
									check_base->parent = newidx;
dvander@1385
   389
								}
dvander@1385
   390
							}
dvander@1385
   391
						}
dvander@1385
   392
					}
dvander@1385
   393
dvander@1385
   394
					/* Take the invisible node and use it as our new node */
dvander@1385
   395
					node = &m_base[q + outgoing_list[outgoing_count]];
dvander@1385
   396
				}
dvander@1385
   397
dvander@1385
   398
				/* We're finally done! */
dvander@1385
   399
				node->parent = lastidx;
dvander@1385
   400
				if (*keyptr == '\0')
dvander@1385
   401
				{
dvander@1385
   402
					node->mode = Node_Arc;
dvander@1579
   403
				}
dvander@1579
   404
				else
dvander@1579
   405
				{
dvander@1385
   406
					node->idx = x_addstring(keyptr);
dvander@1385
   407
					node->mode = Node_Term;
dvander@1385
   408
				}
dvander@1385
   409
				node->valset = true;
dvander@1385
   410
				new (&node->value) K(obj);
dvander@1385
   411
dvander@1829
   412
				m_numElements++;
dvander@1829
   413
dvander@1385
   414
				return true;
dvander@1579
   415
			}
dvander@1579
   416
			else
dvander@1579
   417
			{
dvander@1385
   418
				/* See what's in the next node - special case if terminator! */
dvander@1385
   419
				if (node->mode == Node_Term)
dvander@1385
   420
				{
dvander@1385
   421
					/* If we're a terminator, we need to handle CASE 3:
dvander@1385
   422
					 * Insertion when a terminating collision occurs
dvander@1385
   423
					 */
dvander@1385
   424
					char *term = &m_stringtab[node->idx];
dvander@1385
   425
					/* Do an initial browsing to make sure they're not the same string */
dvander@1385
   426
					if (strcmp(keyptr, term) == 0)
dvander@1385
   427
					{
dvander@1385
   428
						if (!node->valset)
dvander@1385
   429
						{
dvander@1385
   430
							node->valset = true;
dvander@1385
   431
							new (&node->value) K(obj);
dvander@1829
   432
							m_numElements++;
dvander@1385
   433
							return true;
dvander@1385
   434
						}
dvander@1385
   435
						/* Same string.  We can't insert. */
dvander@1385
   436
						return false;
dvander@1385
   437
					}
dvander@1385
   438
					/* For each matching character pair, we need to disband the terminator.
dvander@1385
   439
					 * This splits the similar prefix into a single arc path.
dvander@1385
   440
					 * First, save the old values so we can move them to a new node.
dvander@1385
   441
					 * Next, for each loop:
dvander@1385
   442
					 *  Take the current (invalid) node, and point it to the next arc base.
dvander@1385
   443
					 *  Set the current node to the node at the next arc.
dvander@1385
   444
					 */
dvander@1385
   445
					K oldvalue;
dvander@1385
   446
					bool oldvalset = node->valset;
dvander@1385
   447
					if (oldvalset)
dvander@1385
   448
					{
dvander@1385
   449
						oldvalue = node->value;
dvander@1385
   450
					}
dvander@1385
   451
					if (*term == *keyptr)
dvander@1385
   452
					{
dvander@1385
   453
						while (*term == *keyptr)
dvander@1385
   454
						{
dvander@1385
   455
							/* Find the next free slot in the check array.
dvander@1385
   456
							 * This is the "vector base" essentially
dvander@1385
   457
							 */
dvander@1385
   458
							q = x_check(*term);
dvander@1385
   459
							node = &m_base[curidx];
dvander@1385
   460
							/* Point the node to the next new base */
dvander@1385
   461
							node->idx = q;
dvander@1385
   462
							node->mode = Node_Arc;
dvander@1385
   463
							if (node->valset == true)
dvander@1385
   464
							{
dvander@1385
   465
								node->value.~K();
dvander@1385
   466
								node->valset = false;
dvander@1385
   467
							}
dvander@1385
   468
							/* Advance the input stream and local variables */
dvander@1385
   469
							lastidx = curidx;
dvander@1385
   470
							curidx = q + charval(*term);
dvander@1385
   471
							node = &m_base[curidx];
dvander@1385
   472
							/* Make sure the new current node has its parent set. */
dvander@1385
   473
							node->parent = lastidx;
dvander@1385
   474
							node->mode = Node_Arc;	/* Just in case we run x_check again */
dvander@1385
   475
							*term = '\0';	/* Unmark the string table here */
dvander@1385
   476
							term++;
dvander@1385
   477
							keyptr++;
dvander@1385
   478
						}
dvander@1579
   479
					}
dvander@1579
   480
					else if (node->valset)
dvander@1579
   481
					{
dvander@1385
   482
						node->valset = false;
dvander@1385
   483
						node->value.~K();
dvander@1385
   484
					}
dvander@1385
   485
					/* We're done inserting new pairs.  If one of them is exhausted,
dvander@1385
   486
					 * we take special shortcuts.
dvander@1385
   487
					 */
dvander@1385
   488
					if (*term == '\0')				//EX: BADGERHOUSE added over B -> ADGER.  
dvander@1385
   489
					{
dvander@1385
   490
						/* First backpatch the current node - it ends the newly split terminator.
dvander@1385
   491
						 * In the example, this would mean the node is the production from R -> ?
dvander@1385
   492
						 * This node ends the old BADGER, so we set it here.
dvander@1385
   493
						 */
dvander@1385
   494
						node->valset = oldvalset;
dvander@1385
   495
						if (node->valset)
dvander@1385
   496
						{
dvander@1385
   497
							new (&node->value) K(oldvalue);
dvander@1385
   498
						}
dvander@1385
   499
dvander@1385
   500
						/* The terminator was split up, but pieces of keyptr remain.
dvander@1385
   501
						 * We need to generate a new production, in this example, R -> H,
dvander@1385
   502
						 * with H being a terminator to OUSE.  Thus we get:
dvander@1385
   503
						 * B,A,D,G,E,R*,H*->OUSE (* = value set).
dvander@1385
   504
						 * NOTE: parent was last set at the end of the while loop.
dvander@1385
   505
						 */
dvander@1385
   506
						/* Get the new base and apply re-basing */
dvander@1385
   507
						q = x_check(*keyptr);
dvander@1385
   508
						node = &m_base[curidx];
dvander@1385
   509
dvander@1385
   510
						node->idx = q;
dvander@1385
   511
						node->mode = Node_Arc;
dvander@1385
   512
						lastidx = curidx;
dvander@1385
   513
						/* Finish the final node */
dvander@1385
   514
						curidx = q + charval(*keyptr);
dvander@1385
   515
						node = &m_base[curidx];
dvander@1385
   516
						keyptr++;
dvander@1385
   517
						/* Optimize - don't add to string table if there's nothing more to eat */
dvander@1385
   518
						if (*keyptr == '\0')
dvander@1385
   519
						{
dvander@1385
   520
							node->mode = Node_Arc;
dvander@1579
   521
						}
dvander@1579
   522
						else
dvander@1579
   523
						{
dvander@1385
   524
							node->idx = x_addstring(keyptr);
dvander@1385
   525
							node->mode = Node_Term;
dvander@1385
   526
						}
dvander@1385
   527
						node->parent = lastidx;
dvander@1385
   528
						node->valset = true;
dvander@1385
   529
						new (&node->value) K(obj);
dvander@1579
   530
					}
dvander@1579
   531
					else if (*keyptr == '\0')
dvander@1579
   532
					{	//EX: BADGER added over B -> ADGERHOUSE
dvander@1385
   533
						/* First backpatch the current node - it ends newly split input string.
dvander@1385
   534
						 * This is the exact opposite of the above procedure.
dvander@1385
   535
						 */
dvander@1385
   536
						node->valset = true;
dvander@1385
   537
						new (&node->value) K(obj);
dvander@1385
   538
dvander@1385
   539
						/* Get the new base and apply re-basing */
dvander@1385
   540
						q = x_check(*term);
dvander@1385
   541
						node = &m_base[curidx];
dvander@1385
   542
dvander@1385
   543
						node->idx = q;
dvander@1385
   544
						node->mode = Node_Arc;
dvander@1385
   545
						lastidx = curidx;
dvander@1385
   546
						/* Finish the final node */
dvander@1385
   547
						curidx = q + charval(*term);
dvander@1385
   548
						node = &m_base[curidx];
dvander@1385
   549
						term++;
dvander@1385
   550
						/* Optimize - don't add to string table if there's nothing more to eat */
dvander@1385
   551
						if (*term == '\0')
dvander@1385
   552
						{
dvander@1385
   553
							node->mode = Node_Arc;
dvander@1579
   554
						}
dvander@1579
   555
						else
dvander@1579
   556
						{
dvander@1385
   557
							node->idx = (term - m_stringtab); /* Already in the string table! */
dvander@1385
   558
							node->mode = Node_Term;
dvander@1385
   559
						}
dvander@1385
   560
						node->parent = lastidx;
dvander@1385
   561
						node->valset = oldvalset;
dvander@1385
   562
						if (node->valset)
dvander@1385
   563
						{
dvander@1385
   564
							new (&node->value) K(oldvalue);
dvander@1385
   565
						}
dvander@1579
   566
					}
dvander@1579
   567
					else
dvander@1579
   568
					{
dvander@1385
   569
						/* Finally, we have to create two new nodes instead of just one. */
dvander@1385
   570
						node->mode = Node_Arc;
dvander@1385
   571
dvander@1385
   572
						/* Get the new base and apply re-basing */
dvander@1385
   573
						q = x_check2(*keyptr, *term);
dvander@1385
   574
						node = &m_base[curidx];
dvander@1385
   575
dvander@1385
   576
						node->idx = q;
dvander@1385
   577
						lastidx = curidx;
dvander@1385
   578
dvander@1385
   579
						/* Re-create the old terminated node */
dvander@1385
   580
						curidx = q + charval(*term);
dvander@1385
   581
						node = &m_base[curidx];
dvander@1385
   582
						term++;
dvander@1385
   583
						node->valset = oldvalset;
dvander@1385
   584
						if (node->valset)
dvander@1385
   585
						{
dvander@1385
   586
							new (&node->value) K(oldvalue);
dvander@1385
   587
						}
dvander@1385
   588
						node->parent = lastidx;
dvander@1385
   589
						if (*term == '\0')
dvander@1385
   590
						{
dvander@1385
   591
							node->mode = Node_Arc;
dvander@1579
   592
						}
dvander@1579
   593
						else
dvander@1579
   594
						{
dvander@1385
   595
							node->mode = Node_Term;
dvander@1385
   596
							node->idx = (term - m_stringtab); /* Already in the string table! */
dvander@1385
   597
						}
dvander@1385
   598
dvander@1385
   599
						/* Create the new keyed input node */
dvander@1385
   600
						curidx = q + charval(*keyptr);
dvander@1385
   601
						node = &m_base[curidx];
dvander@1385
   602
						keyptr++;
dvander@1385
   603
						node->valset = true;
dvander@1385
   604
						new (&node->value) K(obj);
dvander@1385
   605
						node->parent = lastidx;
dvander@1385
   606
						if (*keyptr == '\0')
dvander@1385
   607
						{
dvander@1385
   608
							node->mode = Node_Arc;
dvander@1579
   609
						}
dvander@1579
   610
						else
dvander@1579
   611
						{
dvander@1385
   612
							node->mode = Node_Term;
dvander@1385
   613
							node->idx = x_addstring(keyptr);
dvander@1385
   614
						}
dvander@1385
   615
					}
dvander@1385
   616
dvander@1829
   617
					m_numElements++;
dvander@1829
   618
dvander@1385
   619
					/* Phew! */
dvander@1385
   620
					return true;
dvander@1579
   621
				}
dvander@1579
   622
				else
dvander@1579
   623
				{
dvander@1385
   624
					assert(node->mode == Node_Arc);
dvander@1385
   625
				}
dvander@1385
   626
			}
dvander@1385
   627
			lastidx = curidx;
dvander@1385
   628
		} while (*keyptr != '\0');
dvander@1385
   629
dvander@1385
   630
		assert(node);
dvander@1385
   631
dvander@1385
   632
		/* If we've exhausted the string and we have a valid reached node,
dvander@1389
   633
		 * the production rule already existed.  Make sure it's valid to set first.
dvander@1389
   634
		 */
dvander@1385
   635
dvander@1385
   636
		/* We have to be an Arc.  If the last result was anything else, we would have returned a new 
dvander@1389
   637
		 * production earlier.
dvander@1389
   638
		 */
dvander@1385
   639
		assert(node->mode == Node_Arc);
dvander@1385
   640
dvander@1385
   641
		if (!node->valset)
dvander@1385
   642
		{
dvander@1385
   643
			node->valset = true;
dvander@1385
   644
			new (&node->value) K(obj);
dvander@1829
   645
			m_numElements++;
dvander@1385
   646
			return true;
dvander@1385
   647
		}
dvander@1385
   648
dvander@1385
   649
		return false;
dvander@1385
   650
	}
dvander@1965
   651
dvander@1965
   652
	/** 
dvander@1965
   653
	 * @brief Iterates over the trie returning all known values.  
dvander@1965
   654
	 * 
dvander@1965
   655
	 * Note: This function is for debugging.  Do not use it as a 
dvander@1965
   656
	 * production iterator since it's inefficient.  Iteration is 
dvander@1965
   657
	 * guaranteed to be sorted ascendingly.
dvander@1965
   658
	 *
dvander@1965
   659
	 * The callback function takes:
dvander@1965
   660
	 *  (KTrie)			- Pointer to this Trie
dvander@1965
   661
	 *  (const char *)	- String containing key name.
dvander@1965
   662
	 *  (K &)			- By-reference object at the key.
dvander@1965
   663
	 *  (data)			- User pointer.
dvander@1965
   664
	 *
dvander@1965
   665
	 * @param buffer			Buffer to use as a key name cache.
dvander@1965
   666
	 * @param maxlength			Maximum length of the key name buffer.
dvander@1965
   667
	 * @param data				User pointer for passing to the iterator.
dvander@1965
   668
	 * @param func				Iterator callback function.
dvander@1965
   669
	 */
dvander@1965
   670
	void bad_iterator(char *buffer, 
dvander@1965
   671
		size_t maxlength, 
dvander@1965
   672
		void *data,
dvander@1965
   673
		void (*func)(KTrie *, const char *, K & obj, void *data))
dvander@1965
   674
	{
dvander@1965
   675
		bad_iterator_r(buffer, maxlength, 0, data, func, 1);
dvander@1965
   676
	}
dvander@1965
   677
dvander@1965
   678
private:
dvander@1965
   679
	void bad_iterator_r(char *buffer, 
dvander@1965
   680
		size_t maxlength, 
dvander@1965
   681
		size_t buf_pos,
dvander@1965
   682
		void *data,
dvander@1965
   683
		void (*func)(KTrie *, const char *, K & obj, void *data),
dvander@1965
   684
		unsigned int root)
dvander@1965
   685
	{
dvander@1965
   686
		char *term;
dvander@1965
   687
		unsigned int idx, limit, start;
dvander@1965
   688
dvander@1965
   689
		limit = 255;
dvander@1965
   690
		start = m_base[root].idx;
dvander@1965
   691
dvander@1965
   692
		/* Bound our limits */
dvander@1965
   693
		if (start + limit > m_baseSize)
dvander@1965
   694
		{
dvander@1965
   695
			limit = m_baseSize - start;
dvander@1965
   696
		}
dvander@1965
   697
dvander@1965
   698
		/* Search for strings */
dvander@1965
   699
		for (unsigned int i = 1; i <= limit; i++)
dvander@1965
   700
		{
dvander@1965
   701
			idx = start + i;
dvander@1965
   702
			if (m_base[idx].mode == Node_Unused
dvander@1965
   703
				|| m_base[idx].parent != root)
dvander@1965
   704
			{
dvander@1965
   705
				continue;
dvander@1965
   706
			}
dvander@1965
   707
dvander@1965
   708
			if (m_base[idx].mode == Node_Arc)
dvander@1965
   709
			{
dvander@1965
   710
				if (buf_pos < maxlength - 1)
dvander@1965
   711
				{
dvander@1965
   712
					buffer[buf_pos++] = (char)i;
dvander@1965
   713
				}
dvander@1965
   714
dvander@1965
   715
				if (m_base[idx].valset)
dvander@1965
   716
				{
dvander@1965
   717
					buffer[buf_pos] = '\0';
dvander@1965
   718
					func(this, buffer, m_base[idx].value, data);
dvander@1965
   719
				}
dvander@1965
   720
dvander@1965
   721
				bad_iterator_r(buffer,
dvander@1965
   722
					maxlength,
dvander@1965
   723
					buf_pos,
dvander@1965
   724
					data,
dvander@1965
   725
					func,
dvander@1965
   726
					idx);
dvander@1965
   727
dvander@1965
   728
				buf_pos--;
dvander@1965
   729
			}
dvander@1965
   730
			else if (m_base[idx].mode == Node_Term 
dvander@1965
   731
					 && m_base[idx].valset == true)
dvander@1965
   732
			{
dvander@1965
   733
				size_t save_buf_pos;
dvander@1965
   734
dvander@1965
   735
				save_buf_pos = buf_pos;
dvander@1965
   736
				if (buf_pos < maxlength - 1)
dvander@1965
   737
				{
dvander@1965
   738
					buffer[buf_pos++] = (char)i;
dvander@1965
   739
				}
dvander@1965
   740
				if (buf_pos < maxlength - 1)
dvander@1965
   741
				{
dvander@1965
   742
					size_t destlen, j;
dvander@1965
   743
dvander@1965
   744
					term = &m_stringtab[m_base[idx].idx];
dvander@1965
   745
					destlen = strlen(term);
dvander@1965
   746
					for (j = 0; 
dvander@1965
   747
						 j < destlen && j + buf_pos < maxlength - 1;
dvander@1965
   748
						 j++)
dvander@1965
   749
					{
dvander@1965
   750
						buffer[buf_pos + j] = term[j];
dvander@1965
   751
					}
dvander@1965
   752
					buf_pos += j;
dvander@1965
   753
				}
dvander@1965
   754
				buffer[buf_pos] = '\0';
dvander@1965
   755
dvander@1965
   756
				func(this, buffer, m_base[idx].value, data);
dvander@1965
   757
				
dvander@1965
   758
				buf_pos = save_buf_pos;
dvander@1965
   759
			}
dvander@1965
   760
		}
dvander@1965
   761
	}
dvander@1389
   762
public:
dvander@1389
   763
	KTrie()
dvander@1389
   764
	{
dvander@1389
   765
		m_base = (KTrieNode *)malloc(sizeof(KTrieNode) * (256 + 1));
dvander@1389
   766
		m_stringtab = (char *)malloc(sizeof(char) * 256);
dvander@1389
   767
		m_baseSize = 256;
dvander@1389
   768
		m_stSize = 256;
dvander@1667
   769
		m_empty = NULL;
dvander@1829
   770
		m_numElements = 0;
dvander@1389
   771
dvander@1389
   772
		internal_clear();
dvander@1389
   773
	}
dvander@1389
   774
	~KTrie()
dvander@1389
   775
	{
dvander@1667
   776
		if (m_empty != NULL && m_empty->valset)
dvander@1667
   777
		{
dvander@1667
   778
			m_empty->value.~K();
dvander@1667
   779
			m_empty->valset = false;
dvander@1667
   780
		}
dvander@1667
   781
		free(m_empty);
dvander@1389
   782
		run_destructors();
dvander@1389
   783
		free(m_base);
dvander@1389
   784
		free(m_stringtab);
dvander@1389
   785
	}
dvander@1780
   786
	void run_destructor(void (*dtor)(K * ptr))
dvander@1780
   787
	{
dvander@1780
   788
		for (size_t i = 0; i <= m_baseSize; i++)
dvander@1780
   789
		{
dvander@1780
   790
			if (m_base[i].valset)
dvander@1780
   791
			{
dvander@1780
   792
				dtor(&m_base[i].value);
dvander@1780
   793
				m_base[i].valset = false;
dvander@1780
   794
			}
dvander@1780
   795
		}
dvander@1780
   796
	}
dvander@1389
   797
private:
dvander@1389
   798
	class KTrieNode
dvander@1389
   799
	{
dvander@1389
   800
		friend class KTrie;
dvander@1389
   801
	private:
dvander@1389
   802
		/**
dvander@1389
   803
		 * For Node_Arc, this index stores the 'base' offset to the next arc chain.
dvander@1389
   804
		 *   I.e. to jump from this arc to character C, it will be at base[idx+C].
dvander@1389
   805
		 * For Node_Term, this is an index into the string table.
dvander@1389
   806
		 */
dvander@1389
   807
		unsigned int idx;	
dvander@1389
   808
dvander@1389
   809
		/**
dvander@1389
   810
		 * This contains the prior arc that we must have come from.
dvander@1389
   811
		 * For example, if arc 63 has a base jump of index 12, and we want to see if
dvander@1389
   812
		 * there is a valid character C, the parent of 12+C must be 63.
dvander@1389
   813
		 */
dvander@1389
   814
		unsigned int parent;
dvander@1389
   815
		K value;				/* Value associated with this node */
dvander@1389
   816
		NodeType mode;			/* Current usage type of the node */
dvander@1389
   817
		bool valset;			/* Whether or not a value is set */
dvander@1389
   818
	};
dvander@1385
   819
private:
dvander@1385
   820
	KTrieNode *internal_retrieve(const char *key)
dvander@1385
   821
	{
dvander@1385
   822
		unsigned int lastidx = 1;		/* the last node index */
dvander@1385
   823
		unsigned int curidx;			/* current node index */
dvander@1385
   824
		const char *keyptr = key;		/* input stream at current token */
dvander@1385
   825
		KTrieNode *node = NULL;			/* current node being processed */
dvander@1385
   826
dvander@1385
   827
		if (!*key)
dvander@1385
   828
		{
dvander@1667
   829
			return m_empty;
dvander@1385
   830
		}
dvander@1385
   831
dvander@1385
   832
		/* Start traversing at the root node */
dvander@1385
   833
		do
dvander@1385
   834
		{
dvander@1385
   835
			/* Find where the next character is, then advance */
dvander@1385
   836
			curidx = m_base[lastidx].idx;
dvander@1385
   837
			node = &m_base[curidx];
dvander@1385
   838
			curidx += charval(*keyptr);
dvander@1385
   839
			node = &m_base[curidx];
dvander@1385
   840
			keyptr++;
dvander@1385
   841
dvander@1385
   842
			/* Check if this slot is supposed to be empty or is a collision */
dvander@1385
   843
			if ((curidx > m_baseSize) || node->mode == Node_Unused || node->parent != lastidx)
dvander@1385
   844
			{
dvander@1385
   845
				return NULL;
dvander@1579
   846
			}
dvander@1579
   847
			else if (node->mode == Node_Term)
dvander@1579
   848
			{
dvander@1385
   849
				char *term = &m_stringtab[node->idx];
dvander@1385
   850
				if (strcmp(keyptr, term) == 0)
dvander@1385
   851
				{
dvander@1385
   852
					break;
dvander@1385
   853
				}
dvander@1579
   854
				else
dvander@1579
   855
				{
dvander@1579
   856
					return NULL;
dvander@1579
   857
				}
dvander@1385
   858
			}
dvander@1385
   859
			lastidx = curidx;
dvander@1385
   860
		} while (*keyptr != '\0');
dvander@1385
   861
dvander@1385
   862
		return node;
dvander@1385
   863
	}
dvander@1385
   864
	bool grow()
dvander@1385
   865
	{
dvander@1385
   866
		/* The current # of nodes in the tree is trie->baseSize + 1 */
dvander@1385
   867
		unsigned int cur_size = m_baseSize;
dvander@1385
   868
		unsigned int new_size = cur_size * 2;
dvander@1385
   869
dvander@1385
   870
		KTrieNode *new_base = (KTrieNode *)malloc((new_size + 1) * sizeof(KTrieNode));
dvander@1385
   871
		if (!new_base)
dvander@1385
   872
		{
dvander@1385
   873
			return false;
dvander@1385
   874
		}
dvander@1385
   875
dvander@1389
   876
		memcpy(new_base, m_base, sizeof(KTrieNode) * (m_baseSize + 1));
dvander@1385
   877
		memset(&new_base[cur_size + 1], 0, (new_size - cur_size) * sizeof(KTrieNode));
dvander@1385
   878
dvander@1385
   879
		for (size_t i = 0; i <= m_baseSize; i++)
dvander@1385
   880
		{
dvander@1385
   881
			if (m_base[i].valset)
dvander@1385
   882
			{
dvander@1385
   883
				/* Placement construct+copy the object, then placement destroy the old. */
dvander@1385
   884
				new (&new_base[i].value) K(m_base[i].value);
dvander@1385
   885
				m_base[i].value.~K();
dvander@1385
   886
			}
dvander@1385
   887
		}
dvander@1385
   888
dvander@1385
   889
		free(m_base);
dvander@1385
   890
		m_base = new_base;
dvander@1385
   891
		m_baseSize = new_size;
dvander@1385
   892
dvander@1385
   893
		return true;
dvander@1385
   894
	}
dvander@1385
   895
	inline unsigned char charval(char c)
dvander@1385
   896
	{
dvander@1385
   897
		return (unsigned char)c;
dvander@1385
   898
	}
dvander@1385
   899
	void internal_clear()
dvander@1385
   900
	{
dvander@1385
   901
		m_tail = 0;
dvander@1829
   902
		m_numElements = 0;
dvander@1385
   903
dvander@1385
   904
		memset(m_base, 0, sizeof(KTrieNode) * (m_baseSize + 1));
dvander@1385
   905
		memset(m_stringtab, 0, sizeof(char) * m_stSize);
dvander@1385
   906
dvander@1385
   907
		/* Sentinel root node */
dvander@1385
   908
		m_base[1].idx = 1;
dvander@1385
   909
		m_base[1].mode = Node_Arc;
dvander@1385
   910
		m_base[1].parent = 1;
dvander@1385
   911
	}
dvander@1385
   912
	void run_destructors()
dvander@1385
   913
	{
dvander@1385
   914
		for (size_t i = 0; i <= m_baseSize; i++)
dvander@1385
   915
		{
dvander@1385
   916
			if (m_base[i].valset)
dvander@1385
   917
			{
dvander@1385
   918
				m_base[i].value.~K();
dvander@1385
   919
			}
dvander@1385
   920
		}
dvander@1385
   921
	}
dvander@1385
   922
	unsigned int x_addstring(const char *ptr)
dvander@1385
   923
	{
dvander@1385
   924
		size_t len = strlen(ptr) + 1;
dvander@1385
   925
dvander@1385
   926
		if (m_tail + len >= m_stSize)
dvander@1385
   927
		{		
dvander@1385
   928
			while (m_tail + len >= m_stSize)
dvander@1385
   929
			{
dvander@1385
   930
				m_stSize *= 2;
dvander@1385
   931
			}
dvander@1385
   932
			m_stringtab = (char *)realloc(m_stringtab,m_stSize);
dvander@1385
   933
		}
dvander@1385
   934
dvander@1385
   935
		unsigned int tail = m_tail;
dvander@1385
   936
		strcpy(&m_stringtab[tail], ptr);
dvander@1385
   937
		m_tail += len;
dvander@1385
   938
dvander@1385
   939
		return tail;
dvander@1385
   940
	}
dvander@1385
   941
	unsigned int x_check(char c, unsigned int start=1)
dvander@1385
   942
	{
dvander@1385
   943
		unsigned char _c = charval(c);
dvander@1385
   944
		unsigned int to_check = m_baseSize - _c;
dvander@1385
   945
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   946
		{
dvander@1385
   947
			if (m_base[i+_c].mode == Node_Unused)
dvander@1385
   948
			{
dvander@1385
   949
				return i;
dvander@1385
   950
			}
dvander@1385
   951
		}
dvander@1385
   952
dvander@1385
   953
		grow();
dvander@1385
   954
dvander@1385
   955
		return x_check(c, to_check+1);
dvander@1385
   956
	}
dvander@1385
   957
	unsigned int x_check2(char c1, char c2, unsigned int start=1)
dvander@1385
   958
	{
dvander@1385
   959
		unsigned char _c1 = charval(c1);
dvander@1385
   960
		unsigned char _c2 = charval(c2);
dvander@1385
   961
		unsigned int to_check = m_baseSize - (_c1 > _c2 ? _c1 : _c2);
dvander@1385
   962
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   963
		{
dvander@1385
   964
			if (m_base[i+_c1].mode == Node_Unused
dvander@1385
   965
				&& m_base[i+_c2].mode == Node_Unused)
dvander@1385
   966
			{
dvander@1385
   967
				return i;
dvander@1385
   968
			}
dvander@1385
   969
		}
dvander@1385
   970
dvander@1385
   971
		grow();
dvander@1385
   972
dvander@1385
   973
		return x_check2(c1, c2, to_check+1);
dvander@1385
   974
	}
dvander@1385
   975
	unsigned int x_check_multi(
dvander@1385
   976
		unsigned int offsets[], 
dvander@1385
   977
		unsigned int count,
dvander@1385
   978
		unsigned int start=1)
dvander@1385
   979
	{
dvander@1385
   980
		KTrieNode *cur;
dvander@1385
   981
		unsigned int to_check = m_baseSize;
dvander@1385
   982
		unsigned int highest = 0;
dvander@1385
   983
dvander@1385
   984
		for (unsigned int i=0; i<count; i++)
dvander@1385
   985
		{
dvander@1385
   986
			if (offsets[i] > highest)
dvander@1385
   987
			{
dvander@1385
   988
				highest = offsets[i];
dvander@1385
   989
			}
dvander@1385
   990
		}
dvander@1385
   991
dvander@1385
   992
		to_check -= highest;
dvander@1385
   993
dvander@1385
   994
		for (unsigned int i=start; i<=to_check; i++)
dvander@1385
   995
		{
dvander@1385
   996
			bool okay = true;
dvander@1385
   997
			for (unsigned int j=0; j<count; j++)
dvander@1385
   998
			{
dvander@1385
   999
				cur = &m_base[i+offsets[j]];
dvander@1385
  1000
				if (cur->mode != Node_Unused)
dvander@1385
  1001
				{
dvander@1385
  1002
					okay = false;
dvander@1385
  1003
					break;
dvander@1385
  1004
				}
dvander@1385
  1005
			}
dvander@1385
  1006
			if (okay)
dvander@1385
  1007
			{
dvander@1385
  1008
				return i;
dvander@1385
  1009
			}
dvander@1385
  1010
		}
dvander@1385
  1011
dvander@1385
  1012
		grow();
dvander@1385
  1013
dvander@1385
  1014
		return x_check_multi(offsets, count, to_check+1);
dvander@1385
  1015
	}
dvander@1766
  1016
public:
dvander@1766
  1017
	size_t mem_usage()
dvander@1766
  1018
	{
dvander@1766
  1019
		return (sizeof(KTrieNode) * (m_baseSize))
dvander@1766
  1020
			+ m_stSize
dvander@1766
  1021
			+ sizeof(KTrieNode);
dvander@1766
  1022
	}
dvander@1829
  1023
	size_t size()
dvander@1829
  1024
	{
dvander@1829
  1025
		return m_numElements;
dvander@1829
  1026
	}
dvander@1385
  1027
private:
dvander@1385
  1028
	KTrieNode *m_base;			/* Base array for the sparse tables */
dvander@1667
  1029
	KTrieNode *m_empty;			/* Special case for empty strings */
dvander@1385
  1030
	char *m_stringtab;			/* String table pointer */
dvander@1385
  1031
	unsigned int m_baseSize;	/* Size of the base array, in members */
dvander@1385
  1032
	unsigned int m_stSize;		/* Size of the string table, in bytes */
dvander@1385
  1033
	unsigned int m_tail;		/* Current unused offset into the string table */
dvander@1829
  1034
	size_t m_numElements;		/* Number of elements in use */
dvander@1385
  1035
};
dvander@1385
  1036
dvander@1389
  1037
/**
dvander@1389
  1038
 * Double Array Trie algorithm, based on:
dvander@1389
  1039
 * An Efficient Implementation of Trie Structures, by
dvander@1389
  1040
 *  Jun-ichi Aoe and Katsushi Maromoto, and Takashi Sato
dvander@1389
  1041
 * from Software - Practice and Experience, Vol. 22(9), 695-721 (September 1992)
dvander@1389
  1042
 *
dvander@1389
  1043
 *  A Trie is a simple data structure which stores strings as DFAs, with each 
dvander@1389
  1044
 * transition state being a string entry.  For example, observe the following strings:
dvander@1389
  1045
 *
dvander@1389
  1046
 * BAILOPAN, BAT, BACON, BACK
dvander@1389
  1047
 *  These transition as the follow production rules:
dvander@1389
  1048
 *  B -> ...                  B
dvander@1389
  1049
 *       A -> ...             BA
dvander@1389
  1050
 *            I -> ...        BAI
dvander@1389
  1051
 *                 LOPAN      BAILOPAN
dvander@1389
  1052
 *            T -> ...        BAT
dvander@1389
  1053
 *            C ->            BAC
dvander@1389
  1054
 *                 O -> ...   BACO
dvander@1389
  1055
 *                      N     BACON
dvander@1389
  1056
 *                 K          BACK
dvander@1389
  1057
 *
dvander@1389
  1058
 *  The standard implementation for this - using lists - gives a slow linear lookup, somewhere between
dvander@1389
  1059
 * O(N+M) or O(log n).  A faster implementation is proposed in the paper above, which is based on compacting
dvander@1389
  1060
 * the transition states into two arrays.  In the paper's implementation, two arrays are used, and thus it is
dvander@1389
  1061
 * called the "Double Array" algorithm.  However, the CHECK array's size is maintained the same as BASE, 
dvander@1389
  1062
 * so they can be combined into one structure.  The array seems complex at first, but is very simple: it is a
dvander@1389
  1063
 * tree structure flattened out into a single vector.  I am calling this implementation the Flat Array Trie.
dvander@1389
  1064
 *
dvander@1389
  1065
 *  BASE[] is an array where each member is a node in the Trie.  The node can either be UNUSED (empty), an ARC
dvander@1389
  1066
 * (containing an offset to the next set of ARCs), or a TERMINATOR (contains the rest of a string).
dvander@1389
  1067
 * Each node has an index which must be interpreted based on the node type.  If the node is a TERMINATOR, then the
dvander@1389
  1068
 * index is an index into a string table, to find the rest of the string.  
dvander@1389
  1069
 *  If the node is an ARC, the index is another index into BASE.  For each possible token that can follow the
dvander@1389
  1070
 * current token, the value of those tokens can be added to the index given in the ARC.  Thus, given a current
dvander@1389
  1071
 * position and the next desired token, the current arc will jump to another arc which can contain either:
dvander@1389
  1072
 *   1) An invalid production (collision, no entry exists)
dvander@1389
  1073
 *   2) An empty production (no entry exists)
dvander@1389
  1074
 *   3) Another arc label (the string ends here or continues into more productions)
dvander@1389
  1075
 *   4) A TERMINATOR (the string ends here and contains an unused set of productions)
dvander@1389
  1076
 * 
dvander@1389
  1077
 *  So, given current offset N (starting at N=1), jumping to token C means the next offset will be:
dvander@1389
  1078
 *      offs = BASE[n] + C
dvander@1389
  1079
 *  Thus, the next node will be at:
dvander@1389
  1080
 *      BASE[BASE[n] + C]
dvander@1389
  1081
 * 
dvander@1389
  1082
 *  This allows each ARC to specify the base offset for any of its ARC children, like a tree.  Each node specifies
dvander@1389
  1083
 * its parent ARC -- so if an invalid offset is specified, the parent will not match, and thus no such derived 
dvander@1389
  1084
 * string exists.
dvander@1389
  1085
 *
dvander@1389
  1086
 *  This means that arrays can be laid out "sparsely," maximizing their usage.  Note that N need not be related to
dvander@1389
  1087
 * 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@1389
  1088
 * insertion comes with a small deal of complexity.  To insert a new set of tokens T, the algorithm finds a new 
dvander@1389
  1089
 * BASE index N such that BASE[N+T[i]] is unused for each T[i].  Thus, indirection is not necessarily linear; 
dvander@1389
  1090
 * traversing a chain of ARC nodes can _and will_ jump around BASE.
dvander@1389
  1091
 *
dvander@1389
  1092
 *  Of course, given this level of flexibility in the array organization, there are collisions.  This is largely 
dvander@1389
  1093
 * where insertions become slow, as the old chain must be relocated before the new one is used.  Relocation means 
dvander@1389
  1094
 * finding one or more new base indexes, and this means traversing BASE until an acceptable index is found, such 
dvander@1389
  1095
 * that each offset is unused (see description in previous paragraph).
dvander@1389
  1096
 *
dvander@1389
  1097
 *  However, it is not insertion time we are concerned about.  The "trie" name comes from reTRIEval.  We are only
dvander@1389
  1098
 * concerned with lookup and deletion.  Both lookup and deletion are O(k), where k is relative to the length of the 
dvander@1389
  1099
 * input string.  Note that it is best case O(1) and worst case O(k).  Deleting the entire trie is always O(1).
dvander@1389
  1100
 */
dvander@1389
  1101
dvander@1385
  1102
#endif //_INCLUDE_SOURCEMOD_TEMPLATED_TRIE_H_