#include #include #include #include "dict.h" #include #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; //}