Added amb1600: trie natives

This commit is contained in:
Steve Dudenhoeffer 2008-04-14 19:52:11 +00:00
parent 683f453b6f
commit 53ed817183
9 changed files with 732 additions and 2 deletions

View File

@ -20,7 +20,8 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules
amxxfile.cpp CLang.cpp md5.cpp emsg.cpp CForward.cpp CPlugin.cpp CModule.cpp \
CMenu.cpp util.cpp amx.cpp amxdbg.cpp natives.cpp newmenus.cpp debugger.cpp \
optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp \
amxmod_compat.cpp nongpl_matches.cpp CFlagManager.cpp datastructs.cpp
amxmod_compat.cpp nongpl_matches.cpp CFlagManager.cpp datastructs.cpp \
trie_natives.cpp
LINK = -lgcc -static-libgcc

View File

@ -51,7 +51,7 @@
#include "datastructs.h"
#include "CFlagManager.h"
#include "svn_version.h"
#include "trie_natives.h"
plugin_info_t Plugin_info =
{
@ -412,6 +412,7 @@ int C_Spawn(edict_t *pent)
};
VectorHolder.clear();
g_TrieHandles.clear();
char map_pluginsfile_path[256];
char prefixed_map_pluginsfile[256];
char configs_dir[256];

View File

@ -49,6 +49,7 @@
#include "libraries.h"
#include "messages.h"
#include "amxmod_compat.h"
#include "trie_natives.h"
CList<CModule, const char*> g_modules;
CList<CScript, AMX*> g_loadedscripts;
@ -579,6 +580,7 @@ int set_amxnatives(AMX* amx, char error[128])
amx_Register(amx, vector_Natives, -1);
amx_Register(amx, g_SortNatives, -1);
amx_Register(amx, g_DataStructNatives, -1);
amx_Register(amx, trie_Natives, -1);
if (amx->flags & AMX_FLAG_OLDFILE)
{

316
amxmodx/trie_natives.cpp Normal file
View File

@ -0,0 +1,316 @@
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include "amxmodx.h"
#include "sm_trie_tpl.h"
#include "trie_natives.h"
#ifndef NDEBUG
size_t trie_free_count = 0;
size_t trie_malloc_count = 0;
#endif
TrieHandles g_TrieHandles;
typedef KTrie<TrieData> celltrie;
void triedata_dtor(TrieData *ptr)
{
ptr->freeCells();
}
// native Trie:TrieCreate();
static cell AMX_NATIVE_CALL TrieCreate(AMX *amx, cell *params)
{
return static_cast<cell>(g_TrieHandles.create());
}
// native Trie::TrieClear(Trie:handle);
static cell AMX_NATIVE_CALL TrieClear(AMX *amx, cell *params)
{
celltrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie handle provided (%d)", params[1]);
return 0;
}
t->run_destructor(triedata_dtor);
t->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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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)
{
TrieData dummy;
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;
}
}
td->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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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)
{
TrieData dummy;
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;
}
}
td->setString(get_amxaddr(amx, params[3]));
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[2]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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)
{
TrieData dummy;
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;
}
}
td->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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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->getCell(ptr))
{
return 0;
}
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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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]))
{
return 0;
}
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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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]))
{
return 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]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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;
}
// native bool:TrieDeleteKey(Trie:handle, const key[]);
static cell AMX_NATIVE_CALL TrieDeleteKey(AMX *amx, cell *params)
{
celltrie *t = g_TrieHandles.lookup(params[1]);
if (t == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Invalid trie 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)
{
td->freeCells();
}
return t->remove(key) ? 1 : 0;
}
//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);
if (t == NULL)
{
return 0;
}
t->run_destructor(triedata_dtor);
if (g_TrieHandles.destroy(*ptr))
{
*ptr = 0;
return 1;
}
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 },
{ "TrieClear", TrieClear },
{ "TrieSetCell", TrieSetCell },
{ "TrieSetString", TrieSetString },
{ "TrieSetArray", TrieSetArray },
{ "TrieGetCell", TrieGetCell },
{ "TrieGetString", TrieGetString },
{ "TrieGetArray", TrieGetArray },
{ "TrieDeleteKey", TrieDeleteKey },
{ "TrieKeyExists", TrieKeyExists },
{ "TrieDestroy", TrieDestroy },
#ifndef NDEBUG
{ "TrieMallocCount", TrieMallocCount },
{ "TrieFreeCount", TrieFreeCount },
#endif
{ NULL, NULL }
};

211
amxmodx/trie_natives.h Normal file
View File

@ -0,0 +1,211 @@
#ifndef _TRIE_NATIVES_H_
#define _TRIE_NATIVES_H_
#include "amxmodx.h"
#include "sm_trie_tpl.h"
#include "CVector.h"
#define TRIE_DATA_UNSET 0
#define TRIE_DATA_CELL 1
#define TRIE_DATA_STRING 2
#define TRIE_DATA_ARRAY 3
#ifndef NDEBUG
extern size_t trie_malloc_count;
extern size_t trie_free_count;
#endif
class TrieData
{
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)
{
memcpy(out, m_data, (max > m_cellcount ? m_cellcount : max) * sizeof(cell));
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;
}
};
class TrieHandles
{
private:
CVector< KTrie< TrieData > *> m_tries;
public:
TrieHandles() { }
~TrieHandles()
{
this->clear();
}
void clear()
{
for (size_t i = 0; i < m_tries.size(); i++)
{
if (m_tries[i] != NULL)
{
delete m_tries[i];
}
}
m_tries.clear();
}
KTrie<TrieData> *lookup(int handle)
{
handle--;
if (handle < 0 || handle >= static_cast<int>(m_tries.size()))
{
return NULL;
}
return m_tries[handle];
}
int create()
{
for (size_t i = 0; i < m_tries.size(); i++)
{
if (m_tries[i] == NULL)
{
// reuse handle
m_tries[i] = new KTrie<TrieData>;
return static_cast<int>(i) + 1;
}
}
m_tries.push_back(new KTrie<TrieData>);
return m_tries.size();
}
bool destroy(int handle)
{
handle--;
if (handle < 0 || handle >= static_cast<int>(m_tries.size()))
{
return false;
}
if (m_tries[handle] == NULL)
{
return false;
}
delete m_tries[handle];
m_tries[handle] = NULL;
return true;
}
};
extern TrieHandles g_TrieHandles;
extern AMX_NATIVE_INFO trie_Natives[];
#endif

View File

@ -22,6 +22,7 @@
#include <vector>
#include <sorting>
#include <cellarray>
#include <celltrie>
#include <newmenus>
/* Function is called just after server activation.

View File

@ -0,0 +1,26 @@
#if defined _celltrie_included
#endinput
#endif
#define _celltrie_included
enum Trie
{
Invalid_Trie = 0
};
native Trie:TrieCreate();
native TrieClear(Trie:handle);
native TrieSetCell(Trie:handle, const key[], any:value);
native TrieSetString(Trie:handle, const key[], const value[]);
native TrieSetArray(Trie:handle, const key[], const any:buffer[], size);
native bool:TrieGetCell(Trie:handle, const key[], &any:value);
native bool:TrieGetString(Trie:handle, const key[], output[], outputsize);
native bool:TrieGetArray(Trie:handle, const key[], any:output[], outputsize);
native bool:TrieDeleteKey(Trie:handle, const key[]);
native bool:TrieKeyExists(Trie:handle, const key[]);
native TrieDestroy(&Trie:handle);

View File

@ -102,6 +102,9 @@ public arraytest1()
new Float:f;
new Array:a=ArrayCreate(1);
if (a == Invalid_Array)
{
}
for (new i=0; i<1000; i++)
{
f=float(i);

View File

@ -0,0 +1,169 @@
#include <amxmodx>
// These natives are only available in a debug build of amxmodx
native TrieFreeCount();
native TrieMallocCount();
new failcount = 0;
new passcount = 0;
public plugin_init()
{
register_plugin("Trie Test", AMXX_VERSION_STR, "AMXX Dev Team");
register_srvcmd("trietest", "trietest");
}
stock fail(const testname[])
{
server_print("[FAIL] %s", testname);
failcount++;
}
stock pass(const testname[])
{
server_print("[PASS] %s", testname);
passcount++;
}
stock done()
{
server_print("Finished. %d tests, %d failed", failcount + passcount, failcount);
}
stock check_frees()
{
if (TrieMallocCount() != TrieFreeCount())
fail("free count == malloc count");
else
pass("free count == malloc count");
server_print("malloc count: %d free count: %d", TrieMallocCount(), TrieFreeCount());
}
public trietest()
{
failcount = 0;
passcount = 0;
new bool:ok = true;
new Trie:t = TrieCreate();
new Trie:oldhandle = t; // Makes sure that the trie handle system recycles old handles
new key[32];
for (new i = 0; i < 100; i++)
{
formatex(key, charsmax(key), "K%dK", i);
TrieSetCell(t, key, i);
}
for (new i = 0; i < 100; i++)
{
formatex(key, charsmax(key), "K%dK", i);
new val;
if (!TrieGetCell(t, key, val))
{
server_print("TrieGetCell(%d, '%s', %d) failed", t, key, val);
ok = false;
}
else if (val != i)
{
server_print("val mismatch, expected: %d got: %d", i, val);
ok = false;
}
}
if (ok)
pass("Cell tests");
else
fail("Cell tests");
TrieClear(t);
TrieDestroy(t);
t = TrieCreate();
if (t == oldhandle)
pass("Recycle handles");
else
fail("Recycle handles");
ok = true;
for (new i = 0; i < 100; i++)
{
static val[32];
formatex(key, charsmax(key), "K%dK", i);
formatex(val, charsmax(val), "V%dV", i);
TrieSetString(t, key, val);
}
for (new i = 0; i < 100; i++)
{
formatex(key, charsmax(key), "K%dK", i);
static val[32];
static exp[32];
formatex(exp, charsmax(exp), "V%dV", i);
if (!TrieGetString(t, key, val, charsmax(val)))
{
server_print("TrieGetString(%d, '%s', %s) failed", t, key, val);
ok = false;
}
else if (!equal(val, exp))
{
server_print("val mismatch, key: '%s' expected: '%s' got: '%s'", key, exp, val);
ok = false;
}
}
if (ok)
pass("String tests");
else
fail("String tests");
TrieDestroy(t);
check_frees();
t = TrieCreate();
ok = true;
for (new i = 0; i < 1000; i++)
{
formatex(key, charsmax(key), "!%d!", i);
TrieSetString(t, key, key);
}
for (new i = 0; i < 1000; i++)
{
formatex(key, charsmax(key), "!%d!", i);
if (!TrieKeyExists(t, key))
{
ok = false;
server_print("Key '%s' does not exist", key);
}
else
{
if (!TrieDeleteKey(t, key))
{
server_print("Key '%s' could not be deleted", key);
ok = false;
}
}
}
if (ok)
pass("Exists/Delete");
else
fail("Exists/Delete");
check_frees();
TrieClear(t);
TrieDestroy(t);
check_frees();
done();
}