Switch trie_natives off KTrie
This commit is contained in:
parent
f3aee6d766
commit
d933783629
@ -323,6 +323,9 @@
|
||||
<ClInclude Include="..\CDataPack.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\sm_stringhashmap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\version.rc">
|
||||
|
241
amxmodx/sm_stringhashmap.h
Normal file
241
amxmodx/sm_stringhashmap.h
Normal 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_
|
@ -6,18 +6,10 @@
|
||||
#include "sm_trie_tpl.h"
|
||||
#include "trie_natives.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t trie_free_count = 0;
|
||||
size_t trie_malloc_count = 0;
|
||||
#endif
|
||||
using namespace SourceMod;
|
||||
|
||||
TrieHandles g_TrieHandles;
|
||||
typedef KTrie<TrieData> celltrie;
|
||||
TrieHandles<CellTrie> g_TrieHandles;
|
||||
|
||||
void triedata_dtor(TrieData *ptr)
|
||||
{
|
||||
ptr->freeCells();
|
||||
}
|
||||
// native Trie:TrieCreate();
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
t->run_destructor(triedata_dtor);
|
||||
t->clear();
|
||||
t->map.clear();
|
||||
return 1;
|
||||
}
|
||||
// native TrieSetCell(Trie:handle, const key[], any:value);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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;
|
||||
t->insert(key, dummy);
|
||||
|
||||
td = t->retrieve(key);
|
||||
|
||||
// should never, ever happen
|
||||
if (td == NULL)
|
||||
if (!t->map.add(i, key))
|
||||
{
|
||||
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
td->setCell(params[3]);
|
||||
i->value.setCell(params[3]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
// native TrieSetString(Trie:handle, const key[], const data[]);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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;
|
||||
t->insert(key, dummy);
|
||||
td = t->retrieve(key);
|
||||
|
||||
// should never, ever happen
|
||||
if (td == NULL)
|
||||
if (!t->map.add(i, key))
|
||||
{
|
||||
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td->setString(get_amxaddr(amx, params[3]));
|
||||
i->value.setString(value);
|
||||
|
||||
return 1;
|
||||
}
|
||||
// native TrieSetArray(Trie:handle, const key[], const any:buffer[], buffsize)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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;
|
||||
t->insert(key, dummy);
|
||||
td = t->retrieve(key);
|
||||
|
||||
// should never, ever happen
|
||||
if (td == NULL)
|
||||
if (!t->map.add(i, key))
|
||||
{
|
||||
LogError(amx, AMX_ERR_NATIVE, "Couldn't KTrie::retrieve(), handle: %d", params[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td->setArray(get_amxaddr(amx, params[3]), params[4]);
|
||||
i->value.setArray(get_amxaddr(amx, params[3]), params[4]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
// native bool:TrieGetCell(Trie:handle, const key[], &any:value);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// native bool:TrieGetString(Trie:handle, const key[], buff[], len);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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]);
|
||||
if (!td->getString(ptr, params[4]))
|
||||
StringHashMap<Entry>::Result r = t->map.find(key);
|
||||
if (!r.found() || !r->value.isString())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_amxstring_utf8(amx, params[3], r->value.chars(), r->value.arrayLength(), params[4] + 1); // + EOS
|
||||
|
||||
return 1;
|
||||
}
|
||||
// native bool:TrieGetArray(Trie:handle, const key[], any:buff[], len);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
TrieData *td = NULL;
|
||||
int 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]);
|
||||
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;
|
||||
}
|
||||
|
||||
size_t length = r->value.arrayLength();
|
||||
cell *base = r->value.array();
|
||||
|
||||
if (length <= size)
|
||||
{
|
||||
size = length;
|
||||
}
|
||||
|
||||
memcpy(ptr, base, sizeof(cell)* size);
|
||||
return 1;
|
||||
}
|
||||
// native bool:TrieKeyExists(Trie:handle, const key[]);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int 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[]);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int 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)
|
||||
static cell AMX_NATIVE_CALL TrieDestroy(AMX *amx, cell *params)
|
||||
{
|
||||
cell *ptr = get_amxaddr(amx, params[1]);
|
||||
|
||||
celltrie *t = g_TrieHandles.lookup(*ptr);
|
||||
CellTrie *t = g_TrieHandles.lookup(*ptr);
|
||||
|
||||
if (t == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
t->run_destructor(triedata_dtor);
|
||||
|
||||
if (g_TrieHandles.destroy(*ptr))
|
||||
{
|
||||
*ptr = 0;
|
||||
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[] =
|
||||
{
|
||||
{ "TrieCreate", TrieCreate },
|
||||
@ -306,11 +279,6 @@ AMX_NATIVE_INFO trie_Natives[] =
|
||||
{ "TrieKeyExists", TrieKeyExists },
|
||||
{ "TrieDestroy", TrieDestroy },
|
||||
|
||||
#ifndef NDEBUG
|
||||
{ "TrieMallocCount", TrieMallocCount },
|
||||
{ "TrieFreeCount", TrieFreeCount },
|
||||
#endif
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -2,150 +2,145 @@
|
||||
#define _TRIE_NATIVES_H_
|
||||
|
||||
#include "amxmodx.h"
|
||||
#include "sm_trie_tpl.h"
|
||||
#include "sm_stringhashmap.h"
|
||||
#include <am-refcounting.h>
|
||||
#include "CVector.h"
|
||||
|
||||
#define TRIE_DATA_UNSET 0
|
||||
#define TRIE_DATA_CELL 1
|
||||
#define TRIE_DATA_STRING 2
|
||||
#define TRIE_DATA_ARRAY 3
|
||||
using namespace SourceMod;
|
||||
|
||||
#ifndef NDEBUG
|
||||
extern size_t trie_malloc_count;
|
||||
extern size_t trie_free_count;
|
||||
#endif
|
||||
enum EntryType
|
||||
{
|
||||
EntryType_Cell,
|
||||
EntryType_CellArray,
|
||||
EntryType_String,
|
||||
};
|
||||
|
||||
class TrieData
|
||||
class Entry
|
||||
{
|
||||
private:
|
||||
cell *m_data;
|
||||
cell m_cell;
|
||||
cell m_cellcount;
|
||||
int m_type;
|
||||
struct ArrayInfo
|
||||
{
|
||||
size_t length;
|
||||
size_t maxbytes;
|
||||
|
||||
void needCells(cell cellcount)
|
||||
{
|
||||
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;
|
||||
void *base() {
|
||||
return this + 1;
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
CVector< KTrie< TrieData > *> m_tries;
|
||||
CVector<T *> m_tries;
|
||||
|
||||
public:
|
||||
TrieHandles() { }
|
||||
@ -166,7 +161,7 @@ public:
|
||||
|
||||
m_tries.clear();
|
||||
}
|
||||
KTrie<TrieData> *lookup(int handle)
|
||||
T *lookup(int handle)
|
||||
{
|
||||
handle--;
|
||||
|
||||
@ -184,12 +179,12 @@ public:
|
||||
if (m_tries[i] == NULL)
|
||||
{
|
||||
// reuse handle
|
||||
m_tries[i] = new KTrie<TrieData>;
|
||||
m_tries[i] = new T;
|
||||
|
||||
return static_cast<int>(i) + 1;
|
||||
}
|
||||
}
|
||||
m_tries.push_back(new KTrie<TrieData>);
|
||||
m_tries.push_back(new T);
|
||||
return m_tries.size();
|
||||
}
|
||||
bool destroy(int handle)
|
||||
@ -213,7 +208,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
extern TrieHandles g_TrieHandles;
|
||||
extern TrieHandles<CellTrie> g_TrieHandles;
|
||||
extern AMX_NATIVE_INFO trie_Natives[];
|
||||
|
||||
#endif
|
||||
|
@ -8,19 +8,137 @@ enum Trie
|
||||
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();
|
||||
|
||||
/**
|
||||
* Clears all entries from a Map.
|
||||
*
|
||||
* @param handle Map Handle.
|
||||
*
|
||||
* @error Invalid 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);
|
||||
|
||||
/**
|
||||
* 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[]);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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[]);
|
||||
|
||||
/**
|
||||
* 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[]);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user