170 lines
4.6 KiB
C
170 lines
4.6 KiB
C
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "dict.h"
|
|
#include <stdio.h>
|
|
|
|
#define FREE_DICT_NAMES(env) \
|
|
while ((env)->capacity > 0) { \
|
|
char *word_name = (env)->entries[--(env)->capacity].word_name; \
|
|
if (word_name != NULL && word_name != DELETED_ENTRY_MARK) \
|
|
free(word_name); \
|
|
}
|
|
|
|
const size_t DEFAULT_HASHMAP_SIZE = 255;
|
|
const size_t HASHMAP_REHASH_MARK = 70;
|
|
struct dict_ent *dict_last;
|
|
|
|
static size_t dict_table_fnv1a(const char *key, const size_t key_len)
|
|
{
|
|
const size_t fnv_offset = 0xcbf29ce484222325;
|
|
const size_t fnv_prime = 0x100000001b3;
|
|
size_t hash = fnv_offset;
|
|
|
|
for (size_t ki = 0; ki < key_len; ++ki) {
|
|
hash ^= (uint8_t)key[ki];
|
|
hash *= fnv_prime;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
static void dict_destroy_enviroment(struct dict_table *env)
|
|
{
|
|
FREE_DICT_NAMES(env);
|
|
free(env->entries);
|
|
free(env);
|
|
}
|
|
|
|
struct dict_table *dict_new_env(struct dict_table *prev_env)
|
|
{
|
|
struct dict_table *new_env = xcalloc(1, sizeof(*new_env));
|
|
new_env->entries = xcalloc(DEFAULT_HASHMAP_SIZE, sizeof(struct dict_ent));
|
|
new_env->prev = prev_env;
|
|
new_env->capacity = DEFAULT_HASHMAP_SIZE;
|
|
|
|
if (prev_env != NULL)
|
|
new_env->level = prev_env->level + 1;
|
|
return new_env;
|
|
}
|
|
|
|
void dict_destroy_env(struct dict_table *env)
|
|
{
|
|
if (env->prev == NULL) {
|
|
dict_destroy_enviroment(env);
|
|
return;
|
|
}
|
|
|
|
while (env != NULL) {
|
|
struct dict_table *tmp = env;
|
|
env = env->prev;
|
|
dict_destroy_enviroment(tmp);
|
|
}
|
|
}
|
|
|
|
struct dict_ent *_dict_insert(
|
|
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
|
|
struct comp_word *fn_def
|
|
);
|
|
// Resizes the bucket size if needed (shrink or expand), also removes deleted entries
|
|
void dict_table_rehash(struct dict_table *env)
|
|
{
|
|
const size_t new_capacity = env->capacity * 2;
|
|
|
|
struct dict_table new_dict_table = {0};
|
|
new_dict_table.entries = xcalloc(new_capacity, sizeof(struct dict_ent));
|
|
new_dict_table.capacity = new_capacity;
|
|
|
|
// copy elements to the new dict_table, removing deleted entries in the process
|
|
for (size_t i = 0; i < env->capacity; ++i) {
|
|
struct dict_ent *oldent = &env->entries[i];
|
|
//if (oldent->word_name != NULL && oldent->word_name != DELETED_ENTRY_MARK) {
|
|
if (IS_ENTRY(oldent)) {
|
|
dict_insert(&new_dict_table, oldent->word_name, oldent->flags, oldent->nat_fn, oldent->fn_def);
|
|
}
|
|
}
|
|
|
|
FREE_DICT_NAMES(env);
|
|
free(env->entries); // free old buckets
|
|
env->capacity = new_capacity;
|
|
env->entries = new_dict_table.entries;
|
|
}
|
|
|
|
struct dict_ent *_dict_insert(
|
|
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
|
|
struct comp_word *fn_def
|
|
)
|
|
{
|
|
if ((env->used * 100) / env->capacity >= HASHMAP_REHASH_MARK)
|
|
dict_table_rehash(env);
|
|
|
|
size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
|
|
|
|
while (env->entries[hash_index].word_name != NULL) {
|
|
++hash_index;
|
|
if (hash_index >= env->capacity)
|
|
hash_index = 0;
|
|
}
|
|
|
|
if (env->entries[hash_index].word_name != DELETED_ENTRY_MARK)
|
|
++env->used;
|
|
|
|
struct dict_ent *dslot = &env->entries[hash_index];
|
|
dslot->word_name = (char *)word_name;
|
|
dslot->flags = flags;
|
|
dslot->nat_fn = nat_fn;
|
|
dslot->fn_def = fn_def;
|
|
|
|
dict_last = dslot;
|
|
return dslot;
|
|
}
|
|
|
|
struct dict_ent *dict_insert(
|
|
struct dict_table *env, const char *word_name, uint8_t flags, dict_fn nat_fn,
|
|
struct comp_word *fn_def
|
|
)
|
|
{
|
|
return _dict_insert(env, strdup(word_name), flags, nat_fn, fn_def);
|
|
}
|
|
|
|
// like dict_lookup it searches a symbol, but limited to the current environment (scope)
|
|
struct dict_ent *dict_lookup(struct dict_table *env, const char *word_name)
|
|
{
|
|
if (env->entries == NULL)
|
|
return NULL;
|
|
size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
|
|
|
|
while (env->entries[hash_index].word_name != NULL) {
|
|
struct dict_ent *ent = &env->entries[hash_index];
|
|
if (ent->word_name != DELETED_ENTRY_MARK && !strcmp(ent->word_name, word_name))
|
|
return ent;
|
|
|
|
++hash_index;
|
|
if (hash_index >= env->capacity)
|
|
hash_index = 0;
|
|
}
|
|
|
|
// entry not found
|
|
return NULL;
|
|
}
|
|
|
|
// searches for a symbol in the current table and in all linked environments (scopes)
|
|
//struct dict_ent *dict_lookup(struct dict_table *env, char *word_name)
|
|
//{
|
|
// if (env->entries == NULL)
|
|
// return NULL;
|
|
// size_t hash_index = dict_table_fnv1a(word_name, strlen(word_name)) % env->capacity;
|
|
//
|
|
// for (struct dict_table *s = env; s != NULL; s = s->prev) {
|
|
// while (s->entries[hash_index].word_name != NULL) {
|
|
// if (s->entries[hash_index].word_name != DELETED_ENTRY_MARK && !strcmp(s->entries[hash_index].word_name, word_name))
|
|
// return &s->entries[hash_index];
|
|
//
|
|
// ++hash_index;
|
|
// if (hash_index >= s->capacity)
|
|
// hash_index = 0;
|
|
// }
|
|
// }
|
|
//
|
|
// // entry not found
|
|
// return NULL;
|
|
//}
|