Add IntMap methodmap (#2018)

* Add new IntHashMap and IntMap natives

This is a continuation of #579.

[HashMap] Adds new IntHashMap and IntMap natives.

This patch makes the following changes
  * Refactors StringHashMap to a generic HashMap template
  * Adds the IntHashMap class
  * Adds new IntMap natives
  * Adds IntMap tests to the tries test suite

[HashMap] Reverted rename of CharsAndLength

[HashMap] Use more descriptive template names

[HashMap] Removed old-style natives

[HashMap] Removed IntHash class

[HashMap] Reverted some search & replace errors

Co-authored-by: Geoffrey McRae <geoff@hostfission.com>

* Fix spelling mistake + include

* Fix tries test

* Update tests with clone + ContainsKey

---------

Co-authored-by: Geoffrey McRae <geoff@hostfission.com>
Co-authored-by: Nicholas Hastings <nshastings@gmail.com>
This commit is contained in:
Kit o' Rifty 2024-10-31 16:35:41 -07:00 committed by GitHub
parent 6c3486d732
commit 34e9605519
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 880 additions and 42 deletions

View File

@ -38,7 +38,7 @@
#include <am-inlinelist.h>
#include <am-refcounting.h>
#include <am-utility.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include "sm_globals.h"
#include "sourcemm_api.h"

View File

@ -43,7 +43,7 @@
#include <compat_wrappers.h>
#include "concmd_cleaner.h"
#include "PlayerManager.h"
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
using namespace SourceHook;

View File

@ -34,7 +34,7 @@
#include "sm_globals.h"
#include "sourcemm_api.h"
#include <IForwardSys.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
namespace SourceMod {
class ICommandArgs;

View File

@ -36,7 +36,7 @@
#include <ITextParsers.h>
#include <IRootConsoleMenu.h>
#include <am-string.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
using namespace SourceMod;

View File

@ -38,7 +38,7 @@
#include <am-utility.h>
#include <am-hashset.h>
#include <am-hashmap.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sm_namehashset.h>
#include "sm_globals.h"
#include "sm_queue.h"

View File

@ -34,7 +34,7 @@
#include <IUserMessages.h>
#include "sourcemm_api.h"
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include "sm_stringutil.h"
#include "CellRecipientFilter.h"
#include "sm_globals.h"

View File

@ -34,7 +34,7 @@
#include <IADTFactory.h>
#include "common_logic.h"
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
using namespace SourceMod;

View File

@ -39,7 +39,7 @@
#include <sh_list.h>
#include <sh_string.h>
#include <IForwardSys.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sm_namehashset.h>
using namespace SourceHook;

View File

@ -36,7 +36,7 @@
#include <IGameConfigs.h>
#include <ITextParsers.h>
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sm_namehashset.h>
#include <unordered_set>

View File

@ -36,7 +36,7 @@
#include <am-string.h>
#include <am-utility.h>
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include "common_logic.h"
class CNativeOwner;

View File

@ -47,7 +47,7 @@
#include <sh_string.h>
#include "common_logic.h"
#include <IRootConsoleMenu.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sm_namehashset.h>
#include "ITranslator.h"
#include "IGameConfigs.h"

View File

@ -38,7 +38,7 @@
#include <am-utility.h>
#include <am-refcounting.h>
#include <sh_list.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sm_namehashset.h>
#include "common_logic.h"
#include "Native.h"

View File

@ -33,7 +33,7 @@
#define _INCLUDE_SOURCEMOD_TRANSLATOR_H_
#include "common_logic.h"
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include <sh_string.h>
#include <sh_vector.h>
#include "sm_memtable.h"

View File

@ -35,12 +35,14 @@
#include "common_logic.h"
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#include "sm_memtable.h"
#include <IHandleSys.h>
HandleType_t htCellTrie;
HandleType_t htSnapshot;
HandleType_t htIntCellTrie;
HandleType_t htIntSnapshot;
enum EntryType
{
@ -171,6 +173,11 @@ struct CellTrie
StringHashMap<Entry> map;
};
struct IntCellTrie
{
IntHashMap<Entry> map;
};
struct TrieSnapshot
{
TrieSnapshot()
@ -187,6 +194,19 @@ struct TrieSnapshot
BaseStringTable strings;
};
struct IntTrieSnapshot
{
IntTrieSnapshot() {}
size_t mem_usage()
{
return length * sizeof(int);
}
size_t length;
std::unique_ptr<int[]> keys;
};
class TrieHelpers :
public SMGlobalClass,
public IHandleTypeDispatch
@ -196,11 +216,15 @@ public: //SMGlobalClass
{
htCellTrie = handlesys->CreateType("Trie", this, 0, NULL, NULL, g_pCoreIdent, NULL);
htSnapshot = handlesys->CreateType("TrieSnapshot", this, 0, NULL, NULL, g_pCoreIdent, NULL);
htIntCellTrie = handlesys->CreateType("IntTrie", this, 0, NULL, NULL, g_pCoreIdent, NULL);
htIntSnapshot = handlesys->CreateType("IntTrieSnapshot", this, 0, NULL, NULL, g_pCoreIdent, NULL);
}
void OnSourceModShutdown()
{
handlesys->RemoveType(htSnapshot, g_pCoreIdent);
handlesys->RemoveType(htCellTrie, g_pCoreIdent);
handlesys->RemoveType(htIntSnapshot, g_pCoreIdent);
handlesys->RemoveType(htIntCellTrie, g_pCoreIdent);
}
public: //IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object)
@ -208,10 +232,21 @@ public: //IHandleTypeDispatch
if (type == htCellTrie)
{
delete (CellTrie *)object;
} else {
}
else if (type == htSnapshot)
{
TrieSnapshot *snapshot = (TrieSnapshot *)object;
delete snapshot;
}
else if (type == htIntCellTrie)
{
delete (IntCellTrie *)object;
}
else if (type == htIntSnapshot)
{
IntTrieSnapshot *snapshot = (IntTrieSnapshot *)object;
delete snapshot;
}
}
bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
{
@ -219,11 +254,28 @@ public: //IHandleTypeDispatch
{
CellTrie *pArray = (CellTrie *)object;
*pSize = sizeof(CellTrie) + pArray->map.mem_usage();
} else {
return true;
}
else if (type == htSnapshot)
{
TrieSnapshot *snapshot = (TrieSnapshot *)object;
*pSize = sizeof(TrieSnapshot) + snapshot->mem_usage();
return true;
}
return true;
else if (type == htIntCellTrie)
{
IntCellTrie *pArray = (IntCellTrie *)object;
*pSize = sizeof(IntCellTrie) + pArray->map.mem_usage();
return true;
}
else if (type == htIntSnapshot)
{
IntTrieSnapshot *snapshot = (IntTrieSnapshot *)object;
*pSize = sizeof(IntTrieSnapshot) + snapshot->mem_usage();
return true;
}
return false;
}
} s_CellTrieHelpers;
@ -242,6 +294,21 @@ static cell_t CreateTrie(IPluginContext *pContext, const cell_t *params)
return hndl;
}
static cell_t CreateIntTrie(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie = new IntCellTrie;
Handle_t hndl;
if ((hndl = handlesys->CreateHandle(htIntCellTrie, pTrie, pContext->GetIdentity(), g_pCoreIdent, NULL))
== BAD_HANDLE)
{
delete pTrie;
return BAD_HANDLE;
}
return hndl;
}
static cell_t SetTrieValue(IPluginContext *pContext, const cell_t *params)
{
CellTrie *pTrie;
@ -275,6 +342,38 @@ static cell_t SetTrieValue(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t SetIntTrieValue(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
int32_t key = params[2];
IntHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
if (!i.found())
{
if (!pTrie->map.add(i, key))
return 0;
i->value.setCell(params[3]);
return 1;
}
if (!params[4])
return 0;
i->value.setCell(params[3]);
return 1;
}
static cell_t SetTrieArray(IPluginContext *pContext, const cell_t *params)
{
CellTrie *pTrie;
@ -316,6 +415,46 @@ static cell_t SetTrieArray(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t SetIntTrieArray(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
if (params[4] < 0)
{
return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
}
int32_t key = params[2];
cell_t *array;
pContext->LocalToPhysAddr(params[3], &array);
IntHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
if (!i.found())
{
if (!pTrie->map.add(i, key))
return 0;
i->key = key;
i->value.setArray(array, params[4]);
return 1;
}
if (!params[5])
return 0;
i->value.setArray(array, params[4]);
return 1;
}
static cell_t SetTrieString(IPluginContext *pContext, const cell_t *params)
{
CellTrie *pTrie;
@ -350,6 +489,40 @@ static cell_t SetTrieString(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t SetIntTrieString(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
int32_t key = params[2];
char *val;
pContext->LocalToString(params[3], &val);
IntHashMap<Entry>::Insert i = pTrie->map.findForAdd(key);
if (!i.found())
{
if (!pTrie->map.add(i, key))
return 0;
i->value.setString(val);
return 1;
}
if (!params[4])
return 0;
i->value.setString(val);
return 1;
}
static cell_t ContainsKeyInTrie(IPluginContext *pContext, const cell_t *params)
{
CellTrie *pTrie;
@ -371,6 +544,26 @@ static cell_t ContainsKeyInTrie(IPluginContext *pContext, const cell_t *params)
return r.found() ? 1 : 0;
}
static cell_t ContainsKeyInIntTrie(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
int32_t key = params[2];
IntHashMap<Entry>::Result r = pTrie->map.find(key);
return r.found() ? 1 : 0;
}
static cell_t RemoveFromTrie(IPluginContext *pContext, const cell_t *params)
{
CellTrie *pTrie;
@ -396,6 +589,30 @@ static cell_t RemoveFromTrie(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t RemoveFromIntTrie(IPluginContext *pContext, const cell_t *params)
{
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
int32_t key = params[2];
IntHashMap<Entry>::Result r = pTrie->map.find(key);
if (!r.found())
return 0;
pTrie->map.remove(r);
return 1;
}
static cell_t ClearTrie(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
@ -415,6 +632,25 @@ static cell_t ClearTrie(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t ClearIntTrie(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
pTrie->map.clear();
return 1;
}
static cell_t GetTrieValue(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
@ -457,6 +693,47 @@ static cell_t GetTrieValue(IPluginContext *pContext, const cell_t *params)
return 0;
}
static cell_t GetIntTrieValue(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
int32_t key = params[2];
cell_t *pValue;
pContext->LocalToPhysAddr(params[3], &pValue);
IntHashMap<Entry>::Result r = pTrie->map.find(key);
if (!r.found())
return 0;
if (r->value.isCell())
{
*pValue = r->value.cell();
return 1;
}
// Maintain compatibility with an old bug. If an array was set with one
// cell, it was stored internally as a single cell. We now store as an
// actual array, but we make GetTrieValue() still work for this case.
if (r->value.isArray() && r->value.arrayLength() == 1)
{
*pValue = r->value.array()[0];
return 1;
}
return 0;
}
static cell_t GetTrieArray(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
@ -509,6 +786,56 @@ static cell_t GetTrieArray(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t GetIntTrieArray(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
if (params[4] < 0)
{
return pContext->ThrowNativeError("Invalid array size: %d", params[4]);
}
int32_t key = params[2];
cell_t *pValue, *pSize;
pContext->LocalToPhysAddr(params[3], &pValue);
pContext->LocalToPhysAddr(params[5], &pSize);
IntHashMap<Entry>::Result r = pTrie->map.find(key);
if (!r.found() || !r->value.isArray())
return 0;
if (!r->value.array())
{
*pSize = 0;
return 1;
}
if (!params[4])
return 1;
size_t length = r->value.arrayLength();
cell_t *base = r->value.array();
if (length > size_t(params[4]))
*pSize = params[4];
else
*pSize = length;
memcpy(pValue, base, sizeof(cell_t) * pSize[0]);
return 1;
}
static cell_t GetTrieString(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
@ -545,6 +872,41 @@ static cell_t GetTrieString(IPluginContext *pContext, const cell_t *params)
return 1;
}
static cell_t GetIntTrieString(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
if (params[4] < 0)
{
return pContext->ThrowNativeError("Invalid buffer size: %d", params[4]);
}
int32_t key = params[2];
cell_t *pSize;
pContext->LocalToPhysAddr(params[5], &pSize);
IntHashMap<Entry>::Result r = pTrie->map.find(key);
if (!r.found() || !r->value.isString())
return 0;
size_t written;
pContext->StringToLocalUTF8(params[3], params[4], r->value.c_str(), &written);
*pSize = (cell_t)written;
return 1;
}
static cell_t GetTrieSize(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
@ -563,6 +925,24 @@ static cell_t GetTrieSize(IPluginContext *pContext, const cell_t *params)
return pTrie->map.elements();
}
static cell_t GetIntTrieSize(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl;
IntCellTrie *pTrie;
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
hndl = params[1];
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
return pTrie->map.elements();
}
static cell_t CreateTrieSnapshot(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
@ -595,6 +975,38 @@ static cell_t CreateTrieSnapshot(IPluginContext *pContext, const cell_t *params)
return hndl;
}
static cell_t CreateIntTrieSnapshot(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
IntCellTrie *pTrie;
if ((err = handlesys->ReadHandle(hndl, htIntCellTrie, &sec, (void **)&pTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
IntTrieSnapshot *snapshot = new IntTrieSnapshot;
snapshot->length = pTrie->map.elements();
snapshot->keys = std::make_unique<int[]>(snapshot->length);
size_t i = 0;
for (IntHashMap<Entry>::iterator iter = pTrie->map.iter(); !iter.empty(); iter.next(), i++)
snapshot->keys[i] = iter->key;
assert(i == snapshot->length);
if ((hndl = handlesys->CreateHandle(htIntSnapshot, snapshot, pContext->GetIdentity(), g_pCoreIdent, NULL))
== BAD_HANDLE)
{
delete snapshot;
return BAD_HANDLE;
}
return hndl;
}
static cell_t TrieSnapshotLength(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
@ -612,6 +1024,23 @@ static cell_t TrieSnapshotLength(IPluginContext *pContext, const cell_t *params)
return snapshot->length;
}
static cell_t IntTrieSnapshotLength(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
IntTrieSnapshot *snapshot;
if ((err = handlesys->ReadHandle(hndl, htIntSnapshot, &sec, (void **)&snapshot))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
return snapshot->length;
}
static cell_t TrieSnapshotKeyBufferSize(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
@ -657,6 +1086,27 @@ static cell_t GetTrieSnapshotKey(IPluginContext *pContext, const cell_t *params)
return written;
}
static cell_t GetIntTrieSnapshotKey(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
Handle_t hndl = params[1];
IntTrieSnapshot *snapshot;
if ((err = handlesys->ReadHandle(hndl, htIntSnapshot, &sec, (void **)&snapshot))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", hndl, err);
}
unsigned index = params[2];
if (index >= snapshot->length)
return pContext->ThrowNativeError("Invalid index %d", index);
return snapshot->keys[index];
}
static cell_t CloneTrie(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
@ -707,6 +1157,56 @@ static cell_t CloneTrie(IPluginContext *pContext, const cell_t *params)
return hndl;
}
static cell_t CloneIntTrie(IPluginContext *pContext, const cell_t *params)
{
HandleError err;
HandleSecurity sec = HandleSecurity(pContext->GetIdentity(), g_pCoreIdent);
IntCellTrie *pOldTrie;
if ((err = handlesys->ReadHandle(params[1], htIntCellTrie, &sec, (void **)&pOldTrie))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
}
IntCellTrie *pNewTrie = new IntCellTrie;
Handle_t hndl = handlesys->CreateHandle(htIntCellTrie, pNewTrie, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (!hndl)
{
delete pNewTrie;
return hndl;
}
for (IntHashMap<Entry>::iterator it = pOldTrie->map.iter(); !it.empty(); it.next())
{
int32_t key = it->key;
IntHashMap<Entry>::Insert insert = pNewTrie->map.findForAdd(key);
if (pNewTrie->map.add(insert, key))
{
IntHashMap<Entry>::Result result = pOldTrie->map.find(key);
if (result->value.isCell())
{
insert->value.setCell(result->value.cell());
}
else if (result->value.isString())
{
insert->value.setString(result->value.c_str());
}
else if (result->value.isArray())
{
insert->value.setArray(result->value.array(), result->value.arrayLength());
}
else
{
handlesys->FreeHandle(hndl, NULL);
return pContext->ThrowNativeError("Unhandled data type encountered, file a bug and reference pr #852");
}
}
}
return hndl;
}
REGISTER_NATIVES(trieNatives)
{
{"ClearTrie", ClearTrie},
@ -740,9 +1240,26 @@ REGISTER_NATIVES(trieNatives)
{"StringMap.Snapshot", CreateTrieSnapshot},
{"StringMap.Clone", CloneTrie},
{"IntMap.IntMap", CreateIntTrie},
{"IntMap.Clear", ClearIntTrie},
{"IntMap.GetArray", GetIntTrieArray},
{"IntMap.GetString", GetIntTrieString},
{"IntMap.GetValue", GetIntTrieValue},
{"IntMap.ContainsKey", ContainsKeyInIntTrie},
{"IntMap.Remove", RemoveFromIntTrie},
{"IntMap.SetArray", SetIntTrieArray},
{"IntMap.SetString", SetIntTrieString},
{"IntMap.SetValue", SetIntTrieValue},
{"IntMap.Size.get", GetIntTrieSize},
{"IntMap.Snapshot", CreateIntTrieSnapshot},
{"IntMap.Clone", CloneIntTrie},
{"StringMapSnapshot.Length.get", TrieSnapshotLength},
{"StringMapSnapshot.KeyBufferSize", TrieSnapshotKeyBufferSize},
{"StringMapSnapshot.GetKey", GetTrieSnapshotKey},
{"IntMapSnapshot.Length.get", IntTrieSnapshotLength},
{"IntMapSnapshot.GetKey", GetIntTrieSnapshotKey},
{NULL, NULL},
};

View File

@ -36,7 +36,7 @@
#include "util.h"
#include <string>
#include <vector>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
struct ArgumentInfo {
ArgumentInfo() : name()

View File

@ -53,7 +53,7 @@
#include <cdll_int.h>
#if SOURCE_ENGINE == SE_CSGO
#include <am-hashset.h>
#include <sm_stringhashmap.h>
#include <sm_hashmap.h>
#endif
#include "SoundEmitterSystem/isoundemittersystembase.h"

View File

@ -167,6 +167,125 @@ methodmap StringMapSnapshot < Handle
public native int GetKey(int index, char[] buffer, int maxlength);
};
methodmap IntMap < Handle
{
// Creates a hash map. A hash map is a container that can map integers (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 SourceMod 1.6, tries have
// been internally replaced with hash tables, which have O(1) insertion time
// instead of O(n).
//
// The IntMap must be freed via delete or CloseHandle().
public native IntMap();
// Clones a hash map, returning a new handle with the same size and data.
// This should NOT be confused with CloneHandle. This is a completely new
// handle with the same data but no relation to the original. It should be
// closed when no longer needed with delete or CloseHandle().
//
// @return New handle to the cloned hash map
public native IntMap Clone();
// Sets a value in a hash map, either inserting a new entry or replacing an old one.
//
// @param key Key integer.
// @param value Value to store at this key.
// @param replace If false, operation will fail if the key is already set.
// @return True on success, false on failure.
public native bool SetValue(const int key, any value, bool replace=true);
// Sets an array value in a Map, either inserting a new entry or replacing an old one.
//
// @param key Key integer.
// @param array Array to store.
// @param num_items Number of items in the array.
// @param replace If false, operation will fail if the key is already set.
// @return True on success, false on failure.
public native bool SetArray(const int key, const any[] array, int num_items, bool replace=true);
// Sets a string value in a Map, either inserting a new entry or replacing an old one.
//
// @param key Key integer.
// @param value String to store.
// @param replace If false, operation will fail if the key is already set.
// @return True on success, false on failure.
public native bool SetString(const int key, const char[] value, bool replace=true);
// Retrieves a value in a Map.
//
// @param key Key integer.
// @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).
public native bool GetValue(const int key, any &value);
// Retrieves an array in a Map.
//
// @param key Key integer.
// @param array Buffer to store array.
// @param max_size Maximum size of array buffer.
// @param size Optional parameter to store the number of elements written to the 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).
public native bool GetArray(const int key, any[] array, int max_size, int &size=0);
// Retrieves a string in a Map.
//
// @param key Key integer.
// @param value Buffer to store value.
// @param max_size Maximum size of string buffer.
// @param size Optional parameter to store the number of bytes written to the 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).
public native bool GetString(const int key, char[] value, int max_size, int &size=0);
// Checks whether a key is present in a Map.
//
// @param key Key integer.
// @return True if the key has been found, else false.
public native bool ContainsKey(const int key);
// Removes a key entry from a Map.
//
// @param key Key integer.
// @return True on success, false if the value was never set.
public native bool Remove(const int key);
// Clears all entries from a Map.
public native void Clear();
// Create a snapshot of the map's keys. See IntMapSnapshot.
public native IntMapSnapshot Snapshot();
// Retrieves the number of elements in a map.
property int Size {
public native get();
}
};
// A IntMapSnapshot is created via IntMap.Snapshot(). It captures the
// keys on a map so they can be read. Snapshots must be freed with delete or
// CloseHandle().
methodmap IntMapSnapshot < Handle
{
// Returns the number of keys in the map snapshot.
property int Length {
public native get();
}
// Retrieves the key integer of a given key in a map snapshot.
//
// @param index Key index (starting from 0).
// @return The key integer
// @error Index out of range.
public native int GetKey(int index);
};
/**
* 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

View File

@ -13,6 +13,7 @@ public Plugin:myinfo =
public OnPluginStart()
{
RegServerCmd("test_maps", RunTests);
RegServerCmd("test_int_maps", RunIntTests);
}
public Action:RunTests(argc)
@ -161,3 +162,189 @@ public Action:RunTests(argc)
return Plugin_Handled;
}
public Action:RunIntTests(argc)
{
IntMap map = new IntMap();
for (new i = 0; i < 64; i++) {
if (!map.SetValue(i, i))
ThrowError("set map to %d failed", i);
if (!map.ContainsKey(i))
ThrowError("map contains %d failed", i)
new value;
if (!map.GetValue(i, value))
ThrowError("get map %d", i);
if (value != i)
ThrowError("get map %d == %d", i, i);
}
// Setting 17 without replace should fail.
new value;
if (map.SetValue(17, 999, false))
ThrowError("set map 17 should fail");
if (!map.GetValue(17, value) || value != 17)
ThrowError("value at 17 not correct");
if (!map.SetValue(17, 999))
ThrowError("set map 17 = 999 should succeed");
if (!map.GetValue(17, value) || value != 999)
ThrowError("value at 17 not correct");
// Check size is 64.
if (map.Size != 64)
ThrowError("map size not 64");
// Check 100 is not found.
int array[64];
char string[64];
if (map.ContainsKey(100) ||
map.GetValue(100, value) ||
map.GetArray(100, array, sizeof(array)) ||
map.GetString(100, string, sizeof(string)))
{
ThrowError("map should not have 100");
}
// Check that 17 is not a string or array.
if (map.GetArray(17, array, sizeof(array)) ||
map.GetString(17, string, sizeof(string)))
{
ThrowError("entry 17 should not be an array or string");
}
// Strings.
if (!map.SetString(17, "hellokitty"))
ThrowError("17 should be string");
if (!map.GetString(17, string, sizeof(string)) ||
strcmp(string, "hellokitty") != 0)
{
ThrowError("17 should be hellokitty");
}
if (map.GetValue(17, value) ||
map.GetArray(17, array, sizeof(array)))
{
ThrowError("entry 17 should not be an array or string");
}
// Arrays.
new data[5] = { 93, 1, 2, 3, 4 };
if (!map.SetArray(17, data, 5))
ThrowError("couldn't set 17 to 5-entry array");
if (!map.GetArray(17, array, sizeof(array)))
ThrowError("couldn't fetch 5-entry array");
for (new i = 0; i < 5; i++) {
if (data[i] != array[i])
ThrowError("17 slot %d should be %d, got %d", i, data[i], array[i]);
}
if (map.GetValue(17, value) ||
map.GetString(17, string, sizeof(string)))
{
ThrowError("entry 17 should not be a value or string");
}
if (!map.SetArray(17, data, 1))
ThrowError("couldn't set 17 to 1-entry array");
// Check that we fixed an old bug where 1-entry arrays where cells
if (!map.GetArray(17, array, sizeof(array), value))
ThrowError("couldn't fetch 1-entry array");
if (value != 1)
ThrowError("array size mismatch (%d, expected %d)", value, 1);
// Check that we maintained backward compatibility.
if (!map.GetValue(17, value))
ThrowError("backwards compatibility failed");
if (value != data[0])
ThrowError("wrong value (%d, expected %d)", value, data[0]);
// Remove "17".
if (!map.Remove(17))
ThrowError("17 should have been removed");
if (map.Remove(17))
ThrowError("17 should not exist");
if (map.ContainsKey(17) ||
map.GetValue(17, value) ||
map.GetArray(17, array, sizeof(array)) ||
map.GetString(17, string, sizeof(string)))
{
ThrowError("map should not have a 17");
}
map.Clear();
if (map.Size)
ThrowError("size should be 0");
map.SetString(42, "time!");
map.SetString(84, "bees");
map.SetString(126, "egg");
IntMapSnapshot keys = map.Snapshot();
{
if (keys.Length != 3)
ThrowError("map snapshot length should be 3");
bool found[3];
for (new i = 0; i < keys.Length; i++) {
decl key = keys.GetKey(i);
if (key == 42)
found[0] = true;
else if (key == 84)
found[1] = true;
else if (key == 126)
found[2] = true;
else
ThrowError("unexpected key: %d", key);
}
if (!found[0] || !found[1] || !found[2])
ThrowError("did not find all keys");
}
delete keys;
map.SetValue(10240, 6744);
map.SetValue(8, 13);
new cloneData[5] = { 12, 23, 55, 1, 2 };
new cloneArr[5];
map.SetArray(9102, cloneData, 5);
IntMap clone = map.Clone();
if (clone.Size != map.Size)
ThrowError("cloned map size mismatch (%d, expected %d)", clone.Size, map.Size);
if (!clone.GetString(42, string, sizeof(string)))
ThrowError("cloned map entry 42 should be a string");
if (strcmp(string, "time!") != 0)
ThrowError("cloned map entry 42 should be \"time!\"");
if (!clone.GetString(84, string, sizeof(string)))
ThrowError("cloned map entry 84 should be a string");
if (strcmp(string, "bees") != 0)
ThrowError("cloned map entry 84 should be \"bees\"");
if (!clone.GetString(126, string, sizeof(string)))
ThrowError("cloned map entry 126 should be a string");
if (strcmp(string, "egg") != 0)
ThrowError("cloned map entry 126 should be \"egg\"");
if (!clone.GetValue(10240, value))
ThrowError("cloned map entry 10240 should be a value");
if (value != 6744)
ThrowError("cloned map entry 10240 should be 6744")
if (!clone.GetValue(8, value))
ThrowError("cloned map entry 8 should be a value");
if (value != 13)
ThrowError("cloned map entry 8 should be 13")
if (!clone.GetArray(9102, cloneArr, 5))
ThrowError("cloned map entry 9102 should be an array");
for (new i = 0; i < 5; i++) {
if (cloneData[i] != cloneArr[i])
ThrowError("cloned map entry 9102 slot %d should be %d, got %d", i, cloneData[i], cloneArr[i]);
}
delete clone;
PrintToServer("All tests passed!");
delete map;
return Plugin_Handled;
}

View File

@ -98,16 +98,25 @@ namespace detail
return key.hash();
}
};
struct IntHashMapPolicy
{
static inline bool matches(const int32_t lookup, const int32_t compare) {
return lookup == compare;
}
static inline uint32_t hash(const int32_t key) {
return ke::HashInt32(key);
}
};
}
template <typename T>
class StringHashMap
template <typename T, typename KeyStoreType, typename Policy, typename ContainerType, typename KeyLookupType>
class HashMap
{
typedef detail::CharsAndLength CharsAndLength;
typedef ke::HashMap<std::string, T, detail::StringHashMapPolicy> Internal;
typedef ke::HashMap<KeyStoreType, T, Policy> Internal;
public:
StringHashMap()
HashMap()
: internal_(ke::SystemAllocatorPolicy()),
memory_used_(0)
{
@ -120,9 +129,9 @@ public:
typedef typename Internal::iterator iterator;
// Some KTrie-like helper functions.
bool retrieve(const char *aKey, T *aResult = NULL)
bool retrieve(const KeyLookupType &aKey, T *aResult = NULL)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Result r = internal_.find(key);
if (!r.found())
return false;
@ -131,9 +140,9 @@ public:
return true;
}
bool retrieve(const char *aKey, T **aResult)
bool retrieve(const KeyLookupType &aKey, T **aResult)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Result r = internal_.find(key);
if (!r.found())
return false;
@ -141,23 +150,23 @@ public:
return true;
}
Result find(const char *aKey)
Result find(const KeyLookupType &aKey)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
return internal_.find(key);
}
bool contains(const char *aKey)
bool contains(const KeyLookupType &aKey)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Result r = internal_.find(key);
return r.found();
}
template <typename UV>
bool replace(const char *aKey, UV &&value)
bool replace(const KeyLookupType &aKey, UV &&value)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Insert i = internal_.findForAdd(key);
if (!i.found())
{
@ -170,9 +179,9 @@ public:
}
template <typename UV>
bool insert(const char *aKey, UV &&value)
bool insert(const KeyLookupType &aKey, UV &&value)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Insert i = internal_.findForAdd(key);
if (i.found())
return false;
@ -182,9 +191,9 @@ public:
return true;
}
bool remove(const char *aKey)
bool remove(const KeyLookupType &aKey)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
Result r = internal_.find(key);
if (!r.found())
return false;
@ -219,9 +228,9 @@ public:
}
Insert findForAdd(const char *aKey)
Insert findForAdd(const KeyLookupType &aKey)
{
CharsAndLength key(aKey);
ContainerType key(aKey);
return internal_.findForAdd(key);
}
@ -234,7 +243,7 @@ public:
}
// Only value needs to be set after.
bool add(Insert &i, const char *aKey)
bool add(Insert &i, const KeyLookupType &aKey)
{
if (!internal_.add(i, aKey))
return false;
@ -246,6 +255,12 @@ private:
size_t memory_used_;
};
template <typename T>
using StringHashMap = HashMap<T, std::string, detail::StringHashMapPolicy, detail::CharsAndLength, const char *>;
template <typename T>
using IntHashMap = HashMap<T, int32_t, detail::IntHashMapPolicy, const int32_t, int32_t>;
}
#endif // _include_sourcemod_hashtable_h_

View File

@ -41,7 +41,7 @@
#include <am-allocator-policies.h>
#include <am-hashmap.h>
#include <am-string.h>
#include "sm_stringhashmap.h"
#include "sm_hashmap.h"
namespace SourceMod
{