[Chapter20&Hash] - Adding the hashmaps
This commit is contained in:
parent
e218782499
commit
eafabfcb00
@ -3,6 +3,7 @@
|
||||
|
||||
#include "memory.h"
|
||||
#include "object.h"
|
||||
#include "table.h"
|
||||
#include "value.h"
|
||||
#include "vm.h"
|
||||
|
||||
@ -18,21 +19,45 @@ static Obj* allocateObject(size_t size, ObjType type) {
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
static ObjString* allocateString(char* chars, int length) {
|
||||
static ObjString* allocateString(char* chars, int length,
|
||||
uint32_t hash)
|
||||
{
|
||||
ObjString* string = ALLOCATE_OBJ(ObjString, OBJ_STRING);
|
||||
string->length = length;
|
||||
string->chars = chars;
|
||||
string->hash = hash;
|
||||
tableSet(&vm.strings, string, NIL_VAL);
|
||||
return string;
|
||||
}
|
||||
static uint32_t hashString(const char* key, int length) {
|
||||
uint32_t hash = 2166136261u;
|
||||
for (int i = 0; i < length; i++) {
|
||||
hash ^= (uint8_t)key[i];
|
||||
hash *= 16777619;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
ObjString* takeString(char* chars, int length) {
|
||||
return allocateString(chars, length);
|
||||
uint32_t hash = hashString(chars, length);
|
||||
ObjString* interned = tableFindString(&vm.strings, chars, length,
|
||||
hash);
|
||||
if (interned != NULL) {
|
||||
FREE_ARRAY(char, chars, length + 1);
|
||||
return interned;
|
||||
}
|
||||
|
||||
return allocateString(chars, length, hash);
|
||||
}
|
||||
ObjString* copyString(const char* chars, int length) {
|
||||
uint32_t hash = hashString(chars, length);
|
||||
ObjString* interned = tableFindString(&vm.strings, chars, length,
|
||||
hash);
|
||||
if (interned != NULL) return interned;
|
||||
|
||||
char* heapChars = ALLOCATE(char, length + 1);
|
||||
memcpy(heapChars, chars, length);
|
||||
heapChars[length] = '\0';
|
||||
return allocateString(heapChars, length);
|
||||
return allocateString(heapChars, length, hash);
|
||||
}
|
||||
void printObject(Value value) {
|
||||
switch (OBJ_TYPE(value)) {
|
||||
|
@ -21,7 +21,8 @@ struct Obj {
|
||||
struct ObjString {
|
||||
Obj obj;
|
||||
int length;
|
||||
char* chars;
|
||||
char* chars;
|
||||
uint32_t hash;
|
||||
};
|
||||
ObjString* takeString(char* chars, int length);
|
||||
ObjString* copyString(const char* chars, int length);
|
||||
|
130
clox/table.c
Normal file
130
clox/table.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "object.h"
|
||||
#include "table.h"
|
||||
#include "value.h"
|
||||
|
||||
#define TABLE_MAX_LOAD 0.75
|
||||
|
||||
void initTable(Table* table) {
|
||||
table->count = 0;
|
||||
table->capacity = 0;
|
||||
table->entries = NULL;
|
||||
}
|
||||
|
||||
void freeTable(Table* table) {
|
||||
FREE_ARRAY(Entry, table->entries, table->capacity);
|
||||
initTable(table);
|
||||
}
|
||||
static Entry* findEntry(Entry* entries, int capacity,
|
||||
ObjString* key) {
|
||||
uint32_t index = key->hash % capacity;
|
||||
Entry* tombstone = NULL;
|
||||
for (;;) {
|
||||
Entry* entry = &entries[index];
|
||||
if (entry->key == NULL) {
|
||||
if (IS_NIL(entry->value)) {
|
||||
// Empty entry.
|
||||
return tombstone != NULL ? tombstone : entry;
|
||||
} else {
|
||||
// We found a tombstone.
|
||||
if (tombstone == NULL) tombstone = entry;
|
||||
}
|
||||
} else if (entry->key == key) {
|
||||
// We found the key.
|
||||
return entry;
|
||||
}
|
||||
|
||||
index = (index + 1) % capacity;
|
||||
}
|
||||
}
|
||||
|
||||
static void adjustCapacity(Table* table, int capacity) {
|
||||
Entry* entries = ALLOCATE(Entry, capacity);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
entries[i].key = NULL;
|
||||
entries[i].value = NIL_VAL;
|
||||
}
|
||||
table->count = 0;
|
||||
for (int i = 0; i < table->capacity; i++) {
|
||||
Entry* entry = &table->entries[i];
|
||||
if (entry->key == NULL) continue;
|
||||
|
||||
Entry* dest = findEntry(entries, capacity, entry->key);
|
||||
dest->key = entry->key;
|
||||
dest->value = entry->value;
|
||||
table->count++;
|
||||
}
|
||||
FREE_ARRAY(Entry, table->entries, table->capacity);
|
||||
table->entries = entries;
|
||||
table->capacity = capacity;
|
||||
}
|
||||
bool tableGet(Table* table, ObjString* key, Value* value) {
|
||||
if (table->count == 0) return false;
|
||||
|
||||
Entry* entry = findEntry(table->entries, table->capacity, key);
|
||||
if (entry->key == NULL) return false;
|
||||
|
||||
*value = entry->value;
|
||||
return true;
|
||||
}
|
||||
bool tableSet(Table* table, ObjString* key, Value value) {
|
||||
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
|
||||
int capacity = GROW_CAPACITY(table->capacity);
|
||||
adjustCapacity(table, capacity);
|
||||
}
|
||||
|
||||
Entry* entry = findEntry(table->entries, table->capacity, key);
|
||||
bool isNewKey = entry->key == NULL;
|
||||
if (isNewKey && IS_NIL(entry->value)) table->count++;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
return isNewKey;
|
||||
}
|
||||
|
||||
bool tableDelete(Table* table, ObjString* key) {
|
||||
if (table->count == 0) return false;
|
||||
|
||||
// Find the entry.
|
||||
Entry* entry = findEntry(table->entries, table->capacity, key);
|
||||
if (entry->key == NULL) return false;
|
||||
|
||||
// Place a tombstone in the entry.
|
||||
entry->key = NULL;
|
||||
entry->value = BOOL_VAL(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void tableAddAll(Table* from, Table* to) {
|
||||
for (int i = 0; i < from->capacity; i++) {
|
||||
Entry* entry = &from->entries[i];
|
||||
if (entry->key != NULL) {
|
||||
tableSet(to, entry->key, entry->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjString* tableFindString(Table* table, const char* chars,
|
||||
int length, uint32_t hash) {
|
||||
if (table->count == 0) return NULL;
|
||||
|
||||
uint32_t index = hash % table->capacity;
|
||||
for (;;) {
|
||||
Entry* entry = &table->entries[index];
|
||||
if (entry->key == NULL) {
|
||||
// Stop if we find an empty non-tombstone entry.
|
||||
if (IS_NIL(entry->value)) return NULL;
|
||||
} else if (entry->key->length == length &&
|
||||
entry->key->hash == hash &&
|
||||
memcmp(entry->key->chars, chars, length) == 0) {
|
||||
// We found it.
|
||||
return entry->key;
|
||||
}
|
||||
|
||||
index = (index + 1) % table->capacity;
|
||||
}
|
||||
}
|
26
clox/table.h
Normal file
26
clox/table.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef clox_table_h
|
||||
#define clox_table_h
|
||||
|
||||
#include "common.h"
|
||||
#include "value.h"
|
||||
|
||||
typedef struct {
|
||||
ObjString* key;
|
||||
Value value;
|
||||
} Entry;
|
||||
|
||||
typedef struct {
|
||||
int count;
|
||||
int capacity;
|
||||
Entry* entries;
|
||||
} Table;
|
||||
|
||||
void initTable(Table* table);
|
||||
void freeTable(Table* table);
|
||||
bool tableGet(Table* table, ObjString* key, Value* value);
|
||||
bool tableSet(Table* table, ObjString* key, Value value);
|
||||
bool tableDelete(Table* table, ObjString* key);
|
||||
void tableAddAll(Table* from, Table* to);
|
||||
ObjString* tableFindString(Table* table, const char* chars,
|
||||
int length, uint32_t hash);
|
||||
#endif
|
@ -42,13 +42,7 @@ bool valuesEqual(Value a, Value b) {
|
||||
case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b);
|
||||
case VAL_NIL: return true;
|
||||
case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
|
||||
case VAL_OBJ: {
|
||||
ObjString* aString = AS_STRING(a);
|
||||
ObjString* bString = AS_STRING(b);
|
||||
return aString->length == bString->length &&
|
||||
memcmp(aString->chars, bString->chars,
|
||||
aString->length) == 0;
|
||||
}
|
||||
case VAL_OBJ: return AS_OBJ(a) == AS_OBJ(b);
|
||||
default: return false; // Unreachable.
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,11 @@ static void runtimeError(const char* format, ...) {
|
||||
void initVM() {
|
||||
resetStack();
|
||||
vm.objects = NULL;
|
||||
initTable(&vm.strings);
|
||||
}
|
||||
|
||||
void freeVM() {
|
||||
freeTable(&vm.strings);
|
||||
freeObjects();
|
||||
}
|
||||
void push(Value value) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define clox_vm_h
|
||||
|
||||
#include "chunk.h"
|
||||
#include "table.h"
|
||||
#include "value.h"
|
||||
|
||||
#define STACK_MAX 256
|
||||
@ -11,6 +12,7 @@ typedef struct {
|
||||
uint8_t* ip;
|
||||
Value stack[STACK_MAX];
|
||||
Value* stackTop;
|
||||
Table strings;
|
||||
Obj* objects;
|
||||
} VM;
|
||||
typedef enum {
|
||||
|
Loading…
Reference in New Issue
Block a user