triangular_arbitrage_bot/src/hash.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;
}