runningwater 2 роки тому
батько
коміт
cf5f7436f9
7 змінених файлів з 227 додано та 8 видалено
  1. 33 3
      object.c
  2. 1 0
      object.h
  3. 146 0
      table.c
  4. 42 0
      table.h
  5. 1 5
      value.c
  6. 2 0
      vm.c
  7. 2 0
      vm.h

+ 33 - 3
object.c

@@ -11,26 +11,36 @@
 #include <string.h>
 
 #include "memory.h"
+#include "table.h"
 #include "object.h"
 #include "vm.h"
 
 static Obj *allocateObject(size_t size, ObjType type);
+static uint32_t hashString(const char *key, int length);
 
 #define ALLOCATE_OBJ(type, objectType) \
   (type *) allocateObject(sizeof(type), objectType)
 
-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;
 }
 
 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)) {
@@ -40,7 +50,15 @@ void printObject(Value value) {
   }
 }
 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);
 }
 
 static Obj *allocateObject(size_t size, ObjType type) {
@@ -51,4 +69,16 @@ static Obj *allocateObject(size_t size, ObjType type) {
   vm.objects = object;
 
   return object;
+}
+/// Hash 函数 -- 使用 FNV-1a 算法
+/// \param key
+/// \param length
+/// \return
+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;
 }

+ 1 - 0
object.h

@@ -33,6 +33,7 @@ struct ObjString {
   struct Obj obj;
   int length;
   char *chars;
+  uint32_t hash;// 缓存 hash 值
 };
 
 ObjString *takeString(char *chars, int length);

+ 146 - 0
table.c

@@ -0,0 +1,146 @@
+/**
+  ******************************************************************************
+  * @file           : table.c
+  * @author         : simon
+  * @brief          : None
+  * @attention      : None
+  * @date           : 2023/8/24
+  ******************************************************************************
+  */
+
+#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);
+}
+/// 查找位置,hash 冲突使用 线性探测(Linear probing) 策略
+/// \param entries
+/// \param capacity
+/// \param key
+/// \return 应该设置的位置
+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;
+  }
+}
+/// Allocating and resizing
+/// \param table
+/// \param capacity
+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;
+  }
+
+  // 原来的 entry 重新放置到新位置
+  table->count = 0;
+  for (int i = 0; i < table->capacity; i++) {
+    Entry *src = &table->entries[i];
+    if (src->key == NULL) continue;
+
+    Entry *dest = findEntry(entries, capacity, src->key);
+    dest->key = src->key;
+    dest->value = src->value;
+    table->count++;
+  }
+
+  FREE_ARRAY(Entry, table->entries, table->capacity);
+  table->entries = entries;
+  table->capacity = capacity;
+}
+bool tableGet(Table *table, ObjString *key, Value *value) {
+  // the table is completely empty
+  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) {
+  // 容量少于75% 就开始扩容
+  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;
+  }
+  return NULL;
+}

+ 42 - 0
table.h

@@ -0,0 +1,42 @@
+/**
+  ******************************************************************************
+  * @file           : table.h
+  * @author         : simon
+  * @brief          : Building a Hash Table -- buckets, load factors, open addressing
+  *                   collision resolution, and hash functions
+  * @attention      : None
+  * @date           : 2023/8/24
+  ******************************************************************************
+  */
+
+#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);
+/// 查找字符串
+/// \param table Table*
+/// \param chars const char*
+/// \param length int
+/// \param hash uint32_t
+/// \return  ObjString*
+ObjString *tableFindString(Table *table, const char *chars, int length, uint32_t hash);
+
+#endif//CLOX_TABLE_H

+ 1 - 5
value.c

@@ -45,11 +45,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;
   }
 }

+ 2 - 0
vm.c

@@ -37,6 +37,7 @@ static void runtimeError(const char *format, ...) {
 void initVM() {
   resetStack();
   vm.objects = NULL;
+  initTable(&vm.strings);
 }
 
 static Value peek(int distance) {
@@ -165,6 +166,7 @@ static InterpretResult run() {
 }
 
 void freeVM() {
+  freeTable(&vm.strings);
   freeObjects();
 }
 

+ 2 - 0
vm.h

@@ -12,6 +12,7 @@
 #define CLOX__VM_H_
 
 #include "chunk.h"
+#include "table.h"
 #include "value.h"
 
 #define STACK_MAX 256
@@ -21,6 +22,7 @@ typedef struct {
   uint8_t *ip;
   Value stack[STACK_MAX];
   Value *stackTop;// 栈指针
+  Table strings;  //
   Obj *objects;   // 管理分配的 heap 内存
 } VM;
 typedef enum {