amxmodx/amxmodx/trie_natives.h
Vincent Herbet 1dc1f1b9c4 Introduce Trie Iterators (#413)
* TrieIter: Add possibility to obtain a new'd HashTable iterator

* TrieIter: Add CellTrieIter and storage

* TrieIter: Implement TrieIterCreate

* TrieIter: Implement TrieIterEnded

* TrieIter: Implement TrieIterMore

* TrieIter: Implement TrieIterGetKey

* TrieIter: Implement TrieIterGetSize

* TrieIter: Implement TrieIterGetCell

* TrieIter: Implement TrieIterGetString

* TrieIter: Implement TrieIterGetArray

* TrieIter: Implement TrieIterDestroy

* TrieIter: Invalidate any mutating change that is key addition or key removal

* TrieIter: Clean up the handles at map change

* TrieITer; Add iter tests to trietest.sma

* TrieIter: Fix linux compilation

* TrieIter: Rename TrieIterMore to TrieIterNext

* TrieIter: Adjust documentation

* TrieITer; Adjust trietest.sma

* TrieIter: Create a custom StringHashMap class instead

+ used a copy of |iterator| instead of dynamic allocation
+ initialized vars directly in constructor
+ added a nested iteration test
2017-08-01 15:05:27 +02:00

289 lines
5.1 KiB
C++

// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
#ifndef _TRIE_NATIVES_H_
#define _TRIE_NATIVES_H_
#include "amxmodx.h"
#include <sm_stringhashmap.h>
#include <sm_memtable.h>
#include "natives_handles.h"
#include <amtl/am-uniqueptr.h>
enum EntryType
{
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(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) = 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_;
};
template <typename T>
class StringHashMapCustom
{
typedef StringHashMap<T> Internal;
public:
StringHashMapCustom() : mod_count_(0)
{}
public:
typedef typename Internal::Result Result;
typedef typename Internal::Insert Insert;
typedef typename Internal::iterator iterator;
public:
size_t elements() const
{
return internal_.elements();
}
iterator iter()
{
return internal_.iter();
}
bool contains(const char *aKey)
{
return internal_.contains(aKey);
}
bool replace(const char *aKey, const T &value)
{
return internal_.contains(aKey, value);
}
bool insert(const char *aKey, const T &value)
{
if (internal_.insert(aKey, value))
{
mod_count_++;
return true;
}
return false;
}
bool remove(const char *aKey)
{
if (internal_.remove(aKey))
{
mod_count_++;
return true;
}
return false;
}
void clear()
{
mod_count_++;
internal_.clear();
}
void remove(Result &r)
{
mod_count_++;
internal_.remove(r);
}
Result find(const char *aKey)
{
return internal_.find(aKey);
}
Insert findForAdd(const char *aKey)
{
return internal_.findForAdd(aKey);
}
bool add(Insert &i, const char *aKey)
{
if (internal_.add(i, aKey))
{
mod_count_++;
return true;
}
return false;
}
bool add(Insert &i)
{
if (internal_.add(i))
{
mod_count_++;
return true;
}
return false;
}
public:
size_t mod_count() const
{
return mod_count_;
}
private:
Internal internal_;
size_t mod_count_;
};
struct CellTrie
{
StringHashMapCustom<Entry> map;
};
struct CellTrieIter
{
CellTrieIter(CellTrie *_trie) : trie(_trie), iter(_trie->map.iter()), mod_count(_trie->map.mod_count())
{}
CellTrie *trie;
StringHashMapCustom<Entry>::iterator iter;
size_t mod_count;
};
struct TrieSnapshot
{
TrieSnapshot()
: strings(128)
{ }
size_t mem_usage()
{
return length * sizeof(int) + strings.GetMemTable()->GetMemUsage();
}
size_t length;
ke::AutoPtr<int[]> keys;
BaseStringTable strings;
};
extern NativeHandle<CellTrie> TrieHandles;
extern NativeHandle<CellTrieIter> TrieIterHandles;
extern NativeHandle<TrieSnapshot> TrieSnapshotHandles;
extern AMX_NATIVE_INFO trie_Natives[];
#endif