Change to a different hash table implementation

This commit is contained in:
2025-09-23 14:24:13 -07:00
parent 733c4103a3
commit f961673670
2 changed files with 219 additions and 185 deletions

View File

@ -1,6 +1,7 @@
#ifndef INCLUDED_LISP_H
#define INCLUDED_LISP_H
#include <assert.h>
#include <refcount/refcount.h>
#include <setjmp.h>
#include <stdarg.h>
@ -125,23 +126,20 @@ typedef struct {
LispVal *lexenv;
} LispFunction;
struct HashtableBucket {
struct HashtableBucket *next;
#define LISP_HASHTABLE_INITIAL_SIZE 32
#define LISP_HASHTABLE_GROWTH_FACTOR 2
struct HashtableEntry {
uint64_t hash;
LispVal *key;
LispVal *value;
};
#define LISP_HASHTABLE_INITIAL_SIZE 32
#define LISP_HASHTABLE_GROWTH_FACTOR 2
#define LISP_HASHTABLE_GROWTH_THRESHOLD 0.75f
#define LISP_HASHTABLE_SHRINK_THRESHOLD 0.25f
typedef struct {
LISP_OBJECT_HEADER;
struct HashtableBucket **data;
size_t table_size;
size_t table_size; // number of buckets
struct HashtableEntry *key_vals; // array of key, hash, value
size_t count;
LispVal *eq_fn;
LispVal *hash_fn;
@ -306,11 +304,13 @@ inline static bool NUMBERP(LispVal *v) {
refcount_init_static(Q##sym); \
refcount_init_static(((LispSymbol *) Q##sym)->name); \
}
#define REGISTER_SYMBOL_INTO(sym, pkg) \
REGISTER_SYMBOL_NOINTERN(sym) \
#define REGISTER_DO_INTERN(sym, pkg) \
((LispSymbol *) Q##sym)->package = refcount_ref(pkg); \
puthash(((LispPackage *) pkg)->obarray, \
LISPVAL(((LispSymbol *) Q##sym)->name), Q##sym);
#define REGISTER_SYMBOL_INTO(sym, pkg) \
REGISTER_SYMBOL_NOINTERN(sym) \
REGISTER_DO_INTERN(sym, pkg)
#define REGISTER_SYMBOL(sym) REGISTER_SYMBOL_INTO(sym, system_package)
#define REGISTER_STATIC_FUNCTION(name, args, docstr) \
REGISTER_SYMBOL_NOINTERN(name); \
@ -333,22 +333,11 @@ inline static bool NUMBERP(LispVal *v) {
// ###############
// # Loop macros #
// ###############
#define HASHTABLE_FOREACH(key_var, val_var, table) \
for (struct { \
LispHashtable *ht; \
size_t i; \
} __l = {.ht = (void *) table, .i = 0}; \
__l.i < __l.ht->table_size; ++__l.i) \
for (LispVal *__b = (void *) __l.ht->data[__l.i], \
*key_var = __b ? ((struct HashtableBucket *) __b)->key \
: NULL, \
*val_var = __b ? ((struct HashtableBucket *) __b)->value \
: NULL; \
__b; __b = (void *) ((struct HashtableBucket *) __b)->next, \
key_var = __b ? ((struct HashtableBucket *) __b)->key \
: NULL, \
val_var = __b ? ((struct HashtableBucket *) __b)->value \
: NULL)
#define HT_FOREACH_VALID_INDEX(table, i_var) \
for (size_t i_var = 0; i_var < ((LispHashtable *) (table))->table_size; \
++i_var) \
if (!HASH_SLOT_UNSET_P((table), i_var))
#define FOREACH(var, list) \
for (LispVal *__foreach_cur = list, *var = HEAD(list); \
!NILP(__foreach_cur); \
@ -362,6 +351,7 @@ inline static bool NUMBERP(LispVal *v) {
#define GC_EVERY_N_BYTES 1024 * 80
void *lisp_malloc(size_t size);
void *lisp_realloc(void *old_ptr, size_t size);
void *lisp_malloc0(size_t size);
#define lisp_free free
void garbage_collect(void);
@ -528,7 +518,7 @@ LispVal *intern(const char *name, size_t length, bool take, LispVal *package,
bool included_too);
// #######################
// # Hashtable Functions #
// # Hash Table Functions #
// #######################
DECLARE_FUNCTION(hashtablep, (LispVal * val));
DECLARE_FUNCTION(make_hashtable, (LispVal * hash_fn, LispVal *eq_fn));
@ -537,6 +527,11 @@ DECLARE_FUNCTION(hash_table_count, (LispVal * table));
DECLARE_FUNCTION(puthash, (LispVal * table, LispVal *key, LispVal *value));
DECLARE_FUNCTION(gethash, (LispVal * table, LispVal *key, LispVal *def));
DECLARE_FUNCTION(remhash, (LispVal * table, LispVal *key));
struct HashtableDataArray {
size_t size;
struct HashtableEntry *entries;
};
void free_hash_table_data_array(void *data);
// Don't ref their return value
LispVal *puthash(LispVal *table, LispVal *key, LispVal *value);
@ -760,7 +755,8 @@ static inline void push_to_lexenv(LispVal **lexenv, LispVal *key,
refcount_unref(old);
}
// These are like the internal functions, but they don't ref their return value
// These are like the internal functions, but they don't ref their
// return value
static inline LispVal *HEAD(LispVal *list) {
if (NILP(list)) {
return Qnil;
@ -788,6 +784,32 @@ static inline LispVal *TAIL_SAFE(LispVal *list) {
return ((LispPair *) list)->tail;
}
static inline double HASH_TABLE_LOAD_FACTOR(void *obj) {
assert(HASHTABLEP(obj));
LispHashtable *table = obj;
return (double) table->count / table->table_size;
}
static inline bool HASH_SLOT_UNSET_P(void *obj, size_t i) {
assert(HASHTABLEP(obj));
LispHashtable *table = obj;
return !table->key_vals[i].key;
}
static inline LispVal *HASH_KEY(void *obj, size_t i) {
assert(HASHTABLEP(obj));
LispHashtable *table = obj;
return table->key_vals[i].key;
}
static inline LispVal *HASH_VALUE(void *obj, size_t i) {
assert(HASHTABLEP(obj));
LispHashtable *table = obj;
return table->key_vals[i].value;
}
static inline uint64_t HASH_HASH(void *obj, size_t i) {
assert(HASHTABLEP(obj));
LispHashtable *table = obj;
return table->key_vals[i].hash;
}
// ###################
// # Debug Functions #
// ###################