177 lines
3.8 KiB
D
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;
|
|
|