// +-------------------------------------------------------------------------+ // | Script Plus Plus vers. 0.3.72 | // | Copyright (c) Andrey V. Stolyarov 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 // 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; itable[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