Refactor Trie natives to use HashMap instead of KTrie; add iteration API (bug 5892, r=ds).
authorDavid Anderson <dvander@alliedmods.net>
Thu Aug 29 10:09:02 2013 -0700 (2013-08-29)
changeset 40961ed7fe4e8f7e
parent 4095 b63c762918b0
child 4097 92a2ef02f407
Refactor Trie natives to use HashMap instead of KTrie; add iteration API (bug 5892, r=ds).
core/logic/sm_memtable.h
core/logic/smn_adt_trie.cpp
core/sm_memtable.h
plugins/include/adt_trie.inc
plugins/testsuite/tries.sp
public/amtl/am-hashmap.h
public/amtl/am-hashtable.h
public/amtl/am-moveable.h
public/amtl/am-string.h
public/amtl/am-vector.h
public/sm_stringhashmap.h
     1.1 --- a/core/logic/sm_memtable.h	Thu Aug 29 11:11:41 2013 -0400
     1.2 +++ b/core/logic/sm_memtable.h	Thu Aug 29 10:09:02 2013 -0700
     1.3 @@ -1,5 +1,5 @@
     1.4  /**
     1.5 - * vim: set ts=4 :
     1.6 + * vim: set ts=4 sw=4 tw=99 noet :
     1.7   * =============================================================================
     1.8   * SourceMod
     1.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    1.10 @@ -120,13 +120,20 @@
    1.11  	 */
    1.12  	int AddString(const char *string)
    1.13  	{
    1.14 -		size_t len = strlen(string) + 1;
    1.15 +		return AddString(string, strlen(string));
    1.16 +	}
    1.17 +
    1.18 +	/** 
    1.19 +	 * Adds a string to the string table and returns its index.
    1.20 +	 */
    1.21 +	int AddString(const char *string, size_t length)
    1.22 +	{
    1.23 +		size_t len = length + 1;
    1.24  		int idx;
    1.25  		char *addr;
    1.26  
    1.27  		idx = m_table.CreateMem(len, (void **)&addr);
    1.28 -		strcpy(addr, string);
    1.29 -
    1.30 +		memcpy(addr, string, length + 1);
    1.31  		return idx;
    1.32  	}
    1.33  
    1.34 @@ -139,7 +146,7 @@
    1.35  	}
    1.36  
    1.37  	/**
    1.38 -	 * Scraps the string table.  For caching purposes, the memory
    1.39 +	 * Scraps the string table. For caching purposes, the memory
    1.40  	 * is not freed, however subsequent calls to AddString() will 
    1.41  	 * begin at the first index again.
    1.42  	 */
     2.1 --- a/core/logic/smn_adt_trie.cpp	Thu Aug 29 11:11:41 2013 -0400
     2.2 +++ b/core/logic/smn_adt_trie.cpp	Thu Aug 29 10:09:02 2013 -0700
     2.3 @@ -1,5 +1,5 @@
     2.4  /**
     2.5 - * vim: set ts=4 :
     2.6 + * vim: set ts=4 sw=4 tw=99 noet :
     2.7   * =============================================================================
     2.8   * SourceMod
     2.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    2.10 @@ -31,49 +31,157 @@
    2.11  
    2.12  #include <stdlib.h>
    2.13  #include "common_logic.h"
    2.14 -#include <sm_trie_tpl.h>
    2.15 +#include <am-moveable.h>
    2.16 +#include <am-refcounting.h>
    2.17 +#include <sm_stringhashmap.h>
    2.18 +#include "sm_memtable.h"
    2.19  
    2.20  HandleType_t htCellTrie;
    2.21 +HandleType_t htSnapshot;
    2.22  
    2.23 -enum TrieNodeType
    2.24 +enum EntryType
    2.25  {
    2.26 -	TrieNode_Cell,
    2.27 -	TrieNode_CellArray,
    2.28 -	TrieNode_String,
    2.29 +	EntryType_Cell,
    2.30 +	EntryType_CellArray,
    2.31 +	EntryType_String,
    2.32  };
    2.33  
    2.34 -struct SmartTrieNode
    2.35 +class Entry
    2.36  {
    2.37 -	SmartTrieNode()
    2.38 +	struct ArrayInfo
    2.39  	{
    2.40 -		ptr = NULL;
    2.41 -		type = TrieNode_Cell;
    2.42 +		size_t length;
    2.43 +		size_t maxbytes;
    2.44 +
    2.45 +		void *base() {
    2.46 +			return this + 1;
    2.47 +		}
    2.48 +	};
    2.49 +
    2.50 +public:
    2.51 +	Entry()
    2.52 +		: control_(0)
    2.53 +	{
    2.54  	}
    2.55 -	SmartTrieNode(const SmartTrieNode &obj)
    2.56 +	Entry(ke::Moveable<Entry> other)
    2.57 +	{
    2.58 +		control_ = other->control_;
    2.59 +		data_ = other->data_;
    2.60 +		other->control_ = 0;
    2.61 +	}
    2.62 +
    2.63 +	~Entry()
    2.64  	{
    2.65 -		type = obj.type;
    2.66 -		ptr = obj.ptr;
    2.67 -		data = obj.data;
    2.68 -		data_len = obj.data_len;
    2.69 +		free(raw());
    2.70 +	}
    2.71 +
    2.72 +	void setCell(cell_t value) {
    2.73 +		setType(EntryType_Cell);
    2.74 +		data_ = value;
    2.75 +	}
    2.76 +	void setArray(cell_t *cells, size_t length) {
    2.77 +		ArrayInfo *array = ensureArray(length * sizeof(cell_t));
    2.78 +		array->length = length;
    2.79 +		memcpy(array->base(), cells, length * sizeof(cell_t));
    2.80 +		setTypeAndPointer(EntryType_CellArray, array);
    2.81 +	}
    2.82 +	void setString(const char *str) {
    2.83 +		size_t length = strlen(str);
    2.84 +		ArrayInfo *array = ensureArray(length + 1);
    2.85 +		array->length = length;
    2.86 +		strcpy((char *)array->base(), str);
    2.87 +		setTypeAndPointer(EntryType_String, array);
    2.88 +	}
    2.89 +
    2.90 +	size_t arrayLength() const {
    2.91 +		assert(isArray());
    2.92 +		return raw()->length;
    2.93 +	}
    2.94 +	cell_t *array() const {
    2.95 +		assert(isArray());
    2.96 +		return reinterpret_cast<cell_t *>(raw()->base());
    2.97 +	}
    2.98 +	char *chars() const {
    2.99 +		assert(isString());
   2.100 +		return reinterpret_cast<char *>(raw()->base());
   2.101 +	}
   2.102 +	cell_t cell() const {
   2.103 +		assert(isCell());
   2.104 +		return data_;
   2.105 +	}
   2.106 +
   2.107 +	bool isCell() const {
   2.108 +		return type() == EntryType_Cell;
   2.109 +	}
   2.110 +	bool isArray() const {
   2.111 +		return type() == EntryType_CellArray;
   2.112  	}
   2.113 -	SmartTrieNode & operator =(const SmartTrieNode &src)
   2.114 -	{
   2.115 -		type = src.type;
   2.116 -		ptr = src.ptr;
   2.117 -		data = src.data;
   2.118 -		data_len = src.data_len;
   2.119 -		return *this;
   2.120 +	bool isString() const {
   2.121 +		return type() == EntryType_String;
   2.122 +	}
   2.123 +
   2.124 +private:
   2.125 +	Entry(const Entry &other) KE_DELETE;
   2.126 +
   2.127 +	ArrayInfo *ensureArray(size_t bytes) {
   2.128 +		ArrayInfo *array = raw();
   2.129 +		if (array && array->maxbytes >= bytes)
   2.130 +			return array;
   2.131 +		array = (ArrayInfo *)realloc(array, bytes + sizeof(ArrayInfo));
   2.132 +		if (!array)
   2.133 +		{
   2.134 +			fprintf(stderr, "Out of memory!\n");
   2.135 +			abort();
   2.136 +		}
   2.137 +		array->maxbytes = bytes;
   2.138 +		return array;
   2.139  	}
   2.140 -	TrieNodeType type;
   2.141 -	cell_t *ptr;
   2.142 -	cell_t data;
   2.143 -	cell_t data_len;
   2.144 +
   2.145 +	// Pointer and type are overlaid, so we have some accessors.
   2.146 +	ArrayInfo *raw() const {
   2.147 +		return reinterpret_cast<ArrayInfo *>(control_ & ~uintptr_t(0x3));
   2.148 +	}
   2.149 +	void setType(EntryType aType) {
   2.150 +		control_ = uintptr_t(raw()) | uintptr_t(aType);
   2.151 +		assert(type() == aType);
   2.152 +	}
   2.153 +	void setTypeAndPointer(EntryType aType, ArrayInfo *ptr) {
   2.154 +		// malloc() should guarantee 8-byte alignment at worst
   2.155 +		assert((uintptr_t(ptr) & 0x3) == 0);
   2.156 +		control_ = uintptr_t(ptr) | uintptr_t(aType);
   2.157 +		assert(type() == aType);
   2.158 +	}
   2.159 +	EntryType type() const {
   2.160 +		return (EntryType)(control_ & 0x3);
   2.161 +	}
   2.162 +
   2.163 +private:
   2.164 +	// Contains the bits for the type, and an array pointer, if one is set.
   2.165 +	uintptr_t control_;
   2.166 +
   2.167 +	// Contains data for cell-only entries.
   2.168 +	cell_t data_;
   2.169  };
   2.170  
   2.171 -struct CellTrie
   2.172 +struct CellTrie : public ke::Refcounted<CellTrie>
   2.173 +{
   2.174 +	StringHashMap<Entry> map;
   2.175 +};
   2.176 +
   2.177 +struct TrieSnapshot
   2.178  {
   2.179 -	KTrie<SmartTrieNode> trie;
   2.180 -	cell_t mem_usage;
   2.181 +	TrieSnapshot()
   2.182 +		: strings(128)
   2.183 +	{ }
   2.184 +
   2.185 +	size_t mem_usage()
   2.186 +	{
   2.187 +		return length * sizeof(int) + strings.GetMemTable()->GetMemUsage();
   2.188 +	}
   2.189 +
   2.190 +	size_t length;
   2.191 +	ke::AutoArray<int> keys;
   2.192 +	BaseStringTable strings;
   2.193  };
   2.194  
   2.195  class TrieHelpers : 
   2.196 @@ -84,28 +192,35 @@
   2.197  	void OnSourceModAllInitialized()
   2.198  	{
   2.199  		htCellTrie = handlesys->CreateType("Trie", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   2.200 +		htSnapshot = handlesys->CreateType("TrieSnapshot", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   2.201  	}
   2.202  	void OnSourceModShutdown()
   2.203  	{
   2.204 +		handlesys->RemoveType(htSnapshot, g_pCoreIdent);
   2.205  		handlesys->RemoveType(htCellTrie, g_pCoreIdent);
   2.206  	}
   2.207  public: //IHandleTypeDispatch
   2.208 -	static void DestroySmartTrieNode(SmartTrieNode *pNode)
   2.209 -	{
   2.210 -		free(pNode->ptr);
   2.211 -	}
   2.212  	void OnHandleDestroy(HandleType_t type, void *object)
   2.213  	{
   2.214 -		CellTrie *pTrie = (CellTrie *)object;
   2.215 -
   2.216 -		pTrie->trie.run_destructor(DestroySmartTrieNode);
   2.217 -
   2.218 -		delete pTrie;
   2.219 +		if (type == htCellTrie)
   2.220 +		{
   2.221 +			CellTrie *pTrie = (CellTrie *)object;
   2.222 +			pTrie->Release();
   2.223 +		} else {
   2.224 +			TrieSnapshot *snapshot = (TrieSnapshot *)object;
   2.225 +			delete snapshot;
   2.226 +		}
   2.227  	}
   2.228  	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   2.229  	{
   2.230 -		CellTrie *pArray = (CellTrie *)object;
   2.231 -		*pSize = sizeof(CellTrie) + pArray->mem_usage + pArray->trie.mem_usage();
   2.232 +		if (type == htCellTrie)
   2.233 +		{
   2.234 +			CellTrie *pArray = (CellTrie *)object;
   2.235 +			*pSize = sizeof(CellTrie) + pArray->map.mem_usage();
   2.236 +		} else {
   2.237 +			TrieSnapshot *snapshot = (TrieSnapshot *)object;
   2.238 +			*pSize = sizeof(TrieSnapshot) + snapshot->mem_usage();
   2.239 +		}
   2.240  		return true;
   2.241  	}
   2.242  } s_CellTrieHelpers;
   2.243 @@ -115,8 +230,6 @@
   2.244  	CellTrie *pTrie = new CellTrie;
   2.245  	Handle_t hndl;
   2.246  
   2.247 -	pTrie->mem_usage = 0;
   2.248 -
   2.249  	if ((hndl = handlesys->CreateHandle(htCellTrie, pTrie, pContext->GetIdentity(), g_pCoreIdent, NULL))
   2.250  		== BAD_HANDLE)
   2.251  	{
   2.252 @@ -127,67 +240,13 @@
   2.253  	return hndl;
   2.254  }
   2.255  
   2.256 -static void UpdateNodeCells(CellTrie *pTrie, SmartTrieNode *pData, const cell_t *cells, cell_t num_cells)
   2.257 -{
   2.258 -	if (num_cells == 1)
   2.259 -	{
   2.260 -		pData->data = *cells;
   2.261 -		pData->type = TrieNode_Cell;
   2.262 -	}
   2.263 -	else
   2.264 -	{
   2.265 -		pData->type = TrieNode_CellArray;
   2.266 -		if (pData->ptr == NULL)
   2.267 -		{
   2.268 -			pData->ptr = (cell_t *)malloc(num_cells * sizeof(cell_t));
   2.269 -			pData->data_len = num_cells;
   2.270 -			pTrie->mem_usage += (pData->data_len * sizeof(cell_t));
   2.271 -		}
   2.272 -		else if (pData->data_len < num_cells)
   2.273 -		{
   2.274 -			pData->ptr = (cell_t *)realloc(pData->ptr, num_cells * sizeof(cell_t));
   2.275 -			pTrie->mem_usage += (num_cells - pData->data_len) * sizeof(cell_t);
   2.276 -			pData->data_len = num_cells;
   2.277 -		}
   2.278 -		if (num_cells != 0)
   2.279 -		{
   2.280 -			memcpy(pData->ptr, cells, sizeof(cell_t) * num_cells);
   2.281 -		}
   2.282 -		pData->data = num_cells;
   2.283 -	}
   2.284 -}
   2.285 -
   2.286 -static void UpdateNodeString(CellTrie *pTrie, SmartTrieNode *pData, const char *str)
   2.287 -{
   2.288 -	size_t len = strlen(str);
   2.289 -	cell_t num_cells = (len + sizeof(cell_t)) / sizeof(cell_t);
   2.290 -
   2.291 -	if (pData->ptr == NULL)
   2.292 -	{
   2.293 -		pData->ptr = (cell_t *)malloc(num_cells * sizeof(cell_t));
   2.294 -		pData->data_len = num_cells;
   2.295 -		pTrie->mem_usage += (pData->data_len * sizeof(cell_t));
   2.296 -	}
   2.297 -	else if (pData->data_len < num_cells)
   2.298 -	{
   2.299 -		pData->ptr = (cell_t *)realloc(pData->ptr, num_cells * sizeof(cell_t));
   2.300 -		pTrie->mem_usage += (num_cells - pData->data_len) * sizeof(cell_t);
   2.301 -		pData->data_len = num_cells;
   2.302 -	}
   2.303 -
   2.304 -	strcpy((char *)pData->ptr, str);
   2.305 -	pData->data = len;
   2.306 -	pData->type = TrieNode_String;
   2.307 -}
   2.308 -
   2.309  static cell_t SetTrieValue(IPluginContext *pContext, const cell_t *params)
   2.310  {
   2.311 -	Handle_t hndl;
   2.312  	CellTrie *pTrie;
   2.313  	HandleError err;
   2.314  	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.315  
   2.316 -	hndl = params[1];
   2.317 +	Handle_t hndl = params[1];
   2.318  
   2.319  	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   2.320  		!= HandleError_None)
   2.321 @@ -198,32 +257,29 @@
   2.322  	char *key;
   2.323  	pContext->LocalToString(params[2], &key);
   2.324  
   2.325 -	SmartTrieNode *pNode;
   2.326 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL)
   2.327 +	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   2.328 +	if (!i.found())
   2.329  	{
   2.330 -		SmartTrieNode node;
   2.331 -		UpdateNodeCells(pTrie, &node, &params[3], 1);
   2.332 -		return pTrie->trie.insert(key, node) ? 1 : 0;
   2.333 +		if (!pTrie->map.add(i, key))
   2.334 +			return 0;
   2.335 +		i->value.setCell(params[3]);
   2.336 +		return 1;
   2.337  	}
   2.338  
   2.339  	if (!params[4])
   2.340 -	{
   2.341  		return 0;
   2.342 -	}
   2.343  
   2.344 -	UpdateNodeCells(pTrie, pNode, &params[3], 1);
   2.345 -
   2.346 +	i->value.setCell(params[3]);
   2.347  	return 1;
   2.348  }
   2.349  
   2.350  static cell_t SetTrieArray(IPluginContext *pContext, const cell_t *params)
   2.351  {
   2.352 -	Handle_t hndl;
   2.353  	CellTrie *pTrie;
   2.354  	HandleError err;
   2.355  	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.356  
   2.357 -	hndl = params[1];
   2.358 +	Handle_t hndl = params[1];
   2.359  
   2.360  	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   2.361  		!= HandleError_None)
   2.362 @@ -241,37 +297,30 @@
   2.363  	pContext->LocalToString(params[2], &key);
   2.364  	pContext->LocalToPhysAddr(params[3], &array);
   2.365  
   2.366 -	SmartTrieNode *pNode;
   2.367 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL)
   2.368 +	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   2.369 +	if (!i.found())
   2.370  	{
   2.371 -		SmartTrieNode node;
   2.372 -		UpdateNodeCells(pTrie, &node, array, params[4]);
   2.373 -		if (!pTrie->trie.insert(key, node))
   2.374 -		{
   2.375 -			free(node.ptr);
   2.376 +		if (!pTrie->map.add(i, key))
   2.377  			return 0;
   2.378 -		}
   2.379 +		i->key = key;
   2.380 +		i->value.setArray(array, params[4]);
   2.381  		return 1;
   2.382  	}
   2.383  
   2.384  	if (!params[4])
   2.385 -	{
   2.386  		return 0;
   2.387 -	}
   2.388  
   2.389 -	UpdateNodeCells(pTrie, pNode, array, params[4]);
   2.390 -
   2.391 +	i->value.setArray(array, params[4]);
   2.392  	return 1;
   2.393  }
   2.394  
   2.395  static cell_t SetTrieString(IPluginContext *pContext, const cell_t *params)
   2.396  {
   2.397 -	Handle_t hndl;
   2.398  	CellTrie *pTrie;
   2.399  	HandleError err;
   2.400  	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.401  
   2.402 -	hndl = params[1];
   2.403 +	Handle_t hndl = params[1];
   2.404  
   2.405  	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   2.406  		!= HandleError_None)
   2.407 @@ -283,37 +332,29 @@
   2.408  	pContext->LocalToString(params[2], &key);
   2.409  	pContext->LocalToString(params[3], &val);
   2.410  
   2.411 -	SmartTrieNode *pNode;
   2.412 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL)
   2.413 +	StringHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
   2.414 +	if (!i.found())
   2.415  	{
   2.416 -		SmartTrieNode node;
   2.417 -		UpdateNodeString(pTrie, &node, val);
   2.418 -		if (!pTrie->trie.insert(key, node))
   2.419 -		{
   2.420 -			free(node.ptr);
   2.421 +		if (!pTrie->map.add(i, key))
   2.422  			return 0;
   2.423 -		}
   2.424 +		i->value.setString(val);
   2.425  		return 1;
   2.426  	}
   2.427  
   2.428  	if (!params[4])
   2.429 -	{
   2.430  		return 0;
   2.431 -	}
   2.432  
   2.433 -	UpdateNodeString(pTrie, pNode, val);
   2.434 -
   2.435 +	i->value.setString(val);
   2.436  	return 1;
   2.437  }
   2.438  
   2.439  static cell_t RemoveFromTrie(IPluginContext *pContext, const cell_t *params)
   2.440  {
   2.441 -	Handle_t hndl;
   2.442  	CellTrie *pTrie;
   2.443  	HandleError err;
   2.444  	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.445  
   2.446 -	hndl = params[1];
   2.447 +	Handle_t hndl = params[1];
   2.448  
   2.449  	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   2.450  		!= HandleError_None)
   2.451 @@ -324,16 +365,12 @@
   2.452  	char *key;
   2.453  	pContext->LocalToString(params[2], &key);
   2.454  
   2.455 -	SmartTrieNode *pNode;
   2.456 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL)
   2.457 -	{
   2.458 +	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   2.459 +	if (!r.found())
   2.460  		return 0;
   2.461 -	}
   2.462  
   2.463 -	free(pNode->ptr);
   2.464 -	pNode->ptr = NULL;
   2.465 -
   2.466 -	return pTrie->trie.remove(key) ? 1 : 0;
   2.467 +	pTrie->map.remove(r);
   2.468 +	return 1;
   2.469  }
   2.470  
   2.471  static cell_t ClearTrie(IPluginContext *pContext, const cell_t *params)
   2.472 @@ -351,9 +388,7 @@
   2.473  		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.474  	}
   2.475  
   2.476 -	pTrie->trie.run_destructor(TrieHelpers::DestroySmartTrieNode);
   2.477 -	pTrie->trie.clear();
   2.478 -
   2.479 +	pTrie->map.clear();
   2.480  	return 1;
   2.481  }
   2.482  
   2.483 @@ -377,15 +412,22 @@
   2.484  	pContext->LocalToString(params[2], &key);
   2.485  	pContext->LocalToPhysAddr(params[3], &pValue);
   2.486  
   2.487 -	SmartTrieNode *pNode;
   2.488 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL)
   2.489 +	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   2.490 +	if (!r.found())
   2.491 +		return 0;
   2.492 +
   2.493 +	if (r->value.isCell())
   2.494  	{
   2.495 -		return 0;
   2.496 +		*pValue = r->value.cell();
   2.497 +		return 1;
   2.498  	}
   2.499  
   2.500 -	if (pNode->type == TrieNode_Cell)
   2.501 +	// Maintain compatibility with an old bug. If an array was set with one
   2.502 +	// cell, it was stored internally as a single cell. We now store as an
   2.503 +	// actual array, but we make GetTrieValue() still work for this case.
   2.504 +	if (r->value.isArray() && r->value.arrayLength() == 1)
   2.505  	{
   2.506 -		*pValue = pNode->data;
   2.507 +		*pValue = r->value.array()[0];
   2.508  		return 1;
   2.509  	}
   2.510  	
   2.511 @@ -418,34 +460,29 @@
   2.512  	pContext->LocalToPhysAddr(params[3], &pValue);
   2.513  	pContext->LocalToPhysAddr(params[5], &pSize);
   2.514  
   2.515 -	SmartTrieNode *pNode;
   2.516 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL
   2.517 -		|| pNode->type != TrieNode_CellArray)
   2.518 -	{
   2.519 +
   2.520 +	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   2.521 +	if (!r.found() || !r->value.isArray())
   2.522  		return 0;
   2.523 -	}
   2.524  
   2.525 -	if (pNode->ptr == NULL)
   2.526 +	if (!r->value.array())
   2.527  	{
   2.528  		*pSize = 0;
   2.529  		return 1;
   2.530  	}
   2.531  
   2.532 -	if (pNode->data > params[4])
   2.533 -	{
   2.534 +	if (!params[4])
   2.535 +		return 1;
   2.536 +
   2.537 +	size_t length = r->value.arrayLength();
   2.538 +	cell_t *base = r->value.array();
   2.539 +
   2.540 +	if (length > size_t(params[4]))
   2.541  		*pSize = params[4];
   2.542 -	}
   2.543 -	else if (params[4] != 0)
   2.544 -	{
   2.545 -		*pSize = pNode->data;
   2.546 -	}
   2.547  	else
   2.548 -	{
   2.549 -		return 1;
   2.550 -	}
   2.551 +		*pSize = length;
   2.552  
   2.553 -	memcpy(pValue, pNode->ptr, sizeof(cell_t) * pSize[0]);
   2.554 -
   2.555 +	memcpy(pValue, base, sizeof(cell_t) * pSize[0]);
   2.556  	return 1;
   2.557  }
   2.558  
   2.559 @@ -474,25 +511,14 @@
   2.560  	pContext->LocalToString(params[2], &key);
   2.561  	pContext->LocalToPhysAddr(params[5], &pSize);
   2.562  
   2.563 -	SmartTrieNode *pNode;
   2.564 -	if ((pNode = pTrie->trie.retrieve(key)) == NULL
   2.565 -		|| pNode->type != TrieNode_String)
   2.566 -	{
   2.567 +	StringHashMap<Entry>::Result r = pTrie->map.find(key);
   2.568 +	if (!r.found() || !r->value.isString())
   2.569  		return 0;
   2.570 -	}
   2.571 -
   2.572 -	if (pNode->ptr == NULL)
   2.573 -	{
   2.574 -		*pSize = 0;
   2.575 -		pContext->StringToLocal(params[3], params[4], "");
   2.576 -		return 1;
   2.577 -	}
   2.578  
   2.579  	size_t written;
   2.580 -	pContext->StringToLocalUTF8(params[3], params[4], (char *)pNode->ptr, &written);
   2.581 +	pContext->StringToLocalUTF8(params[3], params[4], r->value.chars(), &written);
   2.582  
   2.583  	*pSize = (cell_t)written;
   2.584 -
   2.585  	return 1;
   2.586  }
   2.587  
   2.588 @@ -511,7 +537,101 @@
   2.589  		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.590  	}
   2.591  
   2.592 -	return pTrie->trie.size();
   2.593 +	return pTrie->map.elements();
   2.594 +}
   2.595 +
   2.596 +static cell_t CreateTrieSnapshot(IPluginContext *pContext, const cell_t *params)
   2.597 +{
   2.598 +	HandleError err;
   2.599 +	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.600 +
   2.601 +	Handle_t hndl = params[1];
   2.602 +
   2.603 +	CellTrie *pTrie;
   2.604 +	if ((err = handlesys->ReadHandle(hndl, htCellTrie, &sec, (void **)&pTrie))
   2.605 +		!= HandleError_None)
   2.606 +	{
   2.607 +		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.608 +	}
   2.609 +
   2.610 +	TrieSnapshot *snapshot = new TrieSnapshot;
   2.611 +	snapshot->length = pTrie->map.elements();
   2.612 +	snapshot->keys = new int[snapshot->length];
   2.613 +	size_t i = 0;
   2.614 +	for (StringHashMap<Entry>::iterator iter = pTrie->map.iter(); !iter.empty(); iter.next(), i++)
   2.615 +		snapshot->keys[i] = snapshot->strings.AddString(iter->key.chars(), iter->key.length());
   2.616 +	assert(i == snapshot->length);
   2.617 +
   2.618 +	if ((hndl = handlesys->CreateHandle(htSnapshot, snapshot, pContext->GetIdentity(), g_pCoreIdent, NULL))
   2.619 +		== BAD_HANDLE)
   2.620 +	{
   2.621 +		delete snapshot;
   2.622 +		return BAD_HANDLE;
   2.623 +	}
   2.624 +
   2.625 +	return hndl;
   2.626 +}
   2.627 +
   2.628 +static cell_t TrieSnapshotLength(IPluginContext *pContext, const cell_t *params)
   2.629 +{
   2.630 +	HandleError err;
   2.631 +	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.632 +
   2.633 +	Handle_t hndl = params[1];
   2.634 +
   2.635 +	TrieSnapshot *snapshot;
   2.636 +	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   2.637 +		!= HandleError_None)
   2.638 +	{
   2.639 +		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.640 +	}
   2.641 +
   2.642 +	return snapshot->length;
   2.643 +}
   2.644 +
   2.645 +static cell_t TrieSnapshotKeyBufferSize(IPluginContext *pContext, const cell_t *params)
   2.646 +{
   2.647 +	HandleError err;
   2.648 +	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.649 +
   2.650 +	Handle_t hndl = params[1];
   2.651 +
   2.652 +	TrieSnapshot *snapshot;
   2.653 +	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   2.654 +		!= HandleError_None)
   2.655 +	{
   2.656 +		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.657 +	}
   2.658 +
   2.659 +	unsigned index = params[2];
   2.660 +	if (index >= snapshot->length)
   2.661 +		return pContext->ThrowNativeError("Invalid index %d", index);
   2.662 +
   2.663 +	return strlen(snapshot->strings.GetString(snapshot->keys[index])) + 1;
   2.664 +}
   2.665 +
   2.666 +static cell_t GetTrieSnapshotKey(IPluginContext *pContext, const cell_t *params)
   2.667 +{
   2.668 +	HandleError err;
   2.669 +	HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
   2.670 +
   2.671 +	Handle_t hndl = params[1];
   2.672 +
   2.673 +	TrieSnapshot *snapshot;
   2.674 +	if ((err = handlesys->ReadHandle(hndl, htSnapshot, &sec, (void **)&snapshot))
   2.675 +		!= HandleError_None)
   2.676 +	{
   2.677 +		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
   2.678 +	}
   2.679 +
   2.680 +	unsigned index = params[2];
   2.681 +	if (index >= snapshot->length)
   2.682 +		return pContext->ThrowNativeError("Invalid index %d", index);
   2.683 +
   2.684 +	size_t written;
   2.685 +	const char *str = snapshot->strings.GetString(snapshot->keys[index]);
   2.686 +	pContext->StringToLocalUTF8(params[3], params[4], str, &written);
   2.687 +	return written;
   2.688  }
   2.689  
   2.690  REGISTER_NATIVES(trieNatives)
   2.691 @@ -526,5 +646,9 @@
   2.692  	{"SetTrieString",			SetTrieString},
   2.693  	{"SetTrieValue",			SetTrieValue},
   2.694  	{"GetTrieSize",				GetTrieSize},
   2.695 +	{"CreateTrieSnapshot",		CreateTrieSnapshot},
   2.696 +	{"TrieSnapshotLength",		TrieSnapshotLength},
   2.697 +	{"TrieSnapshotKeyBufferSize", TrieSnapshotKeyBufferSize},
   2.698 +	{"GetTrieSnapshotKey",		GetTrieSnapshotKey},
   2.699  	{NULL,						NULL},
   2.700  };
     3.1 --- a/core/sm_memtable.h	Thu Aug 29 11:11:41 2013 -0400
     3.2 +++ b/core/sm_memtable.h	Thu Aug 29 10:09:02 2013 -0700
     3.3 @@ -1,5 +1,5 @@
     3.4  /**
     3.5 - * vim: set ts=4 :
     3.6 + * vim: set ts=4 sw=4 tw=99 noet :
     3.7   * =============================================================================
     3.8   * SourceMod
     3.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    3.10 @@ -120,13 +120,20 @@
    3.11  	 */
    3.12  	int AddString(const char *string)
    3.13  	{
    3.14 -		size_t len = strlen(string) + 1;
    3.15 +		return AddString(string, strlen(string));
    3.16 +	}
    3.17 +
    3.18 +	/** 
    3.19 +	 * Adds a string to the string table and returns its index.
    3.20 +	 */
    3.21 +	int AddString(const char *string, size_t length)
    3.22 +	{
    3.23 +		size_t len = length + 1;
    3.24  		int idx;
    3.25  		char *addr;
    3.26  
    3.27  		idx = m_table.CreateMem(len, (void **)&addr);
    3.28 -		strcpy(addr, string);
    3.29 -
    3.30 +		memcpy(addr, string, length + 1);
    3.31  		return idx;
    3.32  	}
    3.33  
    3.34 @@ -139,7 +146,7 @@
    3.35  	}
    3.36  
    3.37  	/**
    3.38 -	 * Scraps the string table.  For caching purposes, the memory
    3.39 +	 * Scraps the string table. For caching purposes, the memory
    3.40  	 * is not freed, however subsequent calls to AddString() will 
    3.41  	 * begin at the first index again.
    3.42  	 */
     4.1 --- a/plugins/include/adt_trie.inc	Thu Aug 29 11:11:41 2013 -0400
     4.2 +++ b/plugins/include/adt_trie.inc	Thu Aug 29 10:09:02 2013 -0700
     4.3 @@ -1,5 +1,5 @@
     4.4  /**
     4.5 - * vim: set ts=4 :
     4.6 + * vim: set ts=4 sw=4 tw=99 noet :
     4.7   * =============================================================================
     4.8   * SourceMod (C)2004-2008 AlliedModders LLC.  All rights reserved.
     4.9   * =============================================================================
    4.10 @@ -36,35 +36,37 @@
    4.11  #define _adt_trie_included
    4.12  
    4.13  /**
    4.14 - * Creates a Trie structure.  A trie is a data storage object that maps any value to a 
    4.15 - * string of text.  It features very fast lookup and deletion, but grows very slow for 
    4.16 - * insertion once tens of thousands of items are added.
    4.17 + * Creates a hash map. A hash map is a container that can map strings (called
    4.18 + * "keys") to arbitrary values (cells, arrays, or strings). Keys in a hash map
    4.19 + * are unique. That is, there is at most one entry in the map for a given key.
    4.20   *
    4.21 - * Keys in Tries are unique.  That is, each key may only have one value.  Unlike arrays, 
    4.22 - * Tries cannot be iterated right now.  Since the contents are known to be unique, to 
    4.23 - * work around this, you can use ADT Arrays to store a list of keys known to be in a 
    4.24 - * Trie.
    4.25 + * Insertion, deletion, and lookup in a hash map are all considered to be fast
    4.26 + * operations, amortized to O(1), or constant time.
    4.27   *
    4.28 - * @return 			New Trie Handle, which must be freed via CloseHandle().
    4.29 + * The word "Trie" in this API is historical. As of SourceMod 1.6, tries have
    4.30 + * been internally replaced with hash tables, which have O(1) insertion time
    4.31 + * instead of O(n).
    4.32 + *
    4.33 + * @return 			New Map Handle, which must be freed via CloseHandle().
    4.34   */
    4.35  native Handle:CreateTrie();
    4.36  
    4.37  /**
    4.38 - * Sets a value in a Trie, either inserting a new entry or replacing an old one.
    4.39 + * Sets a value in a hash map, either inserting a new entry or replacing an old one.
    4.40   *
    4.41 - * @param trie		Trie Handle.
    4.42 + * @param map		Map Handle.
    4.43   * @param key		Key string.
    4.44   * @param value		Value to store at this key.
    4.45   * @param replace	If false, operation will fail if the key is already set.
    4.46   * @return			True on success, false on failure.
    4.47   * @error			Invalid Handle.
    4.48   */
    4.49 -native bool:SetTrieValue(Handle:trie, const String:key[], any:value, bool:replace=true);
    4.50 +native bool:SetTrieValue(Handle:map, const String:key[], any:value, bool:replace=true);
    4.51  
    4.52  /**
    4.53 - * Sets an array value in a Trie, either inserting a new entry or replacing an old one.
    4.54 + * Sets an array value in a Map, either inserting a new entry or replacing an old one.
    4.55   *
    4.56 - * @param trie		Trie Handle.
    4.57 + * @param map		Map Handle.
    4.58   * @param key		Key string.
    4.59   * @param array		Array to store.
    4.60   * @param num_items	Number of items in the array.
    4.61 @@ -72,36 +74,36 @@
    4.62   * @return			True on success, false on failure.
    4.63   * @error			Invalid Handle.
    4.64   */
    4.65 -native bool:SetTrieArray(Handle:trie, const String:key[], const any:array[], num_items, bool:replace=true);
    4.66 +native bool:SetTrieArray(Handle:map, const String:key[], const any:array[], num_items, bool:replace=true);
    4.67  
    4.68  /**
    4.69 - * Sets a string value in a Trie, either inserting a new entry or replacing an old one.
    4.70 + * Sets a string value in a Map, either inserting a new entry or replacing an old one.
    4.71   *
    4.72 - * @param trie		Trie Handle.
    4.73 + * @param map		Map Handle.
    4.74   * @param key		Key string.
    4.75   * @param value		String to store.
    4.76   * @param replace	If false, operation will fail if the key is already set.
    4.77   * @return			True on success, false on failure.
    4.78   * @error			Invalid Handle.
    4.79   */
    4.80 -native bool:SetTrieString(Handle:trie, const String:key[], const String:value[], bool:replace=true);
    4.81 +native bool:SetTrieString(Handle:map, const String:key[], const String:value[], bool:replace=true);
    4.82  
    4.83  /**
    4.84 - * Retrieves a value in a Trie.
    4.85 + * Retrieves a value in a Map.
    4.86   *
    4.87 - * @param trie		Trie Handle.
    4.88 + * @param map		Map Handle.
    4.89   * @param key		Key string.
    4.90   * @param value		Variable to store value.
    4.91   * @return			True on success.  False if the key is not set, or the key is set 
    4.92   *					as an array or string (not a value).
    4.93   * @error			Invalid Handle.
    4.94   */
    4.95 -native bool:GetTrieValue(Handle:trie, const String:key[], &any:value);
    4.96 +native bool:GetTrieValue(Handle:map, const String:key[], &any:value);
    4.97  
    4.98  /**
    4.99 - * Retrieves an array in a Trie.
   4.100 + * Retrieves an array in a Map.
   4.101   *
   4.102 - * @param trie		Trie Handle.
   4.103 + * @param map		Map Handle.
   4.104   * @param key		Key string.
   4.105   * @param array		Buffer to store array.
   4.106   * @param max_size	Maximum size of array buffer.
   4.107 @@ -110,12 +112,12 @@
   4.108   *					as a value or string (not an array).
   4.109   * @error			Invalid Handle.
   4.110   */
   4.111 -native bool:GetTrieArray(Handle:trie, const String:key[], any:array[], max_size, &size=0);
   4.112 +native bool:GetTrieArray(Handle:map, const String:key[], any:array[], max_size, &size=0);
   4.113  
   4.114  /**
   4.115 - * Retrieves a string in a Trie.
   4.116 + * Retrieves a string in a Map.
   4.117   *
   4.118 - * @param trie		Trie Handle.
   4.119 + * @param map		Map Handle.
   4.120   * @param key		Key string.
   4.121   * @param value		Buffer to store value.
   4.122   * @param max_size	Maximum size of string buffer.
   4.123 @@ -124,34 +126,75 @@
   4.124   *					as a value or array (not a string).
   4.125   * @error			Invalid Handle.
   4.126   */
   4.127 -native bool:GetTrieString(Handle:trie, const String:key[], String:value[], max_size, &size=0);
   4.128 +native bool:GetTrieString(Handle:map, const String:key[], String:value[], max_size, &size=0);
   4.129  
   4.130  /**
   4.131 - * Removes a key entry from a Trie.
   4.132 + * Removes a key entry from a Map.
   4.133   *
   4.134 - * @param trie		Trie Handle.
   4.135 + * @param map		Map Handle.
   4.136   * @param key		Key string.
   4.137   * @return			True on success, false if the value was never set.
   4.138   * @error			Invalid Handle.
   4.139   */
   4.140 -native RemoveFromTrie(Handle:trie, const String:key[]);
   4.141 +native RemoveFromTrie(Handle:map, const String:key[]);
   4.142  
   4.143  /**
   4.144 - * Clears all entries from a Trie.
   4.145 + * Clears all entries from a Map.
   4.146   *
   4.147 - * @param trie		Trie Handle.
   4.148 + * @param map		Map Handle.
   4.149   * @error			Invalid Handle.
   4.150   */
   4.151 -native ClearTrie(Handle:trie);
   4.152 +native ClearTrie(Handle:map);
   4.153  
   4.154  /**
   4.155 - * Retrieves the number of elements in a trie.
   4.156 + * Retrieves the number of elements in a map.
   4.157   *
   4.158 - * Note that trie items are not enumerable/iteratable.  If you need to 
   4.159 - * retrieve the elements in a trie, store its keys in an ADT Array. 
   4.160 - *
   4.161 - * @param trie		Trie Handle.
   4.162 + * @param map		Map Handle.
   4.163   * @return			Number of elements in the trie.
   4.164   * @error			Invalid Handle.
   4.165   */
   4.166 -native GetTrieSize(Handle:trie);
   4.167 +native GetTrieSize(Handle:map);
   4.168 +
   4.169 +/**
   4.170 + * Creates a snapshot of all keys in the map. If the map is changed after this
   4.171 + * call, the changes are not reflected in the snapshot. Keys are not sorted.
   4.172 + *
   4.173 + * @param map       Map Handle.
   4.174 + * @return			New Map Snapshot Handle, which must be closed via CloseHandle().
   4.175 + * @error			Invalid Handle.
   4.176 + */
   4.177 +native Handle:CreateTrieSnapshot(Handle:map);
   4.178 +
   4.179 +/**
   4.180 + * Returns the number of keys in a map snapshot. Note that this may be
   4.181 + * different from the size of the map, since the map can change after the
   4.182 + * snapshot of its keys was taken.
   4.183 + *
   4.184 + * @param snapshot	Map snapshot.
   4.185 + * @return			Number of keys.
   4.186 + * @error			Invalid Handle.
   4.187 + */
   4.188 +native TrieSnapshotLength(Handle:snapshot);
   4.189 +
   4.190 +/**
   4.191 + * Returns the buffer size required to store a given key. That is, it returns
   4.192 + * the length of the key plus one.
   4.193 + *
   4.194 + * @param snapshot	Map snapshot.
   4.195 + * @param index		Key index (starting from 0).
   4.196 + * @return 			Buffer size required to store the key string.
   4.197 + * @error			Invalid Handle or index out of range.
   4.198 + */
   4.199 +native TrieSnapshotKeyBufferSize(Handle:snapshot, index);
   4.200 +
   4.201 +/**
   4.202 + * Retrieves the key string of a given key in a map snapshot.
   4.203 + *
   4.204 + * @param snapshot	Map snapshot.
   4.205 + * @param index		Key index (starting from 0).
   4.206 + * @param buffer	String buffer.
   4.207 + * @param maxlength	Maximum buffer length.
   4.208 + * @return			Number of bytes written to the buffer.
   4.209 + * @error			Invalid Handle or index out of range.
   4.210 + */
   4.211 +native GetTrieSnapshotKey(Handle:snapshot, index, String:buffer[], maxlength);
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/plugins/testsuite/tries.sp	Thu Aug 29 10:09:02 2013 -0700
     5.3 @@ -0,0 +1,162 @@
     5.4 +// vim: set sts=2 ts=8 sw=2 tw=99 et ft=c :
     5.5 +#include <sourcemod>
     5.6 +
     5.7 +public Plugin:myinfo = 
     5.8 +{
     5.9 +  name        = "Trie test",
    5.10 +  author      = "AlliedModders LLC",
    5.11 +  description = "Trie tests",
    5.12 +  version     = "1.0.0.0",
    5.13 +  url         = "http://www.sourcemod.net/"
    5.14 +};
    5.15 +
    5.16 +public OnPluginStart()
    5.17 +{
    5.18 +  RegServerCmd("test_tries", RunTests);
    5.19 +}
    5.20 +
    5.21 +public Action:RunTests(argc)
    5.22 +{
    5.23 +  new Handle:trie = CreateTrie();
    5.24 +
    5.25 +  for (new i = 0; i < 64; i++) {
    5.26 +    new String:buffer[24];
    5.27 +    Format(buffer, sizeof(buffer), "%d", i);
    5.28 +
    5.29 +    if (!SetTrieValue(trie, buffer, i))
    5.30 +      ThrowError("set trie to %d failed", i);
    5.31 +
    5.32 +    new value;
    5.33 +    if (!GetTrieValue(trie, buffer, value))
    5.34 +      ThrowError("get trie %d", i);
    5.35 +    if (value != i)
    5.36 +      ThrowError("get trie %d == %d", i, i);
    5.37 +  }
    5.38 +
    5.39 +  // Setting 17 without replace should fail.
    5.40 +  new value;
    5.41 +  if (SetTrieValue(trie, "17", 999, false))
    5.42 +    ThrowError("set trie 17 should fail");
    5.43 +  if (!GetTrieValue(trie, "17", value) || value != 17)
    5.44 +    ThrowError("value at 17 not correct");
    5.45 +  if (!SetTrieValue(trie, "17", 999))
    5.46 +    ThrowError("set trie 17 = 999 should succeed");
    5.47 +  if (!GetTrieValue(trie, "17", value) || value != 999)
    5.48 +    ThrowError("value at 17 not correct");
    5.49 +
    5.50 +  // Check size is 64.
    5.51 +  if (GetTrieSize(trie) != 64)
    5.52 +    ThrowError("trie size not 64");
    5.53 +
    5.54 +  // Check "cat" is not found.
    5.55 +  new array[64];
    5.56 +  new String:string[64];
    5.57 +  if (GetTrieValue(trie, "cat", value) ||
    5.58 +      GetTrieArray(trie, "cat", array, sizeof(array)) ||
    5.59 +      GetTrieString(trie, "cat", string, sizeof(string)))
    5.60 +  {
    5.61 +    ThrowError("trie should not have a cat");
    5.62 +  }
    5.63 +
    5.64 +  // Check that "17" is not a string or array.
    5.65 +  if (GetTrieArray(trie, "17", array, sizeof(array)) ||
    5.66 +      GetTrieString(trie, "17", string, sizeof(string)))
    5.67 +  {
    5.68 +    ThrowError("entry 17 should not be an array or string");
    5.69 +  }
    5.70 +
    5.71 +  // Strings.
    5.72 +  if (!SetTrieString(trie, "17", "hellokitty"))
    5.73 +    ThrowError("17 should be string");
    5.74 +  if (!GetTrieString(trie, "17", string, sizeof(string)) ||
    5.75 +      strcmp(string, "hellokitty") != 0)
    5.76 +  {
    5.77 +    ThrowError("17 should be hellokitty");
    5.78 +  }
    5.79 +  if (GetTrieValue(trie, "17", value) ||
    5.80 +      GetTrieArray(trie, "17", array, sizeof(array)))
    5.81 +  {
    5.82 +    ThrowError("entry 17 should not be an array or string");
    5.83 +  }
    5.84 +
    5.85 +  // Arrays.
    5.86 +  new data[5] = { 93, 1, 2, 3, 4 };
    5.87 +  if (!SetTrieArray(trie, "17", data, 5))
    5.88 +    ThrowError("17 should be string");
    5.89 +  if (!GetTrieArray(trie, "17", array, sizeof(array)))
    5.90 +    ThrowError("17 should be hellokitty");
    5.91 +  for (new i = 0; i < 5; i++) {
    5.92 +    if (data[i] != array[i])
    5.93 +      ThrowError("17 slot %d should be %d, got %d", i, data[i], array[i]);
    5.94 +  }
    5.95 +  if (GetTrieValue(trie, "17", value) ||
    5.96 +      GetTrieString(trie, "17", string, sizeof(string)))
    5.97 +  {
    5.98 +    ThrowError("entry 17 should not be an array or string");
    5.99 +  }
   5.100 +
   5.101 +  if (!SetTrieArray(trie, "17", data, 1))
   5.102 +    ThrowError("couldn't set 17 to 1-entry array");
   5.103 +  // Check that we fixed an old bug where 1-entry arrays where cells
   5.104 +  if (!GetTrieArray(trie, "17", array, sizeof(array), value))
   5.105 +    ThrowError("couldn't fetch 1-entry array");
   5.106 +  if (value != 1)
   5.107 +    ThrowError("array size mismatch (%d, expected %d)", value, 1);
   5.108 +  // Check that we maintained backward compatibility.
   5.109 +  if (!GetTrieValue(trie, "17", value))
   5.110 +    ThrowError("backwards compatibility failed");
   5.111 +  if (value != data[0])
   5.112 +    ThrowError("wrong value (%d, expected %d)", value, data[0]);
   5.113 +
   5.114 +  // Remove "17".
   5.115 +  if (!RemoveFromTrie(trie, "17"))
   5.116 +    ThrowError("17 should have been removed");
   5.117 +  if (RemoveFromTrie(trie, "17"))
   5.118 +    ThrowError("17 should not exist");
   5.119 +  if (GetTrieValue(trie, "17", value) ||
   5.120 +      GetTrieArray(trie, "17", array, sizeof(array)) ||
   5.121 +      GetTrieString(trie, "17", string, sizeof(string)))
   5.122 +  {
   5.123 +    ThrowError("trie should not have a 17");
   5.124 +  }
   5.125 +
   5.126 +  ClearTrie(trie);
   5.127 +
   5.128 +  if (GetTrieSize(trie))
   5.129 +    ThrowError("size should be 0");
   5.130 +
   5.131 +  SetTrieString(trie, "adventure", "time!");
   5.132 +  SetTrieString(trie, "butterflies", "bees");
   5.133 +  SetTrieString(trie, "egg", "egg");
   5.134 +
   5.135 +  new Handle:keys = CreateTrieSnapshot(trie);
   5.136 +  {
   5.137 +    if (TrieSnapshotLength(keys) != 3)
   5.138 +      ThrowError("trie snapshot length should be 3");
   5.139 +
   5.140 +    new bool:found[3];
   5.141 +    for (new i = 0; i < TrieSnapshotLength(keys); i++) {
   5.142 +      new size = TrieSnapshotKeyBufferSize(keys, i);
   5.143 +      new String:buffer[size];
   5.144 +      GetTrieSnapshotKey(keys, i, buffer, size);
   5.145 +
   5.146 +      if (strcmp(buffer, "adventure") == 0)
   5.147 +        found[0] = true;
   5.148 +      else if (strcmp(buffer, "butterflies") == 0)
   5.149 +        found[1] = true;
   5.150 +      else if (strcmp(buffer, "egg") == 0)
   5.151 +        found[2] = true;
   5.152 +      else
   5.153 +        ThrowError("unexpected key: %s", buffer);
   5.154 +    }
   5.155 +
   5.156 +    if (!found[0] || !found[1] || !found[2])
   5.157 +      ThrowError("did not find all keys");
   5.158 +  }
   5.159 +  CloseHandle(keys);
   5.160 +
   5.161 +  PrintToServer("All tests passed!");
   5.162 +  CloseHandle(trie);
   5.163 +  return Plugin_Handled;
   5.164 +}
   5.165 +
     6.1 --- a/public/amtl/am-hashmap.h	Thu Aug 29 11:11:41 2013 -0400
     6.2 +++ b/public/amtl/am-hashmap.h	Thu Aug 29 10:09:02 2013 -0700
     6.3 @@ -60,12 +60,28 @@
     6.4      Entry()
     6.5      {
     6.6      }
     6.7 +    Entry(Moveable<Entry> other)
     6.8 +      : key(Moveable<K>(other->key)),
     6.9 +        value(Moveable<V>(other->value))
    6.10 +    {
    6.11 +    }
    6.12  
    6.13      Entry(const K &aKey, const V &aValue)
    6.14       : key(aKey),
    6.15         value(aValue)
    6.16 -    {
    6.17 -    }
    6.18 +    { }
    6.19 +    Entry(const K &aKey, Moveable<V> aValue)
    6.20 +     : key(aKey),
    6.21 +       value(aValue)
    6.22 +    { }
    6.23 +    Entry(Moveable<K> aKey, const V &aValue)
    6.24 +     : key(aKey),
    6.25 +       value(aValue)
    6.26 +    { }
    6.27 +    Entry(Moveable<K> aKey, Moveable<V> aValue)
    6.28 +     : key(aKey),
    6.29 +       value(aValue)
    6.30 +    { }
    6.31    };
    6.32  
    6.33    struct Policy
    6.34 @@ -124,6 +140,18 @@
    6.35    bool add(Insert &i, const K &key, const V &value) {
    6.36      return table_.add(i, Entry(key, value));
    6.37    }
    6.38 +  bool add(Insert &i, Moveable<K> key, const V &value) {
    6.39 +    return table_.add(i, Entry(key, value));
    6.40 +  }
    6.41 +  bool add(Insert &i, const K &key, Moveable<V> value) {
    6.42 +    return table_.add(i, Entry(key, value));
    6.43 +  }
    6.44 +  bool add(Insert &i, Moveable<K> key, Moveable<V> value) {
    6.45 +    return table_.add(i, Entry(key, value));
    6.46 +  }
    6.47 +  bool add(Insert &i, Moveable<K> key) {
    6.48 +    return table_.add(i, Entry(key, V()));
    6.49 +  }
    6.50  
    6.51    // This can be used to avoid compiler constructed temporaries, since AMTL
    6.52    // does not yet support move semantics. If you use this, the key and value
    6.53 @@ -140,6 +168,10 @@
    6.54      table_.clear();
    6.55    }
    6.56  
    6.57 +  size_t elements() const {
    6.58 +    return table_.elements();
    6.59 +  }
    6.60 +
    6.61    size_t estimateMemoryUse() const {
    6.62      return table_.estimateMemoryUse();
    6.63    }
     7.1 --- a/public/amtl/am-hashtable.h	Thu Aug 29 11:11:41 2013 -0400
     7.2 +++ b/public/amtl/am-hashtable.h	Thu Aug 29 10:09:02 2013 -0700
     7.3 @@ -35,6 +35,7 @@
     7.4  #include <stdlib.h>
     7.5  #include "am-allocator-policies.h"
     7.6  #include "am-utility.h"
     7.7 +#include "am-moveable.h"
     7.8  
     7.9  namespace ke {
    7.10  
    7.11 @@ -50,8 +51,16 @@
    7.12      static const uint32_t kRemovedHash = 1;
    7.13    
    7.14     public:
    7.15 -    void setHash(uint32_t hash, const T &t) {
    7.16 +    void setHash(uint32_t hash) {
    7.17        hash_ = hash;
    7.18 +    }
    7.19 +    void construct() {
    7.20 +      new (&t_) T();
    7.21 +    }
    7.22 +    void construct(const T &t) {
    7.23 +      new (&t_) T(t);
    7.24 +    }
    7.25 +    void construct(Moveable<T> t) {
    7.26        new (&t_) T(t);
    7.27      }
    7.28      uint32_t hash() const {
    7.29 @@ -257,7 +266,8 @@
    7.30        Entry &oldEntry = oldTable[i];
    7.31        if (oldEntry.isLive()) {
    7.32          Insert p = insertUnique(oldEntry.hash());
    7.33 -        p.entry().setHash(p.hash(), oldEntry.payload());
    7.34 +        p.entry().setHash(p.hash());
    7.35 +        p.entry().construct(Moveable<Payload>(oldEntry.payload()));
    7.36        }
    7.37        oldEntry.destruct();
    7.38      }
    7.39 @@ -290,8 +300,8 @@
    7.40        if (e->free())
    7.41          break;
    7.42        if (e->isLive() &&
    7.43 -        e->sameHash(hash) &&
    7.44 -        HashPolicy::matches(key, e->payload()))
    7.45 +          e->sameHash(hash) &&
    7.46 +          HashPolicy::matches(key, e->payload()))
    7.47        {
    7.48          return Result(e);
    7.49        }
    7.50 @@ -318,7 +328,7 @@
    7.51      return Insert(e, hash);
    7.52    }
    7.53  
    7.54 -  bool internalAdd(Insert &i, const Payload &payload) {
    7.55 +  bool internalAdd(Insert &i) {
    7.56      assert(!i.found());
    7.57  
    7.58      // If the entry is deleted, just re-use the slot.
    7.59 @@ -345,8 +355,8 @@
    7.60          i = insertUnique(i.hash());
    7.61      }
    7.62  
    7.63 -    i.entry().setHash(i.hash(), payload);
    7.64      nelements_++;
    7.65 +    i.entry().setHash(i.hash());
    7.66      return true;
    7.67    }
    7.68  
    7.69 @@ -423,10 +433,22 @@
    7.70    // The table must not have been mutated in between findForAdd() and add().
    7.71    // The Insert object is still valid after add() returns, however.
    7.72    bool add(Insert &i, const Payload &payload) {
    7.73 -    return internalAdd(i, payload);
    7.74 +    if (!internalAdd(i))
    7.75 +      return false;
    7.76 +    i.entry().construct(payload);
    7.77 +    return true;
    7.78 +  }
    7.79 +  bool add(Insert &i, Moveable<Payload> payload) {
    7.80 +    if (!internalAdd(i))
    7.81 +      return false;
    7.82 +    i.entry().construct(payload);
    7.83 +    return true;
    7.84    }
    7.85    bool add(Insert &i) {
    7.86 -    return internalAdd(i, Payload());
    7.87 +    if (!internalAdd(i))
    7.88 +      return false;
    7.89 +    i.entry().construct();
    7.90 +    return true;
    7.91    }
    7.92  
    7.93    bool checkDensity() {
    7.94 @@ -445,6 +467,10 @@
    7.95      nelements_ = 0;
    7.96    }
    7.97  
    7.98 +  size_t elements() const {
    7.99 +    return nelements_;
   7.100 +  }
   7.101 +
   7.102    size_t estimateMemoryUse() const {
   7.103      return sizeof(Entry) * capacity_;
   7.104    }
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/public/amtl/am-moveable.h	Thu Aug 29 10:09:02 2013 -0700
     8.3 @@ -0,0 +1,66 @@
     8.4 +// vim: set sts=8 ts=2 sw=2 tw=99 et:
     8.5 +//
     8.6 +// Copyright (C) 2013, David Anderson and AlliedModders LLC
     8.7 +// All rights reserved.
     8.8 +// 
     8.9 +// Redistribution and use in source and binary forms, with or without
    8.10 +// modification, are permitted provided that the following conditions are met:
    8.11 +// 
    8.12 +//  * Redistributions of source code must retain the above copyright notice, this
    8.13 +//    list of conditions and the following disclaimer.
    8.14 +//  * Redistributions in binary form must reproduce the above copyright notice,
    8.15 +//    this list of conditions and the following disclaimer in the documentation
    8.16 +//    and/or other materials provided with the distribution.
    8.17 +//  * Neither the name of AlliedModders LLC nor the names of its contributors
    8.18 +//    may be used to endorse or promote products derived from this software
    8.19 +//    without specific prior written permission.
    8.20 +//
    8.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    8.22 +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    8.23 +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    8.24 +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    8.25 +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    8.26 +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    8.27 +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    8.28 +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    8.29 +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    8.30 +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    8.31 +// POSSIBILITY OF SUCH DAMAGE.
    8.32 +
    8.33 +#ifndef _include_amtl_moveable_h_
    8.34 +#define _include_amtl_moveable_h_
    8.35 +
    8.36 +namespace ke {
    8.37 +
    8.38 +// This is a feature in C++11, but since AM projects do not have access to
    8.39 +// C++11 yet, we provide templates to implement move semantics. A class can
    8.40 +// provide a constructor for (ke::Moveable<T> t) which containers will try
    8.41 +// to use.
    8.42 +//
    8.43 +// When implementing a constructor that takes a Moveable, the object being
    8.44 +// moved should be left in a state that is safe, since its destructor will
    8.45 +// be called even though it has been moved.
    8.46 +
    8.47 +template <typename T>
    8.48 +struct Moveable
    8.49 +{
    8.50 + public:
    8.51 +  explicit Moveable(T &t)
    8.52 +   : t_(t)
    8.53 +  {
    8.54 +  }
    8.55 +
    8.56 +  T *operator ->() {
    8.57 +    return &t_;
    8.58 +  }
    8.59 +  operator T &() {
    8.60 +    return t_;
    8.61 +  }
    8.62 +
    8.63 + private:
    8.64 +  T &t_;
    8.65 +};
    8.66 +
    8.67 +} // namespace ke
    8.68 +
    8.69 +#endif // _include_amtl_moveable_h_
     9.1 --- a/public/amtl/am-string.h	Thu Aug 29 11:11:41 2013 -0400
     9.2 +++ b/public/amtl/am-string.h	Thu Aug 29 10:09:02 2013 -0700
     9.3 @@ -33,6 +33,7 @@
     9.4  #include <stdlib.h>
     9.5  #include <string.h>
     9.6  #include <am-utility.h>
     9.7 +#include <am-moveable.h>
     9.8  
     9.9  namespace ke {
    9.10  
    9.11 @@ -54,6 +55,12 @@
    9.12      if (other.length_)
    9.13        set(other.chars_, other.length_);
    9.14    }
    9.15 +  AString(Moveable<AString> other)
    9.16 +    : chars_(other->chars_.take()),
    9.17 +      length_(other->length_)
    9.18 +  {
    9.19 +    other->length_ = 0;
    9.20 +  }
    9.21  
    9.22    AString &operator =(const char *str) {
    9.23      if (str && str[0]) {
    10.1 --- a/public/amtl/am-vector.h	Thu Aug 29 11:11:41 2013 -0400
    10.2 +++ b/public/amtl/am-vector.h	Thu Aug 29 10:09:02 2013 -0700
    10.3 @@ -34,6 +34,7 @@
    10.4  #include <stdlib.h>
    10.5  #include <am-allocator-policies.h>
    10.6  #include <am-utility.h>
    10.7 +#include <am-moveable.h>
    10.8  
    10.9  namespace ke {
   10.10  
   10.11 @@ -163,7 +164,7 @@
   10.12      if (newdata == NULL)
   10.13        return false;
   10.14      for (size_t i = 0; i < nitems_; i++) {
   10.15 -      new (&newdata[i]) T(data_[i]);
   10.16 +      new (&newdata[i]) T(Moveable<T>(data_[i]));
   10.17        data_[i].~T();
   10.18      }
   10.19      this->free(data_);
    11.1 --- a/public/sm_stringhashmap.h	Thu Aug 29 11:11:41 2013 -0400
    11.2 +++ b/public/sm_stringhashmap.h	Thu Aug 29 10:09:02 2013 -0700
    11.3 @@ -47,6 +47,7 @@
    11.4  #include <am-allocator-policies.h>
    11.5  #include <am-hashmap.h>
    11.6  #include <am-string.h>
    11.7 +#include <am-moveable.h>
    11.8  #include <string.h>
    11.9  
   11.10  namespace SourceMod
   11.11 @@ -113,6 +114,7 @@
   11.12  	}
   11.13  
   11.14  	typedef typename Internal::Result Result;
   11.15 +	typedef typename Internal::Insert Insert;
   11.16  	typedef typename Internal::iterator iterator;
   11.17  
   11.18  	// Some KTrie-like helper functions.
   11.19 @@ -143,7 +145,7 @@
   11.20  	bool replace(const char *aKey, const T &value)
   11.21  	{
   11.22  		CharsAndLength key(aKey);
   11.23 -		typename Internal::Insert i = internal_.findForAdd(key);
   11.24 +		Insert i = internal_.findForAdd(key);
   11.25  		if (!i.found())
   11.26  		{
   11.27  			memory_used_ += key.length() + 1;
   11.28 @@ -158,7 +160,7 @@
   11.29  	bool insert(const char *aKey, const T &value)
   11.30  	{
   11.31  		CharsAndLength key(aKey);
   11.32 -		typename Internal::Insert i = internal_.findForAdd(key);
   11.33 +		Insert i = internal_.findForAdd(key);
   11.34  		if (i.found())
   11.35  			return false;
   11.36  		if (!internal_.add(i))
   11.37 @@ -190,14 +192,45 @@
   11.38  		internal_.clear();
   11.39  	}
   11.40  
   11.41 -	iterator iter() {
   11.42 +	iterator iter()
   11.43 +	{
   11.44  		return internal_.iter();
   11.45  	}
   11.46  
   11.47 -	size_t mem_usage() const {
   11.48 +	size_t mem_usage() const
   11.49 +	{
   11.50  		return internal_.estimateMemoryUse() + memory_used_;
   11.51  	}
   11.52  
   11.53 +	size_t elements() const
   11.54 +	{
   11.55 +		return internal_.elements();
   11.56 +	}
   11.57 +
   11.58 +
   11.59 +	Insert findForAdd(const char *aKey)
   11.60 +	{
   11.61 +		CharsAndLength key(aKey);
   11.62 +		return internal_.findForAdd(key);
   11.63 +	}
   11.64 +
   11.65 +	// Note that |i->key| must be set after calling this, and the key must
   11.66 +	// be the same as used with findForAdd(). It is best to avoid these two
   11.67 +	// functions as the combined variants above are safer.
   11.68 +	bool add(Insert &i)
   11.69 +	{
   11.70 +		return internal_.add(i);
   11.71 +	}
   11.72 +
   11.73 +	// Only value needs to be set after.
   11.74 +	bool add(Insert &i, const char *aKey)
   11.75 +	{
   11.76 +		if (!internal_.add(i))
   11.77 +			return false;
   11.78 +		i->key = aKey;
   11.79 +		return true;
   11.80 +	}
   11.81 +
   11.82  private:
   11.83  	Internal internal_;
   11.84  	size_t memory_used_;