core/logic/smn_adt_trie.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Feb 09 22:50:20 2014 -0600 (2014-02-09)
changeset 4241 46fdb8cd9883
parent 4096 1ed7fe4e8f7e
child 4340 c6e0fecd7f42
permissions -rw-r--r--
Use == instead of is operator for checking string equality in AMBuild scripts (bug 6043, r=dvander).
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <stdlib.h>
    33 #include "common_logic.h"
    34 #include <am-moveable.h>
    35 #include <am-refcounting.h>
    36 #include <sm_stringhashmap.h>
    37 #include "sm_memtable.h"
    38 
    39 HandleType_t htCellTrie;
    40 HandleType_t htSnapshot;
    41 
    42 enum EntryType
    43 {
    44 	EntryType_Cell,
    45 	EntryType_CellArray,
    46 	EntryType_String,
    47 };
    48 
    49 class Entry
    50 {
    51 	struct ArrayInfo
    52 	{
    53 		size_t length;
    54 		size_t maxbytes;
    55 
    56 		void *base() {
    57 			return this + 1;
    58 		}
    59 	};
    60 
    61 public:
    62 	Entry()
    63 		: control_(0)
    64 	{
    65 	}
    66 	Entry(ke::Moveable<Entry> other)
    67 	{
    68 		control_ = other->control_;
    69 		data_ = other->data_;
    70 		other->control_ = 0;
    71 	}
    72 
    73 	~Entry()
    74 	{
    75 		free(raw());
    76 	}
    77 
    78 	void setCell(cell_t value) {
    79 		setType(EntryType_Cell);
    80 		data_ = value;
    81 	}
    82 	void setArray(cell_t *cells, size_t length) {
    83 		ArrayInfo *array = ensureArray(length * sizeof(cell_t));
    84 		array->length = length;
    85 		memcpy(array->base(), cells, length * sizeof(cell_t));
    86 		setTypeAndPointer(EntryType_CellArray, array);
    87 	}
    88 	void setString(const char *str) {
    89 		size_t length = strlen(str);
    90 		ArrayInfo *array = ensureArray(length + 1);
    91 		array->length = length;
    92 		strcpy((char *)array->base(), str);
    93 		setTypeAndPointer(EntryType_String, array);
    94 	}
    95 
    96 	size_t arrayLength() const {
    97 		assert(isArray());
    98 		return raw()->length;
    99 	}
   100 	cell_t *array() const {
   101 		assert(isArray());
   102 		return reinterpret_cast<cell_t *>(raw()->base());
   103 	}
   104 	char *chars() const {
   105 		assert(isString());
   106 		return reinterpret_cast<char *>(raw()->base());
   107 	}
   108 	cell_t cell() const {
   109 		assert(isCell());
   110 		return data_;
   111 	}
   112 
   113 	bool isCell() const {
   114 		return type() == EntryType_Cell;
   115 	}
   116 	bool isArray() const {
   117 		return type() == EntryType_CellArray;
   118 	}
   119 	bool isString() const {
   120 		return type() == EntryType_String;
   121 	}
   122 
   123 private:
   124 	Entry(const Entry &other) KE_DELETE;
   125 
   126 	ArrayInfo *ensureArray(size_t bytes) {
   127 		ArrayInfo *array = raw();
   128 		if (array && array->maxbytes >= bytes)
   129 			return array;
   130 		array = (ArrayInfo *)realloc(array, bytes + sizeof(ArrayInfo));
   131 		if (!array)
   132 		{
   133 			fprintf(stderr, "Out of memory!\n");
   134 			abort();
   135 		}
   136 		array->maxbytes = bytes;
   137 		return array;
   138 	}
   139 
   140 	// Pointer and type are overlaid, so we have some accessors.
   141 	ArrayInfo *raw() const {
   142 		return reinterpret_cast<ArrayInfo *>(control_ & ~uintptr_t(0x3));
   143 	}
   144 	void setType(EntryType aType) {
   145 		control_ = uintptr_t(raw()) | uintptr_t(aType);
   146 		assert(type() == aType);
   147 	}
   148 	void setTypeAndPointer(EntryType aType, ArrayInfo *ptr) {
   149 		// malloc() should guarantee 8-byte alignment at worst
   150 		assert((uintptr_t(ptr) & 0x3) == 0);
   151 		control_ = uintptr_t(ptr) | uintptr_t(aType);
   152 		assert(type() == aType);
   153 	}
   154 	EntryType type() const {
   155 		return (EntryType)(control_ & 0x3);
   156 	}
   157 
   158 private:
   159 	// Contains the bits for the type, and an array pointer, if one is set.
   160 	uintptr_t control_;
   161 
   162 	// Contains data for cell-only entries.
   163 	cell_t data_;
   164 };
   165 
   166 struct CellTrie : public ke::Refcounted<CellTrie>
   167 {
   168 	StringHashMap<Entry> map;
   169 };
   170 
   171 struct TrieSnapshot
   172 {
   173 	TrieSnapshot()
   174 		: strings(128)
   175 	{ }
   176 
   177 	size_t mem_usage()
   178 	{
   179 		return length * sizeof(int) + strings.GetMemTable()->GetMemUsage();
   180 	}
   181 
   182 	size_t length;
   183 	ke::AutoArray<int> keys;
   184 	BaseStringTable strings;
   185 };
   186 
   187 class TrieHelpers : 
   188 	public SMGlobalClass,
   189 	public IHandleTypeDispatch
   190 {
   191 public: //SMGlobalClass
   192 	void OnSourceModAllInitialized()
   193 	{
   194 		htCellTrie = handlesys->CreateType("Trie", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   195 		htSnapshot = handlesys->CreateType("TrieSnapshot", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   196 	}
   197 	void OnSourceModShutdown()
   198 	{
   199 		handlesys->RemoveType(htSnapshot, g_pCoreIdent);
   200 		handlesys->RemoveType(htCellTrie, g_pCoreIdent);
   201 	}
   202 public: //IHandleTypeDispatch
   203 	void OnHandleDestroy(HandleType_t type, void *object)
   204 	{
   205 		if (type == htCellTrie)
   206 		{
   207 			CellTrie *pTrie = (CellTrie *)object;
   208 			pTrie->Release();
   209 		} else {
   210 			TrieSnapshot *snapshot = (TrieSnapshot *)object;
   211 			delete snapshot;
   212 		}
   213 	}
   214 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   215 	{
   216 		if (type == htCellTrie)
   217 		{
   218 			CellTrie *pArray = (CellTrie *)object;
   219 			*pSize = sizeof(CellTrie) + pArray->map.mem_usage();
   220 		} else {
   221 			TrieSnapshot *snapshot = (TrieSnapshot *)object;
   222 			*pSize = sizeof(TrieSnapshot) + snapshot->mem_usage();
   223 		}
   224 		return true;
   225 	}
   226 } s_CellTrieHelpers;
   227 
   228 static cell_t CreateTrie(IPluginContext *pContext, const cell_t *params)
   229 {
   230 	CellTrie *pTrie = new CellTrie;
   231 	Handle_t hndl;
   232 
   233 	if ((hndl = handlesys->CreateHandle(htCellTrie, pTrie, pContext->GetIdentity(), g_pCoreIdent, NULL))
   234 		== BAD_HANDLE)
   235 	{
   236 		delete pTrie;
   237 		return BAD_HANDLE;
   238 	}
   239 
   240 	return hndl;
   241 }
   242 
   243 static cell_t SetTrieValue(IPluginContext *pContext, const cell_t *params)
   244 {
   245 	CellTrie *pTrie;
   246 	HandleError err;
   247 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   248 
   249 	Handle_t hndl = params[1];
   250 
   251 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   252 		!= HandleError_None)
   253 	{
   254 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   255 	}
   256 
   257 	char *key;
   258 	pContext->LocalToString(params[2], &key);
   259 
   260 	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   261 	if (!i.found())
   262 	{
   263 		if (!pTrie->map.add(i, key))
   264 			return 0;
   265 		i->value.setCell(params[3]);
   266 		return 1;
   267 	}
   268 
   269 	if (!params[4])
   270 		return 0;
   271 
   272 	i->value.setCell(params[3]);
   273 	return 1;
   274 }
   275 
   276 static cell_t SetTrieArray(IPluginContext *pContext, const cell_t *params)
   277 {
   278 	CellTrie *pTrie;
   279 	HandleError err;
   280 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   281 
   282 	Handle_t hndl = params[1];
   283 
   284 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   285 		!= HandleError_None)
   286 	{
   287 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   288 	}
   289 
   290 	if (params[4] < 0)
   291 	{
   292 		return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
   293 	}
   294 
   295 	char *key;
   296 	cell_t *array;
   297 	pContext->LocalToString(params[2], &key);
   298 	pContext->LocalToPhysAddr(params[3], &array);
   299 
   300 	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   301 	if (!i.found())
   302 	{
   303 		if (!pTrie->map.add(i, key))
   304 			return 0;
   305 		i->key = key;
   306 		i->value.setArray(array, params[4]);
   307 		return 1;
   308 	}
   309 
   310 	if (!params[4])
   311 		return 0;
   312 
   313 	i->value.setArray(array, params[4]);
   314 	return 1;
   315 }
   316 
   317 static cell_t SetTrieString(IPluginContext *pContext, const cell_t *params)
   318 {
   319 	CellTrie *pTrie;
   320 	HandleError err;
   321 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   322 
   323 	Handle_t hndl = params[1];
   324 
   325 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   326 		!= HandleError_None)
   327 	{
   328 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   329 	}
   330 
   331 	char *key, *val;
   332 	pContext->LocalToString(params[2], &key);
   333 	pContext->LocalToString(params[3], &val);
   334 
   335 	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   336 	if (!i.found())
   337 	{
   338 		if (!pTrie->map.add(i, key))
   339 			return 0;
   340 		i->value.setString(val);
   341 		return 1;
   342 	}
   343 
   344 	if (!params[4])
   345 		return 0;
   346 
   347 	i->value.setString(val);
   348 	return 1;
   349 }
   350 
   351 static cell_t RemoveFromTrie(IPluginContext *pContext, const cell_t *params)
   352 {
   353 	CellTrie *pTrie;
   354 	HandleError err;
   355 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   356 
   357 	Handle_t hndl = params[1];
   358 
   359 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   360 		!= HandleError_None)
   361 	{
   362 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   363 	}
   364 
   365 	char *key;
   366 	pContext->LocalToString(params[2], &key);
   367 
   368 	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   369 	if (!r.found())
   370 		return 0;
   371 
   372 	pTrie->map.remove(r);
   373 	return 1;
   374 }
   375 
   376 static cell_t ClearTrie(IPluginContext *pContext, const cell_t *params)
   377 {
   378 	Handle_t hndl;
   379 	CellTrie *pTrie;
   380 	HandleError err;
   381 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   382 
   383 	hndl = params[1];
   384 
   385 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   386 		!= HandleError_None)
   387 	{
   388 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   389 	}
   390 
   391 	pTrie->map.clear();
   392 	return 1;
   393 }
   394 
   395 static cell_t GetTrieValue(IPluginContext *pContext, const cell_t *params)
   396 {
   397 	Handle_t hndl;
   398 	CellTrie *pTrie;
   399 	HandleError err;
   400 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   401 
   402 	hndl = params[1];
   403 
   404 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   405 		!= HandleError_None)
   406 	{
   407 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   408 	}
   409 
   410 	char *key;
   411 	cell_t *pValue;
   412 	pContext->LocalToString(params[2], &key);
   413 	pContext->LocalToPhysAddr(params[3], &pValue);
   414 
   415 	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   416 	if (!r.found())
   417 		return 0;
   418 
   419 	if (r->value.isCell())
   420 	{
   421 		*pValue = r->value.cell();
   422 		return 1;
   423 	}
   424 
   425 	// Maintain compatibility with an old bug. If an array was set with one
   426 	// cell, it was stored internally as a single cell. We now store as an
   427 	// actual array, but we make GetTrieValue() still work for this case.
   428 	if (r->value.isArray() && r->value.arrayLength() == 1)
   429 	{
   430 		*pValue = r->value.array()[0];
   431 		return 1;
   432 	}
   433 	
   434 	return 0;
   435 }
   436 
   437 static cell_t GetTrieArray(IPluginContext *pContext, const cell_t *params)
   438 {
   439 	Handle_t hndl;
   440 	CellTrie *pTrie;
   441 	HandleError err;
   442 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   443 
   444 	hndl = params[1];
   445 
   446 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   447 		!= HandleError_None)
   448 	{
   449 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   450 	}
   451 
   452 	if (params[4] < 0)
   453 	{
   454 		return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
   455 	}
   456 
   457 	char *key;
   458 	cell_t *pValue, *pSize;
   459 	pContext->LocalToString(params[2], &key);
   460 	pContext->LocalToPhysAddr(params[3], &pValue);
   461 	pContext->LocalToPhysAddr(params[5], &pSize);
   462 
   463 
   464 	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   465 	if (!r.found() || !r->value.isArray())
   466 		return 0;
   467 
   468 	if (!r->value.array())
   469 	{
   470 		*pSize = 0;
   471 		return 1;
   472 	}
   473 
   474 	if (!params[4])
   475 		return 1;
   476 
   477 	size_t length = r->value.arrayLength();
   478 	cell_t *base = r->value.array();
   479 
   480 	if (length > size_t(params[4]))
   481 		*pSize = params[4];
   482 	else
   483 		*pSize = length;
   484 
   485 	memcpy(pValue, base, sizeof(cell_t) * pSize[0]);
   486 	return 1;
   487 }
   488 
   489 static cell_t GetTrieString(IPluginContext *pContext, const cell_t *params)
   490 {
   491 	Handle_t hndl;
   492 	CellTrie *pTrie;
   493 	HandleError err;
   494 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   495 
   496 	hndl = params[1];
   497 
   498 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   499 		!= HandleError_None)
   500 	{
   501 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   502 	}
   503 
   504 	if (params[4] < 0)
   505 	{
   506 		return pContext->ThrowNativeError("Invalid buffer size: %d", params[4]);
   507 	}
   508 
   509 	char *key;
   510 	cell_t *pSize;
   511 	pContext->LocalToString(params[2], &key);
   512 	pContext->LocalToPhysAddr(params[5], &pSize);
   513 
   514 	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   515 	if (!r.found() || !r->value.isString())
   516 		return 0;
   517 
   518 	size_t written;
   519 	pContext->StringToLocalUTF8(params[3], params[4], r->value.chars(), &written);
   520 
   521 	*pSize = (cell_t)written;
   522 	return 1;
   523 }
   524 
   525 static cell_t GetTrieSize(IPluginContext *pContext, const cell_t *params)
   526 {
   527 	Handle_t hndl;
   528 	CellTrie *pTrie;
   529 	HandleError err;
   530 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   531 
   532 	hndl = params[1];
   533 
   534 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   535 		!= HandleError_None)
   536 	{
   537 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   538 	}
   539 
   540 	return pTrie->map.elements();
   541 }
   542 
   543 static cell_t CreateTrieSnapshot(IPluginContext *pContext, const cell_t *params)
   544 {
   545 	HandleError err;
   546 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   547 
   548 	Handle_t hndl = params[1];
   549 
   550 	CellTrie *pTrie;
   551 	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   552 		!= HandleError_None)
   553 	{
   554 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   555 	}
   556 
   557 	TrieSnapshot *snapshot = new TrieSnapshot;
   558 	snapshot->length = pTrie->map.elements();
   559 	snapshot->keys = new int[snapshot->length];
   560 	size_t i = 0;
   561 	for (StringHashMap<Entry>::iterator iter = pTrie->map.iter(); !iter.empty(); iter.next(), i++)
   562 		snapshot->keys[i] = snapshot->strings.AddString(iter->key.chars(), iter->key.length());
   563 	assert(i == snapshot->length);
   564 
   565 	if ((hndl = handlesys->CreateHandle(htSnapshot, snapshot, pContext->GetIdentity(), g_pCoreIdent, NULL))
   566 		== BAD_HANDLE)
   567 	{
   568 		delete snapshot;
   569 		return BAD_HANDLE;
   570 	}
   571 
   572 	return hndl;
   573 }
   574 
   575 static cell_t TrieSnapshotLength(IPluginContext *pContext, const cell_t *params)
   576 {
   577 	HandleError err;
   578 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   579 
   580 	Handle_t hndl = params[1];
   581 
   582 	TrieSnapshot *snapshot;
   583 	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   584 		!= HandleError_None)
   585 	{
   586 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   587 	}
   588 
   589 	return snapshot->length;
   590 }
   591 
   592 static cell_t TrieSnapshotKeyBufferSize(IPluginContext *pContext, const cell_t *params)
   593 {
   594 	HandleError err;
   595 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   596 
   597 	Handle_t hndl = params[1];
   598 
   599 	TrieSnapshot *snapshot;
   600 	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   601 		!= HandleError_None)
   602 	{
   603 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   604 	}
   605 
   606 	unsigned index = params[2];
   607 	if (index >= snapshot->length)
   608 		return pContext->ThrowNativeError("Invalid index %d", index);
   609 
   610 	return strlen(snapshot->strings.GetString(snapshot->keys[index])) + 1;
   611 }
   612 
   613 static cell_t GetTrieSnapshotKey(IPluginContext *pContext, const cell_t *params)
   614 {
   615 	HandleError err;
   616 	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   617 
   618 	Handle_t hndl = params[1];
   619 
   620 	TrieSnapshot *snapshot;
   621 	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   622 		!= HandleError_None)
   623 	{
   624 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   625 	}
   626 
   627 	unsigned index = params[2];
   628 	if (index >= snapshot->length)
   629 		return pContext->ThrowNativeError("Invalid index %d", index);
   630 
   631 	size_t written;
   632 	const char *str = snapshot->strings.GetString(snapshot->keys[index]);
   633 	pContext->StringToLocalUTF8(params[3], params[4], str, &written);
   634 	return written;
   635 }
   636 
   637 REGISTER_NATIVES(trieNatives)
   638 {
   639 	{"ClearTrie",				ClearTrie},
   640 	{"CreateTrie",				CreateTrie},
   641 	{"GetTrieArray",			GetTrieArray},
   642 	{"GetTrieString",			GetTrieString},
   643 	{"GetTrieValue",			GetTrieValue},
   644 	{"RemoveFromTrie",			RemoveFromTrie},
   645 	{"SetTrieArray",			SetTrieArray},
   646 	{"SetTrieString",			SetTrieString},
   647 	{"SetTrieValue",			SetTrieValue},
   648 	{"GetTrieSize",				GetTrieSize},
   649 	{"CreateTrieSnapshot",		CreateTrieSnapshot},
   650 	{"TrieSnapshotLength",		TrieSnapshotLength},
   651 	{"TrieSnapshotKeyBufferSize", TrieSnapshotKeyBufferSize},
   652 	{"GetTrieSnapshotKey",		GetTrieSnapshotKey},
   653 	{NULL,						NULL},
   654 };