Merge pull request #8 from Arkshine/trie-update

Trie Update
This commit is contained in:
Vincent Herbet
2014-05-04 20:14:10 +02:00
27 changed files with 4637 additions and 298 deletions

View File

@ -6,7 +6,7 @@
###########################################
HLSDK = ../../hlsdk
MM_ROOT = ../../metamod/metamod
MM_ROOT = ../../metamod-am/metamod
#####################################
### EDIT BELOW FOR OTHER PROJECTS ###
@ -36,7 +36,7 @@ CPP_OSX = clang
LINK = -Lzlib
INCLUDE = -I. -I$(HLSDK) -I$(HLSDK)/common -I$(HLSDK)/dlls -I$(HLSDK)/engine -I$(HLSDK)/game_shared \
INCLUDE = -I. -I../public/amtl -I$(HLSDK) -I$(HLSDK)/common -I$(HLSDK)/dlls -I$(HLSDK)/engine -I$(HLSDK)/game_shared \
-I$(HLSDK)/public -I$(MM_ROOT)
################################################

View File

@ -414,6 +414,7 @@ int C_Spawn(edict_t *pent)
VectorHolder.clear();
g_TrieHandles.clear();
g_TrieSnapshotHandles.clear();
g_DataPackHandles.clear();
char map_pluginsfile_path[256];

View File

@ -92,7 +92,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -143,7 +143,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
<StringPooling>true</StringPooling>
@ -192,7 +192,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -243,7 +243,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
<StringPooling>true</StringPooling>
@ -389,6 +389,8 @@
<ClInclude Include="..\sh_list.h" />
<ClInclude Include="..\sh_stack.h" />
<ClInclude Include="..\sh_tinyhash.h" />
<ClInclude Include="..\sm_memtable.h" />
<ClInclude Include="..\sm_stringhashmap.h" />
<ClInclude Include="..\svn_version.h" />
<ClInclude Include="..\trie_natives.h" />
<ClInclude Include="..\zlib\zconf.h" />

View File

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

170
amxmodx/sm_memtable.h Normal file
View File

@ -0,0 +1,170 @@
/**
* 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_CORE_STRINGTABLE_H_
#define _INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_
#include <stdlib.h>
#include <string.h>
class BaseMemTable
{
public:
BaseMemTable(unsigned int init_size)
{
membase = (unsigned char *)malloc(init_size);
size = init_size;
tail = 0;
}
~BaseMemTable()
{
free(membase);
membase = NULL;
}
public:
/**
* Allocates 'size' bytes of memory.
* Optionally outputs the address through 'addr'.
* Returns an index >= 0 on success, < 0 on failure.
*/
int CreateMem(unsigned int addsize, void **addr)
{
int idx = (int)tail;
while (tail + addsize >= size) {
size *= 2;
membase = (unsigned char *)realloc(membase, size);
}
tail += addsize;
if (addr)
*addr = (void *)&membase[idx];
return idx;
}
/**
* Given an index into the memory table, returns its address.
* Returns NULL if invalid.
*/
void *GetAddress(int index)
{
if (index < 0 || (unsigned int)index >= tail)
return NULL;
return &membase[index];
}
/**
* Scraps the memory table. For caching purposes, the memory
* is not freed, however subsequent calls to CreateMem() will
* begin at the first index again.
*/
void Reset()
{
tail = 0;
}
inline unsigned int GetMemUsage()
{
return size;
}
inline unsigned int GetActualMemUsed()
{
return tail;
}
private:
unsigned char *membase;
unsigned int size;
unsigned int tail;
};
class BaseStringTable
{
public:
BaseStringTable(unsigned int init_size) : m_table(init_size)
{
}
public:
/**
* Adds a string to the string table and returns its index.
*/
int AddString(const char *string)
{
return AddString(string, strlen(string));
}
/**
* Adds a string to the string table and returns its index.
*/
int AddString(const char *string, size_t length)
{
size_t len = length + 1;
int idx;
char *addr;
idx = m_table.CreateMem(len, (void **)&addr);
memcpy(addr, string, length + 1);
return idx;
}
/**
* Given an index into the string table, returns the associated string.
*/
inline const char *GetString(int str)
{
return (const char *)m_table.GetAddress(str);
}
/**
* Scraps the string table. For caching purposes, the memory
* is not freed, however subsequent calls to AddString() will
* begin at the first index again.
*/
void Reset()
{
m_table.Reset();
}
/**
* Returns the parent BaseMemTable that this string table uses.
*/
inline BaseMemTable *GetMemTable()
{
return &m_table;
}
private:
BaseMemTable m_table;
};
#endif //_INCLUDE_SOURCEMOD_CORE_STRINGTABLE_H_

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,11 @@
#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;
TrieHandles<TrieSnapshot> g_TrieSnapshotHandles;
void triedata_dtor(TrieData *ptr)
{
ptr->freeCells();
}
// native Trie:TrieCreate();
static cell AMX_NATIVE_CALL TrieCreate(AMX *amx, cell *params)
{
@ -27,268 +20,429 @@ 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);
// native TrieSetCell(Trie:handle, const key[], any:value, bool:replace = true);
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;
}
i->value.setCell(params[3]);
return 1;
}
td->setCell(params[3]);
// Old plugin doesn't have 'replace' parameter.
if (*params / sizeof(cell) == 4 && !params[4])
{
return 0;
}
i->value.setCell(params[3]);
return 1;
}
// native TrieSetString(Trie:handle, const key[], const data[]);
// native TrieSetString(Trie:handle, const key[], const data[], bool:replace = true);
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;
}
i->value.setString(value);
return 1;
}
td->setString(get_amxaddr(amx, params[3]));
// Old plugin doesn't have 'replace' parameter.
if (*params / sizeof(cell) == 4 && !params[4])
{
return 0;
}
i->value.setString(value);
return 1;
}
// native TrieSetArray(Trie:handle, const key[], const any:buffer[], buffsize)
// native TrieSetArray(Trie:handle, const key[], const any:buffer[], buffsize, bool:replace = true)
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;
}
if (params[4] < 0)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid array size (%d)", params[4]);
return 0;
}
TrieData *td = NULL;
int len;
const char *key = get_amxstring(amx, params[2], 0, len);
cell *ptr = get_amxaddr(amx, params[3]);
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;
}
i->key = key;
i->value.setArray(ptr, params[4]);
return 1;
}
td->setArray(get_amxaddr(amx, params[3]), params[4]);
// Old plugin doesn't have 'replace' parameter.
if (*params / sizeof(cell) == 4 && !params[5])
{
return 0;
}
i->value.setArray(ptr, 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;
return 0;
}
// native bool:TrieGetString(Trie:handle, const key[], buff[], len);
// native bool:TrieGetString(Trie:handle, const key[], buff[], len, &size = 0);
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;
}
if (params[4] < 0)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid buffer size (%d)", params[4]);
return 0;
}
TrieData *td = NULL;
int len;
const char *key = get_amxstring(amx, params[2], 0, len);
cell *pSize = get_amxaddr(amx, params[5]);
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;
}
*pSize = (cell)set_amxstring_utf8(amx, params[3], r->value.chars(), strlen(r->value.chars()), params[4] + 1); // + EOS
return 1;
}
// native bool:TrieGetArray(Trie:handle, const key[], any:buff[], len);
// native bool:TrieGetArray(Trie:handle, const key[], any:buff[], len, &size = 0);
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;
}
if (params[4] < 0)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid array size (%d)", params[4]);
return 0;
}
TrieData *td = NULL;
int len;
const char *key = get_amxstring(amx, params[2], 0, len);
cell *pValue = get_amxaddr(amx, params[3]);
cell *pSize = get_amxaddr(amx, params[5]);
if ((td = t->retrieve(key)) == NULL)
StringHashMap<Entry>::Result r = t->map.find(key);
if (!r.found() || !r->value.isArray())
{
return 0;
}
cell *ptr = get_amxaddr(amx, params[3]);
if (!td->getArray(ptr, params[4]))
if (!r->value.array())
{
*pSize = 0;
return 1;
}
if (!params[4])
{
return 0;
}
size_t length = r->value.arrayLength();
cell *base = r->value.array();
if (length > size_t(params[4]))
*pSize = params[4];
else
*pSize = length;
memcpy(pValue, base, sizeof(cell) * pSize[0]);
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)
// native TrieGetSize(Trie:handle);
static cell AMX_NATIVE_CALL TrieGetSize(AMX *amx, cell *params)
{
return trie_malloc_count;
CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0;
}
return t->map.elements();
}
static cell AMX_NATIVE_CALL TrieFreeCount(AMX *amx, cell *params)
static cell AMX_NATIVE_CALL TrieSnapshotCreate(AMX *amx, cell *params)
{
return trie_free_count;
CellTrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid map handle provided (%d)", params[1]);
return 0;
}
int index = g_TrieSnapshotHandles.create();
TrieSnapshot *snapshot = g_TrieSnapshotHandles.lookup(index);
snapshot->length = t->map.elements();
snapshot->keys = new int[snapshot->length];
size_t i = 0;
for (StringHashMap<Entry>::iterator iter = t->map.iter(); !iter.empty(); iter.next(), i++)
{
snapshot->keys[i] = snapshot->strings.AddString(iter->key.chars(), iter->key.length());
}
assert(i == snapshot->length);
return static_cast<cell>(index);
}
#endif
static cell AMX_NATIVE_CALL TrieSnapshotLength(AMX *amx, cell *params)
{
TrieSnapshot *snapshot = g_TrieSnapshotHandles.lookup(params[1]);
if (snapshot == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid snapshot handle provided (%d)", params[1]);
return 0;
}
return snapshot->length;
}
static cell AMX_NATIVE_CALL TrieSnapshotKeyBufferSize(AMX *amx, cell *params)
{
TrieSnapshot *snapshot = g_TrieSnapshotHandles.lookup(params[1]);
if (snapshot == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid snapshot handle provided (%d)", params[1]);
return 0;
}
unsigned index = params[2];
if (index >= snapshot->length)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d", index);
return 0;
}
return strlen(snapshot->strings.GetString(snapshot->keys[index])) + 1;
}
static cell AMX_NATIVE_CALL TrieSnapshotGetKey(AMX *amx, cell *params)
{
TrieSnapshot *snapshot = g_TrieSnapshotHandles.lookup(params[1]);
if (snapshot == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid snapshot handle provided (%d)", params[1]);
return 0;
}
unsigned index = params[2];
if (index >= snapshot->length)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid index %d", index);
return 0;
}
const char *str = snapshot->strings.GetString(snapshot->keys[index]);
return set_amxstring_utf8(amx, params[3], str, strlen(str), params[4] + 1);
}
//native TrieSnapshotDestroy(&Snapshot:handle)
static cell AMX_NATIVE_CALL TrieSnapshotDestroy(AMX *amx, cell *params)
{
cell *ptr = get_amxaddr(amx, params[1]);
TrieSnapshot *t = g_TrieSnapshotHandles.lookup(*ptr);
if (t == NULL)
{
return 0;
}
if (g_TrieSnapshotHandles.destroy(*ptr))
{
*ptr = 0;
return 1;
}
return 0;
}
AMX_NATIVE_INFO trie_Natives[] =
{
{ "TrieCreate", TrieCreate },
@ -305,11 +459,13 @@ AMX_NATIVE_INFO trie_Natives[] =
{ "TrieDeleteKey", TrieDeleteKey },
{ "TrieKeyExists", TrieKeyExists },
{ "TrieDestroy", TrieDestroy },
{ "TrieGetSize", TrieGetSize },
#ifndef NDEBUG
{ "TrieMallocCount", TrieMallocCount },
{ "TrieFreeCount", TrieFreeCount },
#endif
{ "TrieSnapshotCreate", TrieSnapshotCreate },
{ "TrieSnapshotLength", TrieSnapshotLength },
{ "TrieSnapshotKeyBufferSize", TrieSnapshotKeyBufferSize },
{ "TrieSnapshotGetKey", TrieSnapshotGetKey },
{ "TrieSnapshotDestroy", TrieSnapshotDestroy },
{ NULL, NULL }
};

View File

@ -2,150 +2,161 @@
#define _TRIE_NATIVES_H_
#include "amxmodx.h"
#include "sm_trie_tpl.h"
#include "sm_stringhashmap.h"
#include "sm_memtable.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
class TrieData
enum EntryType
{
private:
cell *m_data;
cell m_cell;
cell m_cellcount;
int m_type;
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;
}
EntryType_Cell,
EntryType_CellArray,
EntryType_String,
};
class Entry
{
struct ArrayInfo
{
size_t length;
size_t maxbytes;
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
{
StringHashMap<Entry> map;
};
struct TrieSnapshot
{
TrieSnapshot()
: strings(128)
{ }
size_t mem_usage()
{
return length * sizeof(int) + strings.GetMemTable()->GetMemUsage();
}
size_t length;
ke::AutoArray<int> keys;
BaseStringTable strings;
};
template <typename T>
class TrieHandles
{
private:
CVector< KTrie< TrieData > *> m_tries;
CVector<T *> m_tries;
public:
TrieHandles() { }
@ -166,7 +177,7 @@ public:
m_tries.clear();
}
KTrie<TrieData> *lookup(int handle)
T *lookup(int handle)
{
handle--;
@ -184,12 +195,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 +224,8 @@ public:
};
extern TrieHandles g_TrieHandles;
extern TrieHandles<CellTrie> g_TrieHandles;
extern TrieHandles<TrieSnapshot> g_TrieSnapshotHandles;
extern AMX_NATIVE_INFO trie_Natives[];
#endif