Switch trie_natives off KTrie

This commit is contained in:
Arkshine 2014-05-03 13:22:48 +02:00
parent f3aee6d766
commit d933783629
5 changed files with 576 additions and 251 deletions

View File

@ -323,6 +323,9 @@
<ClInclude Include="..\CDataPack.h"> <ClInclude Include="..\CDataPack.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\sm_stringhashmap.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\version.rc"> <ResourceCompile Include="..\version.rc">

241
amxmodx/sm_stringhashmap.h Normal file
View File

@ -0,0 +1,241 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#ifndef _include_sourcemod_hashtable_h_
#define _include_sourcemod_hashtable_h_
/**
* @file sm_stringhashmap.h
*
* @brief Generic Key -> Value map class, based on a hash table. The Key, in
* this case, is always an ASCII string, and the value type is a template
* parameter. This class is intended as a drop-in replacement for KTrie
* (though the retrieve() signature has been improved).
*
* If your Value type already contains the key string, consider using
* NameHashSet instead.
*/
#include <am-allocator-policies.h>
#include <am-hashmap.h>
#include <am-string.h>
#include <am-moveable.h>
#include <string.h>
namespace SourceMod
{
namespace detail
{
class CharsAndLength
{
public:
CharsAndLength(const char *str)
: str_(str),
length_(0)
{
int c;
uint32_t hash = 0;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
hash_ = hash;
length_ = str - str_ - 1;
}
uint32_t hash() const {
return hash_;
}
const char *chars() const {
return str_;
}
size_t length() const {
return length_;
}
private:
const char *str_;
size_t length_;
uint32_t hash_;
};
struct StringHashMapPolicy
{
static inline bool matches(const CharsAndLength &lookup, const ke::AString &key) {
return lookup.length() == key.length() &&
memcmp(lookup.chars(), key.chars(), key.length()) == 0;
}
static inline uint32_t hash(const CharsAndLength &key) {
return key.hash();
}
};
}
template <typename T>
class StringHashMap
{
typedef detail::CharsAndLength CharsAndLength;
typedef ke::HashMap<ke::AString, T, detail::StringHashMapPolicy> Internal;
public:
StringHashMap()
: internal_(ke::SystemAllocatorPolicy()),
memory_used_(0)
{
if (!internal_.init())
internal_.reportOutOfMemory();
}
typedef typename Internal::Result Result;
typedef typename Internal::Insert Insert;
typedef typename Internal::iterator iterator;
// Some KTrie-like helper functions.
bool retrieve(const char *aKey, T *aResult = NULL)
{
CharsAndLength key(aKey);
Result r = internal_.find(key);
if (!r.found())
return false;
if (aResult)
*aResult = r->value;
return true;
}
Result find(const char *aKey)
{
CharsAndLength key(aKey);
return internal_.find(key);
}
bool contains(const char *aKey)
{
CharsAndLength key(aKey);
Result r = internal_.find(key);
return r.found();
}
bool replace(const char *aKey, const T &value)
{
CharsAndLength key(aKey);
Insert i = internal_.findForAdd(key);
if (!i.found())
{
memory_used_ += key.length() + 1;
if (!internal_.add(i))
return false;
i->key = aKey;
}
i->value = value;
return true;
}
bool insert(const char *aKey, const T &value)
{
CharsAndLength key(aKey);
Insert i = internal_.findForAdd(key);
if (i.found())
return false;
if (!internal_.add(i))
return false;
memory_used_ += key.length() + 1;
i->key = aKey;
i->value = value;
return true;
}
bool remove(const char *aKey)
{
CharsAndLength key(aKey);
Result r = internal_.find(key);
if (!r.found())
return false;
memory_used_ -= key.length() + 1;
internal_.remove(r);
return true;
}
void remove(Result &r)
{
internal_.remove(r);
}
void clear()
{
internal_.clear();
}
iterator iter()
{
return internal_.iter();
}
size_t mem_usage() const
{
return internal_.estimateMemoryUse() + memory_used_;
}
size_t elements() const
{
return internal_.elements();
}
Insert findForAdd(const char *aKey)
{
CharsAndLength key(aKey);
return internal_.findForAdd(key);
}
// Note that |i->key| must be set after calling this, and the key must
// be the same as used with findForAdd(). It is best to avoid these two
// functions as the combined variants above are safer.
bool add(Insert &i)
{
return internal_.add(i);
}
// Only value needs to be set after.
bool add(Insert &i, const char *aKey)
{
if (!internal_.add(i))
return false;
i->key = aKey;
return true;
}
private:
Internal internal_;
size_t memory_used_;
};
}
#endif // _include_sourcemod_hashtable_h_

View File

@ -6,18 +6,10 @@
#include "sm_trie_tpl.h" #include "sm_trie_tpl.h"
#include "trie_natives.h" #include "trie_natives.h"
#ifndef NDEBUG using namespace SourceMod;
size_t trie_free_count = 0;
size_t trie_malloc_count = 0;
#endif
TrieHandles g_TrieHandles; TrieHandles<CellTrie> g_TrieHandles;
typedef KTrie<TrieData> celltrie;
void triedata_dtor(TrieData *ptr)
{
ptr->freeCells();
}
// native Trie:TrieCreate(); // native Trie:TrieCreate();
static cell AMX_NATIVE_CALL TrieCreate(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieCreate(AMX *amx, cell *params)
{ {
@ -27,268 +19,249 @@ static cell AMX_NATIVE_CALL TrieCreate(AMX *amx, cell *params)
// native Trie::TrieClear(Trie:handle); // native Trie::TrieClear(Trie:handle);
static cell AMX_NATIVE_CALL TrieClear(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieClear(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
t->run_destructor(triedata_dtor); t->map.clear();
t->clear();
return 1; return 1;
} }
// native TrieSetCell(Trie:handle, const key[], any:value); // native TrieSetCell(Trie:handle, const key[], any:value);
static cell AMX_NATIVE_CALL TrieSetCell(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieSetCell(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
if ((td = t->retrieve(key)) == NULL) StringHashMap<Entry>::Insert i = t->map.findForAdd(key);
if (!i.found())
{ {
TrieData dummy; if (!t->map.add(i, key))
t->insert(key, dummy);
td = t->retrieve(key);
// should never, ever happen
if (td == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
return 0; return 0;
} }
} }
td->setCell(params[3]); i->value.setCell(params[3]);
return 1; return 1;
} }
// native TrieSetString(Trie:handle, const key[], const data[]); // native TrieSetString(Trie:handle, const key[], const data[]);
static cell AMX_NATIVE_CALL TrieSetString(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieSetString(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
const char *value = get_amxstring(amx, params[3], 1, len);
if ((td = t->retrieve(key)) == NULL) StringHashMap<Entry>::Insert i = t->map.findForAdd(key);
if (!i.found())
{ {
TrieData dummy; if (!t->map.add(i, key))
t->insert(key, dummy);
td = t->retrieve(key);
// should never, ever happen
if (td == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
return 0; return 0;
} }
} }
td->setString(get_amxaddr(amx, params[3])); i->value.setString(value);
return 1; return 1;
} }
// native TrieSetArray(Trie:handle, const key[], const any:buffer[], buffsize) // native TrieSetArray(Trie:handle, const key[], const any:buffer[], buffsize)
static cell AMX_NATIVE_CALL TrieSetArray(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieSetArray(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
if ((td = t->retrieve(key)) == NULL) StringHashMap<Entry>::Insert i = t->map.findForAdd(key);
if (!i.found())
{ {
TrieData dummy; if (!t->map.add(i, key))
t->insert(key, dummy);
td = t->retrieve(key);
// should never, ever happen
if (td == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
return 0; return 0;
} }
} }
td->setArray(get_amxaddr(amx, params[3]), params[4]); i->value.setArray(get_amxaddr(amx, params[3]), params[4]);
return 1; return 1;
} }
// native bool:TrieGetCell(Trie:handle, const key[], &any:value); // native bool:TrieGetCell(Trie:handle, const key[], &any:value);
static cell AMX_NATIVE_CALL TrieGetCell(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieGetCell(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
if ((td = t->retrieve(key)) == NULL) StringHashMap<Entry>::Result r = t->map.find(key);
if (!r.found())
{ {
return 0; return 0;
} }
cell *ptr = get_amxaddr(amx, params[3]); cell *ptr = get_amxaddr(amx, params[3]);
if (!td->getCell(ptr))
if (r->value.isCell())
{ {
return 0; *ptr = r->value.cell_();
return 1;
} }
return 1; return 1;
} }
// native bool:TrieGetString(Trie:handle, const key[], buff[], len); // native bool:TrieGetString(Trie:handle, const key[], buff[], len);
static cell AMX_NATIVE_CALL TrieGetString(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieGetString(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
if ((td = t->retrieve(key)) == NULL) StringHashMap<Entry>::Result r = t->map.find(key);
{ if (!r.found() || !r->value.isString())
return 0;
}
cell *ptr = get_amxaddr(amx, params[3]);
if (!td->getString(ptr, params[4]))
{ {
return 0; return 0;
} }
set_amxstring_utf8(amx, params[3], r->value.chars(), r->value.arrayLength(), params[4] + 1); // + EOS
return 1; return 1;
} }
// native bool:TrieGetArray(Trie:handle, const key[], any:buff[], len); // native bool:TrieGetArray(Trie:handle, const key[], any:buff[], len);
static cell AMX_NATIVE_CALL TrieGetArray(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieGetArray(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
TrieData *td = NULL;
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
if ((td = t->retrieve(key)) == NULL)
{
return 0;
}
cell *ptr = get_amxaddr(amx, params[3]); cell *ptr = get_amxaddr(amx, params[3]);
if (!td->getArray(ptr, params[4])) size_t size = params[4];
StringHashMap<Entry>::Result r = t->map.find(key);
if (!r.found() || !r->value.isArray())
{ {
return 0; return 0;
} }
size_t length = r->value.arrayLength();
cell *base = r->value.array();
if (length <= size)
{
size = length;
}
memcpy(ptr, base, sizeof(cell)* size);
return 1; return 1;
} }
// native bool:TrieKeyExists(Trie:handle, const key[]); // native bool:TrieKeyExists(Trie:handle, const key[]);
static cell AMX_NATIVE_CALL TrieKeyExists(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieKeyExists(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
return t->retrieve(key) != NULL ? 1 : 0;
return static_cast<cell>(t->map.contains(key));
} }
// native bool:TrieDeleteKey(Trie:handle, const key[]); // native bool:TrieDeleteKey(Trie:handle, const key[]);
static cell AMX_NATIVE_CALL TrieDeleteKey(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieDeleteKey(AMX *amx, cell *params)
{ {
celltrie *t = g_TrieHandles.lookup(params[1]); CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL) if (t == NULL)
{ {
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]); LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0; return 0;
} }
int len; int len;
const char *key = get_amxstring(amx, params[2], 0, len); const char *key = get_amxstring(amx, params[2], 0, len);
TrieData *td = t->retrieve(key);
if (td != NULL) StringHashMap<Entry>::Result r = t->map.find(key);
if (!r.found())
{ {
td->freeCells(); return 0;
} }
return t->remove(key) ? 1 : 0;
t->map.remove(r);
return 1;
} }
//native TrieDestroy(&Trie:handle) //native TrieDestroy(&Trie:handle)
static cell AMX_NATIVE_CALL TrieDestroy(AMX *amx, cell *params) static cell AMX_NATIVE_CALL TrieDestroy(AMX *amx, cell *params)
{ {
cell *ptr = get_amxaddr(amx, params[1]); cell *ptr = get_amxaddr(amx, params[1]);
celltrie *t = g_TrieHandles.lookup(*ptr); CellTrie *t = g_TrieHandles.lookup(*ptr);
if (t == NULL) if (t == NULL)
{ {
return 0; return 0;
} }
t->run_destructor(triedata_dtor);
if (g_TrieHandles.destroy(*ptr)) if (g_TrieHandles.destroy(*ptr))
{ {
*ptr = 0; *ptr = 0;
return 1; return 1;
} }
return 0;
return 0;
} }
#ifndef NDEBUG
static cell AMX_NATIVE_CALL TrieMallocCount(AMX *amx, cell *params)
{
return trie_malloc_count;
}
static cell AMX_NATIVE_CALL TrieFreeCount(AMX *amx, cell *params)
{
return trie_free_count;
}
#endif
AMX_NATIVE_INFO trie_Natives[] = AMX_NATIVE_INFO trie_Natives[] =
{ {
{ "TrieCreate", TrieCreate }, { "TrieCreate", TrieCreate },
@ -306,11 +279,6 @@ AMX_NATIVE_INFO trie_Natives[] =
{ "TrieKeyExists", TrieKeyExists }, { "TrieKeyExists", TrieKeyExists },
{ "TrieDestroy", TrieDestroy }, { "TrieDestroy", TrieDestroy },
#ifndef NDEBUG
{ "TrieMallocCount", TrieMallocCount },
{ "TrieFreeCount", TrieFreeCount },
#endif
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -2,150 +2,145 @@
#define _TRIE_NATIVES_H_ #define _TRIE_NATIVES_H_
#include "amxmodx.h" #include "amxmodx.h"
#include "sm_trie_tpl.h" #include "sm_stringhashmap.h"
#include <am-refcounting.h>
#include "CVector.h" #include "CVector.h"
#define TRIE_DATA_UNSET 0 using namespace SourceMod;
#define TRIE_DATA_CELL 1
#define TRIE_DATA_STRING 2
#define TRIE_DATA_ARRAY 3
#ifndef NDEBUG enum EntryType
extern size_t trie_malloc_count; {
extern size_t trie_free_count; EntryType_Cell,
#endif EntryType_CellArray,
EntryType_String,
};
class TrieData class Entry
{ {
private: struct ArrayInfo
cell *m_data; {
cell m_cell; size_t length;
cell m_cellcount; size_t maxbytes;
int m_type;
void needCells(cell cellcount) void *base() {
{ return this + 1;
if (m_cellcount < cellcount)
{
if (m_data != NULL)
{
free(m_data);
#ifndef NDEBUG
trie_free_count++;
#endif
}
size_t neededbytes = cellcount * sizeof(cell);
m_data = static_cast<cell *>(malloc(neededbytes));
#ifndef NDEBUG
trie_malloc_count++;
#endif
m_cellcount = cellcount;
}
}
public:
void freeCells()
{
if (m_data)
{
#ifndef NDEBUG
trie_free_count++;
#endif
free(m_data);
m_data = NULL;
}
m_cellcount = 0;
}
TrieData() : m_data(NULL), m_cell(0), m_cellcount(0), m_type(TRIE_DATA_UNSET) { }
TrieData(const TrieData &src) : m_data(src.m_data),
m_cell(src.m_cell),
m_cellcount(src.m_cellcount),
m_type(src.m_type) { }
~TrieData() { }
int getType() { return m_type; }
void setCell(cell value)
{
freeCells();
m_cell = value;
m_type = TRIE_DATA_CELL;
}
void setString(cell *value)
{
cell len = 0;
cell *p = value;
while (*p++ != 0)
{
len++;
}
len += 1; // zero terminator
needCells(len);
memcpy(m_data, value, sizeof(cell) * len);
m_type = TRIE_DATA_STRING;
}
void setArray(cell *value, cell size)
{
if (size <= 0)
return;
needCells(size);
memcpy(m_data, value, sizeof(cell) * size);
m_type = TRIE_DATA_ARRAY;
}
bool getCell(cell *out)
{
if (m_type == TRIE_DATA_CELL)
{
*out = m_cell;
return true;
}
return false;
}
bool getString(cell *out, cell max)
{
if (m_type == TRIE_DATA_STRING && max >= 0)
{
int len = (max > m_cellcount) ? m_cellcount : max;
memcpy(out, m_data, len * sizeof(cell));
/* Don't truncate a multi-byte character */
if (m_data[len - 1] & 1 << 7)
{
len -= UTIL_CheckValidChar(m_data + len - 1);
out[len] = '\0';
}
return true;
}
return false;
}
bool getArray(cell *out, cell max)
{
if (m_type == TRIE_DATA_ARRAY && max >= 0)
{
memcpy(out, m_data, (max > m_cellcount ? m_cellcount : max) * sizeof(cell));
return true;
}
return false;
}
void clear()
{
freeCells();
m_type = TRIE_DATA_UNSET;
} }
}; };
public:
Entry()
: control_(0)
{
}
Entry(ke::Moveable<Entry> other)
{
control_ = other->control_;
data_ = other->data_;
other->control_ = 0;
}
~Entry()
{
free(raw());
}
void setCell(cell value) {
setType(EntryType_Cell);
data_ = value;
}
void setArray(cell *cells, size_t length) {
ArrayInfo *array = ensureArray(length * sizeof(cell));
array->length = length;
memcpy(array->base(), cells, length * sizeof(cell));
setTypeAndPointer(EntryType_CellArray, array);
}
void setString(const char *str) {
size_t length = strlen(str);
ArrayInfo *array = ensureArray(length + 1);
array->length = length;
strcpy((char *)array->base(), str);
setTypeAndPointer(EntryType_String, array);
}
size_t arrayLength() const {
//assert(isArray());
return raw()->length;
}
cell *array() const {
//assert(isArray());
return reinterpret_cast<cell *>(raw()->base());
}
char *chars() const {
//assert(isString());
return reinterpret_cast<char *>(raw()->base());
}
cell cell_() const {
//assert(isCell());
return data_;
}
bool isCell() const {
return type() == EntryType_Cell;
}
bool isArray() const {
return type() == EntryType_CellArray;
}
bool isString() const {
return type() == EntryType_String;
}
private:
Entry(const Entry &other) KE_DELETE;
ArrayInfo *ensureArray(size_t bytes) {
ArrayInfo *array = raw();
if (array && array->maxbytes >= bytes)
return array;
array = (ArrayInfo *)realloc(array, bytes + sizeof(ArrayInfo));
if (!array)
{
fprintf(stderr, "Out of memory!\n");
abort();
}
array->maxbytes = bytes;
return array;
}
// Pointer and type are overlaid, so we have some accessors.
ArrayInfo *raw() const {
return reinterpret_cast<ArrayInfo *>(control_ & ~uintptr_t(0x3));
}
void setType(EntryType aType) {
control_ = uintptr_t(raw()) | uintptr_t(aType);
//assert(type() == aType);
}
void setTypeAndPointer(EntryType aType, ArrayInfo *ptr) {
// malloc() should guarantee 8-byte alignment at worst
//assert((uintptr_t(ptr) & 0x3) == 0);
control_ = uintptr_t(ptr) | uintptr_t(aType);
//assert(type() == aType);
}
EntryType type() const {
return (EntryType)(control_ & 0x3);
}
private:
// Contains the bits for the type, and an array pointer, if one is set.
uintptr_t control_;
// Contains data for cell-only entries.
cell data_;
};
struct CellTrie : public ke::Refcounted<CellTrie>
{
StringHashMap<Entry> map;
};
template <typename T>
class TrieHandles class TrieHandles
{ {
private: private:
CVector< KTrie< TrieData > *> m_tries; CVector<T *> m_tries;
public: public:
TrieHandles() { } TrieHandles() { }
@ -166,7 +161,7 @@ public:
m_tries.clear(); m_tries.clear();
} }
KTrie<TrieData> *lookup(int handle) T *lookup(int handle)
{ {
handle--; handle--;
@ -184,12 +179,12 @@ public:
if (m_tries[i] == NULL) if (m_tries[i] == NULL)
{ {
// reuse handle // reuse handle
m_tries[i] = new KTrie<TrieData>; m_tries[i] = new T;
return static_cast<int>(i) + 1; return static_cast<int>(i) + 1;
} }
} }
m_tries.push_back(new KTrie<TrieData>); m_tries.push_back(new T);
return m_tries.size(); return m_tries.size();
} }
bool destroy(int handle) bool destroy(int handle)
@ -213,7 +208,7 @@ public:
}; };
extern TrieHandles g_TrieHandles; extern TrieHandles<CellTrie> g_TrieHandles;
extern AMX_NATIVE_INFO trie_Natives[]; extern AMX_NATIVE_INFO trie_Natives[];
#endif #endif

View File

@ -8,19 +8,137 @@ enum Trie
Invalid_Trie = 0 Invalid_Trie = 0
}; };
/**
* Creates a hash map. A hash map is a container that can map strings (called
* "keys") to arbitrary values (cells, arrays, or strings). Keys in a hash map
* are unique. That is, there is at most one entry in the map for a given key.
*
* Insertion, deletion, and lookup in a hash map are all considered to be fast
* operations, amortized to O(1), or constant time.
*
* The word "Trie" in this API is historical. As of AMX Mod X 1.8.3, tries have
* been internally replaced with hash tables, which have O(1) insertion time
* instead of O(n).
*
* @return New Map Handle, which must be freed via TrieDestroy().
*/
native Trie:TrieCreate(); native Trie:TrieCreate();
/**
* Clears all entries from a Map.
*
* @param handle Map Handle.
*
* @error Invalid Handle.
*/
native TrieClear(Trie:handle); native TrieClear(Trie:handle);
/**
* Sets a value in a hash map, either inserting a new entry or replacing an old one.
*
* @param handle Map Handle.
* @param key Key string.
* @param value Value to store at this key.
*
* @return True on success, false on failure.
* @error Invalid Handle.
*/
native TrieSetCell(Trie:handle, const key[], any:value); native TrieSetCell(Trie:handle, const key[], any:value);
/**
* Sets a string value in a Map, either inserting a new entry or replacing an old one.
*
* @param handle Map Handle.
* @param key Key string.
* @param value String to store.
*
* @return True on success, false on failure.
* @error Invalid Handle.
*/
native TrieSetString(Trie:handle, const key[], const value[]); native TrieSetString(Trie:handle, const key[], const value[]);
/**
* Sets an array value in a Map, either inserting a new entry or replacing an old one.
*
* @param handle Map Handle.
* @param key Key string.
* @param buffer Array to store.
* @param size Number of items in the array.
*
* @return True on success, false on failure.
* @error Invalid Handle.
*/
native TrieSetArray(Trie:handle, const key[], const any:buffer[], size); native TrieSetArray(Trie:handle, const key[], const any:buffer[], size);
/**
* Retrieves a value in a Map.
*
* @param handle Map Handle.
* @param key Key string.
* @param value Variable to store value.
* @return True on success. False if the key is not set, or the key is set
* as an array or string (not a value).
* @error Invalid Handle.
*/
native bool:TrieGetCell(Trie:handle, const key[], &any:value); native bool:TrieGetCell(Trie:handle, const key[], &any:value);
/**
* Retrieves a string in a Map.
*
* @param handle Map Handle.
* @param key Key string.
* @param output Buffer to store value.
* @param outputsize Maximum size of string buffer.
*
* @return True on success. False if the key is not set, or the key is set
* as a value or array (not a string).
* @error Invalid Handle.
*/
native bool:TrieGetString(Trie:handle, const key[], output[], outputsize); native bool:TrieGetString(Trie:handle, const key[], output[], outputsize);
/**
* Retrieves an array in a Map.
*
* @param handle Map Handle.
* @param key Key string.
* @param output Buffer to store array.
* @param outputsize Maximum size of array buffer.
*
* @return True on success. False if the key is not set, or the key is set
* as a value or string (not an array).
* @error Invalid Handle.
*/
native bool:TrieGetArray(Trie:handle, const key[], any:output[], outputsize); native bool:TrieGetArray(Trie:handle, const key[], any:output[], outputsize);
/**
* Removes a key entry from a Map.
*
* @param handle Map Handle.
* @param key Key string.
*
* @return True on success, false if the value was never set.
* @error Invalid Handle.
*/
native bool:TrieDeleteKey(Trie:handle, const key[]); native bool:TrieDeleteKey(Trie:handle, const key[]);
/**
* Checks a key entry existence from a Map.
*
* @param handle Map Handle.
* @param key Key string.
*
* @return True on success, false if the value was never set.
* @error Invalid Handle.
*/
native bool:TrieKeyExists(Trie:handle, const key[]); native bool:TrieKeyExists(Trie:handle, const key[]);
/**
* Destroys a Map.
*
* @param handle Map Handle.
*
* @return True on success, false if the value was never set.
* @error Invalid Handle.
*/
native TrieDestroy(&Trie:handle); native TrieDestroy(&Trie:handle);