Hash Tables 20
This commit is contained in:
parent
3b234814fa
commit
a7b7348f61
20 changed files with 868 additions and 837 deletions
176
src/clox/container/table.d
Normal file
176
src/clox/container/table.d
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
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{
|
||||
uint 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(uint 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, int 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){
|
||||
uint 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{
|
||||
Table tbl;
|
||||
tbl.initialise();
|
||||
scope(exit)
|
||||
tbl.free();
|
||||
scope(exit)
|
||||
freeObjects();
|
||||
|
||||
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("hello");
|
||||
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;
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue