88 lines
3.0 KiB
C
88 lines
3.0 KiB
C
/*
|
|
* hash.c - FNV-1a hash function and symbol table (sorted array with bsearch)
|
|
*
|
|
* The symbol table maps KuCoin trading pair names (e.g. "BTC-USDT") to
|
|
* dense 16-bit indices. Entries are sorted alphabetically for O(log n)
|
|
* lookup via bsearch(3). Used by ws_client to resolve symbol names in
|
|
* book update messages.
|
|
*/
|
|
|
|
#include "hash.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
static const uint32_t FNV_OFFSET = 2166136261u;
|
|
static const uint32_t FNV_PRIME = 16777619u;
|
|
|
|
/* FNV-1a non-cryptographic hash for arbitrary-length strings. */
|
|
uint32_t fnv1a_hash(const char *str, uint32_t len) {
|
|
uint32_t hash = FNV_OFFSET;
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
hash ^= (uint8_t)str[i];
|
|
hash *= FNV_PRIME;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/* Initialise an empty symbol table with initial capacity. */
|
|
void symbol_table_init(symbol_table_t *table) {
|
|
table->capacity = SYMBOL_TABLE_INITIAL;
|
|
table->count = 0;
|
|
table->entries = calloc(table->capacity, sizeof(symbol_entry_t));
|
|
}
|
|
|
|
/* Compare two symbol entries by name (for bsearch). */
|
|
static int entry_cmp(const void *a, const void *b) {
|
|
const symbol_entry_t *ea = (const symbol_entry_t *)a;
|
|
const symbol_entry_t *eb = (const symbol_entry_t *)b;
|
|
return strcmp(ea->name, eb->name);
|
|
}
|
|
|
|
/* Wrapper matching qsort's comparison signature (same as entry_cmp). */
|
|
static int entry_cmp_qsort(const void *a, const void *b) {
|
|
return entry_cmp(a, b);
|
|
}
|
|
|
|
/* Sort entries by name and re-assign dense indices. Must be called after
|
|
all additions are complete, before any lookups via bsearch. */
|
|
void symbol_table_sort(symbol_table_t *table) {
|
|
qsort(table->entries, table->count, sizeof(symbol_entry_t), entry_cmp_qsort);
|
|
for (uint32_t i = 0; i < table->count; i++) {
|
|
table->entries[i].index = (uint16_t)i;
|
|
}
|
|
}
|
|
|
|
/* Append a new symbol entry, doubling capacity if full. Returns the
|
|
assigned index, or -1 on allocation failure. */
|
|
int symbol_table_add(symbol_table_t *table, const char *name) {
|
|
if (table->count >= table->capacity) {
|
|
uint32_t new_cap = table->capacity * 2;
|
|
symbol_entry_t *new_entries = realloc(table->entries,
|
|
new_cap * sizeof(symbol_entry_t));
|
|
if (!new_entries) return -1;
|
|
table->entries = new_entries;
|
|
table->capacity = new_cap;
|
|
}
|
|
|
|
symbol_entry_t *entry = &table->entries[table->count];
|
|
strncpy(entry->name, name, SYMBOL_NAME_LEN - 1);
|
|
entry->name[SYMBOL_NAME_LEN - 1] = '\0';
|
|
entry->index = (uint16_t)table->count;
|
|
table->count++;
|
|
|
|
return (int)(table->count - 1);
|
|
}
|
|
|
|
/* Binary-search for a symbol by name. Returns its 16-bit index or -1. */
|
|
int16_t symbol_table_lookup(const symbol_table_t *table, const char *name) {
|
|
symbol_entry_t key;
|
|
strncpy(key.name, name, SYMBOL_NAME_LEN - 1);
|
|
key.name[SYMBOL_NAME_LEN - 1] = '\0';
|
|
|
|
symbol_entry_t *found = bsearch(&key, table->entries, table->count,
|
|
sizeof(symbol_entry_t), entry_cmp);
|
|
if (!found) return -1;
|
|
return (int16_t)found->index;
|
|
}
|