/** * @file symtab.c * @brief Implements the fixed-capacity open-addressed symbol table. */ #include #include #include #include #include /* * Probe for `key`. On success *slot points at either the slot holding the key or * the first free slot it could occupy; *found says which. Uses * aksl_strhash_djb2 rather than a private hash. That wrapper sign-extends char, * so a high-bit byte hashes wrong (libakstdlib's UPGRADING.md) -- harmless * here because BASIC identifiers are 7-bit ASCII, and it would only ever cost * probe efficiency, never correctness, since the key comparison is a strcmp. */ static akerr_ErrorContext *probe(akbasic_SymbolTable *obj, const char *key, int *slot, bool *found) { PREPARE_ERROR(errctx); uint32_t hashval = 0; int index = 0; int i = 0; PASS(errctx, aksl_strhash_djb2((char *)key, strlen(key), &hashval)); *found = false; index = (int)(hashval % (uint32_t)obj->capacity); for ( i = 0; i < obj->capacity; i++ ) { int probeidx = (index + i) % obj->capacity; if ( !obj->slots[probeidx].used ) { *slot = probeidx; SUCCEED_RETURN(errctx); } if ( strcmp(obj->slots[probeidx].key, key) == 0 ) { *slot = probeidx; *found = true; SUCCEED_RETURN(errctx); } } FAIL_RETURN(errctx, AKBASIC_ERR_BOUNDS, "Symbol table is full (%d entries), cannot place '%s'", obj->capacity, key); } akerr_ErrorContext *akbasic_symtab_init(akbasic_SymbolTable *obj, int capacity) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER, "NULL symbol table in init"); FAIL_ZERO_RETURN(errctx, (capacity > 0 && capacity <= AKBASIC_SYMTAB_MAX_SLOTS), AKBASIC_ERR_BOUNDS, "Symbol table capacity %d out of range 1..%d", capacity, AKBASIC_SYMTAB_MAX_SLOTS); memset(obj, 0, sizeof(*obj)); obj->capacity = capacity; obj->count = 0; SUCCEED_RETURN(errctx); } akerr_ErrorContext *akbasic_symtab_set(akbasic_SymbolTable *obj, const char *key, void *value, int64_t ivalue) { PREPARE_ERROR(errctx); int slot = 0; bool found = false; FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER, "NULL symbol table in set"); FAIL_ZERO_RETURN(errctx, (key != NULL), AKERR_NULLPOINTER, "NULL key in symbol table set"); FAIL_ZERO_RETURN(errctx, (strlen(key) < AKBASIC_SYMTAB_MAX_KEY), AKBASIC_ERR_BOUNDS, "Symbol name '%s' exceeds %d characters", key, AKBASIC_SYMTAB_MAX_KEY - 1); PASS(errctx, probe(obj, key, &slot, &found)); if ( !found ) { strncpy(obj->slots[slot].key, key, AKBASIC_SYMTAB_MAX_KEY - 1); obj->slots[slot].key[AKBASIC_SYMTAB_MAX_KEY - 1] = '\0'; obj->slots[slot].used = true; obj->count += 1; } obj->slots[slot].value = value; obj->slots[slot].ivalue = ivalue; SUCCEED_RETURN(errctx); } akerr_ErrorContext *akbasic_symtab_get(akbasic_SymbolTable *obj, const char *key, void **value, int64_t *ivalue) { PREPARE_ERROR(errctx); int slot = 0; bool found = false; FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER, "NULL symbol table in get"); FAIL_ZERO_RETURN(errctx, (key != NULL), AKERR_NULLPOINTER, "NULL key in symbol table get"); FAIL_ZERO_RETURN(errctx, (strlen(key) < AKBASIC_SYMTAB_MAX_KEY), AKERR_KEY, "Symbol '%s' is not present", key); PASS(errctx, probe(obj, key, &slot, &found)); FAIL_ZERO_RETURN(errctx, found, AKERR_KEY, "Symbol '%s' is not present", key); if ( value != NULL ) { *value = obj->slots[slot].value; } if ( ivalue != NULL ) { *ivalue = obj->slots[slot].ivalue; } SUCCEED_RETURN(errctx); } akerr_ErrorContext *akbasic_symtab_clear(akbasic_SymbolTable *obj) { PREPARE_ERROR(errctx); int capacity = 0; FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER, "NULL symbol table in clear"); capacity = obj->capacity; memset(obj, 0, sizeof(*obj)); obj->capacity = capacity; SUCCEED_RETURN(errctx); }