lox-d/src/clox/container/table.d
2025-06-07 23:52:16 +00:00

177 lines
3.8 KiB
D

module clox.container.table;
import core.stdc.string;
import core.stdc.stdlib;
import clox.object;
import clox.value;
import clox.memory;
enum TABLE_MAX_LOAD = 0.75;
struct Table{
size_t count, capacity;
Entry* entries;
struct Entry{
Obj.String* key;
Value value;
}
void initialise(){
count = capacity = 0;
entries = null;
}
void free(){
if(entries !is null)
FREE_ARRAY!Entry(entries, capacity);
initialise();
}
private void adjustCapacity(size_t cap){
Entry* ent = ALLOCATE!Entry(cap);
for(int i = 0; i < cap; i++){
ent[i].key = null;
ent[i].value = Value.init;
}
count = 0;
for(int i = 0; i < capacity; i++){
Entry* entry = &entries[i];
if(entry.key is null)
continue;
Entry* dest = findEntry(entries, capacity, entry.key);
dest.key = entry.key;
dest.value = entry.value;
count++;
}
if(entries !is null)
FREE_ARRAY!Entry(entries, capacity);
entries = ent;
capacity = cap;
}
private Entry* findEntry(Entry* entries, size_t capacity, in Obj.String* key){
uint index = key.hash % capacity;
Entry* tombstone = null;
while(true){
Entry* entry = &entries[index];
if(entry.key is null){
if(entry.value.isType(Value.Type.None)){
return tombstone !is null ? tombstone : entry;
} else {
if(tombstone is null)
tombstone = entry;
}
} else if(entry.key is key){
return entry;
}
index = (index + 1) % capacity;
}
}
bool set(Obj.String* key, Value value){
if(count + 1 > capacity * TABLE_MAX_LOAD){
size_t cap = GROW_CAPACITY(capacity);
adjustCapacity(cap);
}
Entry* entry = findEntry(entries, capacity, key);
bool isNewKey = entry.key == null;
if(isNewKey && entry.value.isType(Value.Type.None))
count++;
entry.key = key;
entry.value = value;
return isNewKey;
}
bool get(in Obj.String* key, out Value value){
if(count == 0)
return false;
Entry* entry = findEntry(entries, capacity, key);
if(entry.key is null)
return false;
value = entry.value;
return true;
}
bool del(in Obj.String* key){
if(count == 0)
return false;
Entry* entry = findEntry(entries, capacity, key);
if(entry.key is null)
return false;
entry.key = null;
entry.value = Value.from(true);
return true;
}
void addAll(Table* from){
for(int i = 0; i < from.capacity; i++){
Entry* entry = &from.entries[i];
if(entry.key !is null)
set(entry.key, entry.value);
}
}
Obj.String* findString(const char[] chars, uint hash){
if(count == 0)
return null;
uint index = hash % capacity;
while(true){
Entry* entry = &entries[index];
if(entry.key == null){
if(entry.value.isType(Value.Type.None))
return null;
} else if(entry.key.hash == hash && entry.key.chars == chars){
return entry.key;
}
index = (index + 1) % capacity;
}
}
}
unittest{
import clox.vm : vm;
Table tbl;
tbl.initialise();
scope(exit){
tbl.free();
vm.free();
}
assert(tbl.count == 0);
Obj.String* str = Obj.String.copy("hello");
tbl.set(str, Value.from(true));
assert(tbl.count == 1);
assert(tbl.findString("hello", str.hash) is str);
Value val;
assert(tbl.get(str, val));
assert(val.asBoolean == true);
assert(tbl.del(str));
assert(tbl.get(str, val) == false);
assert(val.isType(Value.Type.None));
assert(tbl.count == 1);
foreach(i; 0..25){
Obj.String* str2 = Obj.String.copy("hi 2");
tbl.set(str2, Value.from(i));
assert(tbl.get(str2, val));
assert(val.asNumber == i);
}
assert(tbl.get(str, val) == false);
tbl.set(str, Value.from(true));
assert(tbl.get(str, val));
assert(val.asBoolean == true);
}
uint jenkinsOneAtATimeHash(T)(in T[] key){
uint hash = 0;
foreach(v; key){
hash += v;
hash += hash << 10;
hash ^= hash >> 6;
}
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
return hash;
}
alias hashString = jenkinsOneAtATimeHash;