thalassa/lib/scriptpp/scrmap.cpp

327 lines
8.4 KiB
C++
Raw Normal View History

2026-03-19 01:23:52 +00:00
// +-------------------------------------------------------------------------+
// | Script Plus Plus vers. 0.3.72 |
// | Copyright (c) Andrey V. Stolyarov <croco at croco dot net> 2003--2023 |
// | ----------------------------------------------------------------------- |
// | This is free software. Permission is granted to everyone to use, copy |
// | or modify this software under the terms and conditions of |
// | GNU LESSER GENERAL PUBLIC LICENSE, v. 2.1 |
// | as published by Free Software Foundation (see the file LGPL.txt) |
// | |
// | Please visit http://www.croco.net/software/scriptpp to get a fresh copy |
// | ----------------------------------------------------------------------- |
// | This code is provided strictly and exclusively on the "AS IS" basis. |
// | !!! THERE IS NO WARRANTY OF ANY KIND, NEITHER EXPRESSED NOR IMPLIED !!! |
// +-------------------------------------------------------------------------+
#include "scrmap.hpp"
//#include <stdio.h> // FOR DEBUGGING!!!
typedef unsigned long script_hash_t;
static const script_hash_t hash_multiplier = 0x9c406bb5;
// Recommended by Knuth
static const script_hash_t hash_xor_op = 0x12fade34;
// arbitrary
static const unsigned long hash_sizes[] = {
11, /* > 8 */
17, /* > 16 */
37, /* > 32 */
67, /* > 64 */
131, /* > 128 */
257, /* > 256 */
521, /* > 512 */
1031, /* > 1024 */
2053, /* > 2048 */
4099, /* > 4096 */
8209, /* > 8192 */
16411, /* > 16384 */
32771, /* > 32768 */
65537, /* > 65536 */
131101, /* > 131072 */
262147, /* > 262144 */
524309, /* > 524288 */
1048583, /* > 1048576 */
2097169, /* > 2097152 */
4194319, /* > 4194304 */
8388617, /* > 8388608 */
16777259, /* > 16777216 */
33554467, /* > 33554432 */
67108879, /* > 67108864 */
134217757, /* > 134217728 */
/* we don't need sizes larger than 2**27; */
/* allocating such an array would crash the program anyway */
0 /* end-of-table mark */
};
static script_hash_t UniversalHash(script_hash_t l)
{
return hash_multiplier * (l ^ hash_xor_op);
}
static script_hash_t CharStringSum(const char *s)
{
register script_hash_t res = 7; // Arbitrary.
register int i = 0;
while(*s) {
res ^= (((script_hash_t) *s) << (8*(i%4)));
// Every byte is XOR'ed with 0th, 1st, 2nd, 3rd, 0th... byte of res.
s++; i++;
}
return res;
}
static script_hash_t script_hash(const ScriptVariable &s)
{
return s.IsInvalid() ? 0 : UniversalHash(CharStringSum(s.c_str()));
}
ScriptSet::ScriptSet()
{
dim = hash_sizes[0];
table = new ScriptVariableInv[dim];
itemcount = 0;
}
ScriptSet::~ScriptSet()
{
delete[] table;
}
bool ScriptSet::Contains(const ScriptVariable &key) const
{
return table[GetItemPosition(key)].IsValid();
}
bool ScriptSet::AddItem(const ScriptVariable &key)
{
int pos = ProvideItemPosition(key);
if(table[pos].IsInvalid()) {
table[pos] = key;
itemcount++;
return true;
}
return false;
}
int ScriptSet::AddItemWithPos(const ScriptVariable &key)
{
int pos = ProvideItemPosition(key);
if(table[pos].IsInvalid()) {
table[pos].ScriptVariable::operator=(key);
itemcount++;
}
return pos;
}
int ScriptSet::GetItemPosition(const ScriptVariable &key) const
{
script_hash_t h = script_hash(key);
int pos = h % dim;
while(table[pos].IsValid()) {
if(table[pos] == key) // found !
return pos;
pos = pos ? pos-1 : dim-1;
}
return pos;
}
int ScriptSet::FindItemPos(const ScriptVariable &key) const
{
int pos = GetItemPosition(key);
if(table[pos].IsInvalid())
return -1;
return pos;
}
int ScriptSet::ProvideItemPosition(const ScriptVariable &key)
{
int pos = GetItemPosition(key);
if(table[pos].IsInvalid()) {
// not found -- we'll need to reserve a slot
if(itemcount * 3 > dim * 2) {
// This is rare event so we can leave it unoptimized
ResizeTable();
// table changed; recompute the pos
pos = GetItemPosition(key);
}
}
return pos;
}
bool ScriptSet::RemoveItem(const ScriptVariable& key)
{
int pos = script_hash(key) % dim;
while(table[pos].IsValid()) {
if(table[pos] == key) {
DoRemoveItem(pos);
return true;
}
pos = pos ? pos-1 : dim-1;
}
// Not found
return false;
}
void ScriptSet::Clear()
{
for(unsigned int i = 0; i< dim; i++)
table[i].Invalidate();
itemcount = 0;
}
void ScriptSet::DoRemoveItem(int pos)
{
/* We implement the Don Knuth' algorythm of removing items */
/* see vol. 3, section 6.4 */
itemcount--;
table[pos].Invalidate();
for(long i = (pos?pos:dim) - 1; table[i].IsValid(); i = (i?i:dim) - 1) {
long r = script_hash(table[i]) % dim;
if((i <= r && r < pos) || ((pos < i) && (r < pos || i <= r)))
continue;
table[pos] = table[i];
table[i].Invalidate();
pos = i;
}
}
void ScriptSet::ResizeTable()
{
unsigned long newdim = 0;
for(int i=1; hash_sizes[i]; i++) {
if(hash_sizes[i] > dim) {
newdim = hash_sizes[i];
break;
}
}
//printf(" ** RESIZE(%ld): %ld -> %ld\n", Count(), dim, newdim);
/* if(!newdim) // this actually can not happen */
long olddim = dim;
ScriptVariable *oldtable = table;
dim = newdim;
table = new ScriptVariableInv[newdim];
itemcount = 0;
void *user_values = HookResizeStart(newdim);
for(long i = 0; i<olddim; i++) {
if(oldtable[i].IsValid()) {
int pos = AddItemWithPos(oldtable[i]);
HookResizeReadd(user_values, i, pos);
}
}
HookResizeFinish(user_values);
delete [] oldtable;
}
ScriptSet::Iterator::Iterator(const ScriptSet &ref)
{
tbl = &ref;
idx = 0;
lim = ref.dim;
}
ScriptVariable ScriptSet::Iterator::GetNext()
{
ScriptVariable res;
int pos;
if(GetNext(res, pos)) {
return res;
} else {
return ScriptVariableInv();
}
}
bool ScriptSet::Iterator::GetNext(ScriptVariable &key, int &pos)
{
while(idx<lim && tbl->table[idx].IsInvalid())
idx++;
if(idx>=lim)
return false;
key = tbl->table[idx];
pos = idx;
idx++;
return true;
}
//////////////////////////////////////////////////
ScriptMap::ScriptMap()
: ScriptSet()
{
val = new ScriptVariableInv[GetTableSize()];
}
ScriptMap::~ScriptMap()
{
delete [] val;
}
bool ScriptMap::AddItem(const ScriptVariable &key, const ScriptVariable &v)
{
int pos = AddItemWithPos(key);
if(val[pos].IsValid())
return false;
val[pos] = v;
return true;
}
void ScriptMap::SetItem(const ScriptVariable &key, const ScriptVariable &v)
{
int pos = AddItemWithPos(key);
val[pos] = v;
}
ScriptVariable ScriptMap::GetItem(const ScriptVariable &key) const
{
int pos = FindItemPos(key);
if(pos == -1)
return ScriptVariableInv();
return val[pos];
}
ScriptVariable& ScriptMap::operator[](const ScriptVariable& key)
{
int pos = AddItemWithPos(key);
// don't remove the following if! This is for a reason, to allow
// using += or the like on the result of 'indexation'
// Use Contains() to determine whether the index is valid or not
if(val[pos].IsInvalid())
val[pos] = "";
return val[pos];
}
void ScriptMap::Clear()
{
int c = GetTableSize();
ScriptSet::Clear();
for(int i=0; i<c; i++)
val[i].Invalidate();
}
void* ScriptMap::HookResizeStart(int newsize)
{
ScriptVariable *old_val = val;
val = new ScriptVariableInv[newsize];
return old_val;
}
void ScriptMap::HookResizeReadd(void *userdata, int oldpos, int newpos)
{
val[newpos] = ((ScriptVariable*)userdata)[oldpos];
}
void ScriptMap::HookResizeFinish(void *userdata)
{
ScriptVariable *p = (ScriptVariable*)userdata;
delete[] p;
}