// Copyright (c) 2019-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.

#include "compiler_internal.h"



typedef struct SymtabEntry_
{
	struct SymtabEntry_* next;
	const char *value;
	uint16_t key_len;
	TokenType type : 16;
	uint32_t hash;
	uint32_t index;
	const char* symbol;
} SymtabEntry;

typedef struct
{
	SymtabEntry **bucket;
	size_t bucket_max;
	size_t bucket_mask;
} SymTab;


typedef struct _Entry
{
	const char *key;
	uint32_t key_len;
	uint32_t hash;
	void *value;
} Entry;


static SymTab symtab;

const char *attribute_list[NUMBER_OF_ATTRIBUTES];
const char *builtin_list[NUMBER_OF_BUILTINS];
const char *builtin_defines[NUMBER_OF_BUILTIN_DEFINES];
const char *type_property_list[NUMBER_OF_TYPE_PROPERTIES];
const char *kw_argc;
const char *kw_argv;
const char *kw_at_checked;
const char *kw_at_deprecated;
const char *kw_at_ensure;
const char *kw_at_param;
const char *kw_at_pure;
const char *kw_at_require;
const char *kw_at_return;
const char *kw_check_assign;
const char *kw_deprecated;
const char *kw_finalize;
const char *kw_in;
const char *kw_incr;
const char *kw_initialize;
const char *kw_inout;
const char *kw_kind;
const char *kw_len;
const char *kw_libc;
const char *kw_main;
const char *kw_mainstub;
const char *kw_nameof;
const char *kw_noinline;
const char *kw_offsetof;
const char *kw_ordinal;
const char *kw_out;
const char *kw_ptr;
const char *kw_pure;
const char *kw_return;
const char *kw_std;
const char *kw_std__core;
const char *kw_std__core__types;
const char *kw___run_default_test_runner;
const char *kw_type;
const char *kw_typekind;
const char *kw_winmain;
const char *kw_wmain;

void symtab_destroy()
{
	free(symtab.bucket);
}



void symtab_init(uint32_t capacity)
{
	if (capacity < 0x100) error_exit("Too small symtab size.");
	capacity = next_highest_power_of_2(capacity);
	symtab.bucket_max = capacity;
	symtab.bucket_mask = capacity - 1;

	size_t size = capacity * sizeof(SymtabEntry*);
	symtab.bucket = malloc(size);
	// Touch all pages to improve perf(!)
	memset(symtab.bucket, 0, size);

	// Add keywords.
	for (TokenType i = TOKEN_FIRST_KEYWORD; i <= TOKEN_LAST_KEYWORD; i++)
	{
		TokenType type = i;
		const char* name = token_type_to_string(type);
		uint32_t len = (uint32_t)strlen(name);
		const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type);
		switch (type)
		{
			case TOKEN_RETURN:
				kw_return = interned;
				break;
			default:
				break;
		}
		assert(type == i);
		assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned);
	}

	// Init some constant idents
#define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type)
	TokenType type = TOKEN_CONST_IDENT;
	builtin_defines[BUILTIN_DEF_DATE] = KW_DEF("DATE");
	builtin_defines[BUILTIN_DEF_FILE] = KW_DEF("FILE");
	builtin_defines[BUILTIN_DEF_FILEPATH] = KW_DEF("FILEPATH");
	builtin_defines[BUILTIN_DEF_FUNCTION] = KW_DEF("FUNCTION");
	builtin_defines[BUILTIN_DEF_FUNC] = KW_DEF("FUNC");
	builtin_defines[BUILTIN_DEF_LINE] = KW_DEF("LINE");
	builtin_defines[BUILTIN_DEF_LINE_RAW] = KW_DEF("LINE_RAW");
	builtin_defines[BUILTIN_DEF_MODULE] = KW_DEF("MODULE");
	builtin_defines[BUILTIN_DEF_TIME] = KW_DEF("TIME");
	builtin_defines[BUILTIN_DEF_TEST_NAMES] = KW_DEF("TEST_NAMES");
	builtin_defines[BUILTIN_DEF_TEST_FNS] = KW_DEF("TEST_FNS");

	type = TOKEN_TYPE_IDENT;
	kw_typekind = KW_DEF("TypeKind");

	type = TOKEN_IDENT;
	kw_argc = KW_DEF("_$argc");
	kw_argv = KW_DEF("_$argv");
	kw_check_assign = KW_DEF("check_assign");
	kw_deprecated = KW_DEF("deprecated");
	kw_finalize = KW_DEF("finalize");
	kw_in = KW_DEF("in");
	kw_initialize = KW_DEF("initialize");
	kw_incr = KW_DEF("incr");
	kw_inout = KW_DEF("inout");
	kw_libc = KW_DEF("libc");
	kw_mainstub = KW_DEF("_$start");
	kw_main = KW_DEF("main");
	kw_nameof = KW_DEF("nameof");
	kw_noinline = KW_DEF("noinline");
	kw_offsetof = KW_DEF("offsetof");
	kw_ordinal = KW_DEF("ordinal");
	kw_out = KW_DEF("out");
	kw_ptr = KW_DEF("ptr");
	kw_pure = KW_DEF("pure");
	KW_DEF("returns");
	kw_std = KW_DEF("std");
	kw_std__core = KW_DEF("std::core");
	kw_std__core__types = KW_DEF("std::core::types");
	kw___run_default_test_runner = KW_DEF("__run_default_test_runner");
	kw_type = KW_DEF("type");
	kw_winmain = KW_DEF("wWinMain");
	kw_wmain = KW_DEF("wmain");

	type_property_list[TYPE_PROPERTY_MAX] = builtin_list[BUILTIN_MAX] = KW_DEF("max");
	type_property_list[TYPE_PROPERTY_MIN] = builtin_list[BUILTIN_MIN] = KW_DEF("min");

	type_property_list[TYPE_PROPERTY_LEN] = kw_len = KW_DEF("len");

	type_property_list[TYPE_PROPERTY_ALIGNOF] = KW_DEF("alignof");
	type_property_list[TYPE_PROPERTY_ASSOCIATED] = KW_DEF("associated");
	type_property_list[TYPE_PROPERTY_ELEMENTS] = KW_DEF("elements");
	type_property_list[TYPE_PROPERTY_EXTNAMEOF] = KW_DEF("extnameof");
	type_property_list[TYPE_PROPERTY_INF] = KW_DEF("inf");
	type_property_list[TYPE_PROPERTY_INNER] = KW_DEF("inner");
	type_property_list[TYPE_PROPERTY_KINDOF] = KW_DEF("kindof");
	type_property_list[TYPE_PROPERTY_MEMBERSOF] = KW_DEF("membersof");
	type_property_list[TYPE_PROPERTY_NAMEOF] = KW_DEF("nameof");
	type_property_list[TYPE_PROPERTY_NAMES] = KW_DEF("names");
	type_property_list[TYPE_PROPERTY_NAN] = KW_DEF("nan");
	type_property_list[TYPE_PROPERTY_PARAMS] = KW_DEF("params");
	type_property_list[TYPE_PROPERTY_QNAMEOF] = KW_DEF("qnameof");
	type_property_list[TYPE_PROPERTY_RETURNS] = KW_DEF("returns");
	type_property_list[TYPE_PROPERTY_SIZEOF] = KW_DEF("sizeof");
	type_property_list[TYPE_PROPERTY_VALUES] = KW_DEF("values");

	builtin_list[BUILTIN_ABS] = KW_DEF("abs");
	builtin_list[BUILTIN_ATOMIC_LOAD] = KW_DEF("atomic_load");
	builtin_list[BUILTIN_ATOMIC_STORE] = KW_DEF("atomic_store");
	builtin_list[BUILTIN_BITREVERSE] = KW_DEF("bitreverse");
	builtin_list[BUILTIN_BSWAP] = KW_DEF("bswap");
	builtin_list[BUILTIN_CEIL] = KW_DEF("ceil");
	builtin_list[BUILTIN_COMPARE_EXCHANGE] = KW_DEF(("compare_exchange"));
	builtin_list[BUILTIN_COPYSIGN] = KW_DEF("copysign");
	builtin_list[BUILTIN_COS] = KW_DEF("cos");
	builtin_list[BUILTIN_CTLZ] = KW_DEF("clz");
	builtin_list[BUILTIN_CTTZ] = KW_DEF("ctz");
	builtin_list[BUILTIN_EXACT_ADD] = KW_DEF("add");
	builtin_list[BUILTIN_EXACT_DIV] = KW_DEF("div");
	builtin_list[BUILTIN_EXACT_MOD] = KW_DEF("mod");
	builtin_list[BUILTIN_EXACT_MUL] = KW_DEF("mul");
	builtin_list[BUILTIN_EXACT_NEG] = KW_DEF("neg");
	builtin_list[BUILTIN_EXACT_SUB] = KW_DEF("sub");
	builtin_list[BUILTIN_EXP] = KW_DEF("exp");
	builtin_list[BUILTIN_EXP2] = KW_DEF("exp2");
	builtin_list[BUILTIN_EXPECT] = KW_DEF("expect");
	builtin_list[BUILTIN_EXPECT_WITH_PROBABILITY] = KW_DEF("expect_with_probability");
	builtin_list[BUILTIN_FLOOR] = KW_DEF("floor");
	builtin_list[BUILTIN_FMA] = KW_DEF("fma");
	builtin_list[BUILTIN_FMULADD] = KW_DEF("fmuladd");
	builtin_list[BUILTIN_FRAMEADDRESS] = KW_DEF("frameaddress");
	builtin_list[BUILTIN_FSHL] = KW_DEF("fshl");
	builtin_list[BUILTIN_FSHR] = KW_DEF("fshr");
	builtin_list[BUILTIN_GET_ROUNDING_MODE] = KW_DEF("get_rounding_mode");
	builtin_list[BUILTIN_LOG] = KW_DEF("log");
	builtin_list[BUILTIN_LOG2] = KW_DEF("log2");
	builtin_list[BUILTIN_LOG10] = KW_DEF("log10");
	builtin_list[BUILTIN_MEMCOPY] = KW_DEF("memcpy");
	builtin_list[BUILTIN_MEMCOPY_INLINE] = KW_DEF("memcpy_inline");
	builtin_list[BUILTIN_MEMMOVE] = KW_DEF("memmove");
	builtin_list[BUILTIN_MEMSET] = KW_DEF("memset");
	builtin_list[BUILTIN_MEMSET_INLINE] = KW_DEF("memset_inline");
	builtin_list[BUILTIN_NEARBYINT] = KW_DEF("nearbyint");
	builtin_list[BUILTIN_OVERFLOW_ADD] = KW_DEF("overflow_add");
	builtin_list[BUILTIN_OVERFLOW_SUB] = KW_DEF("overflow_sub");
	builtin_list[BUILTIN_OVERFLOW_MUL] = KW_DEF("overflow_mul");
	builtin_list[BUILTIN_POPCOUNT] = KW_DEF("popcount");
	builtin_list[BUILTIN_POW] = KW_DEF("pow");
	builtin_list[BUILTIN_POW_INT] = KW_DEF("pow_int");
	builtin_list[BUILTIN_PREFETCH] = KW_DEF("prefetch");
	builtin_list[BUILTIN_REDUCE_ADD] = KW_DEF("reduce_add");
	builtin_list[BUILTIN_REDUCE_AND] = KW_DEF("reduce_and");
	builtin_list[BUILTIN_REDUCE_FADD] = KW_DEF("reduce_fadd");
	builtin_list[BUILTIN_REDUCE_FMUL] = KW_DEF("reduce_fmul");
	builtin_list[BUILTIN_REDUCE_MAX] = KW_DEF("reduce_max");
	builtin_list[BUILTIN_REDUCE_MIN] = KW_DEF("reduce_min");
	builtin_list[BUILTIN_REDUCE_MUL] = KW_DEF("reduce_mul");
	builtin_list[BUILTIN_REDUCE_OR] = KW_DEF("reduce_or");
	builtin_list[BUILTIN_REDUCE_XOR] = KW_DEF("reduce_xor");
	builtin_list[BUILTIN_REVERSE] = KW_DEF("reverse");
	builtin_list[BUILTIN_RINT] = KW_DEF("rint");
	builtin_list[BUILTIN_ROUND] = KW_DEF("round");
	builtin_list[BUILTIN_ROUNDEVEN] = KW_DEF("roundeven");
	builtin_list[BUILTIN_SAT_ADD] = KW_DEF("sat_add");
	builtin_list[BUILTIN_SAT_SHL] = KW_DEF("sat_shl");
	builtin_list[BUILTIN_SAT_SUB] = KW_DEF("sat_sub");
	builtin_list[BUILTIN_SET_ROUNDING_MODE] = KW_DEF("set_rounding_mode");
	builtin_list[BUILTIN_SIN] = KW_DEF("sin");
	builtin_list[BUILTIN_SWIZZLE] = KW_DEF("swizzle");
	builtin_list[BUILTIN_SWIZZLE2] = KW_DEF("swizzle2");
	builtin_list[BUILTIN_SQRT] = KW_DEF("sqrt");
	builtin_list[BUILTIN_STACKTRACE] = KW_DEF("stacktrace");
	builtin_list[BUILTIN_SYSCALL] = KW_DEF("syscall");
	builtin_list[BUILTIN_SYSCLOCK] = KW_DEF("sysclock");
	builtin_list[BUILTIN_TRAP] = KW_DEF("trap");
	builtin_list[BUILTIN_TRUNC] = KW_DEF("trunc");
	builtin_list[BUILTIN_VECCOMPLT] = KW_DEF("veccomplt");
	builtin_list[BUILTIN_VECCOMPLE] = KW_DEF("veccomple");
	builtin_list[BUILTIN_VECCOMPGT] = KW_DEF("veccompgt");
	builtin_list[BUILTIN_VECCOMPGE] = KW_DEF("veccompge");
	builtin_list[BUILTIN_VECCOMPEQ] = KW_DEF("veccompeq");
	builtin_list[BUILTIN_VECCOMPNE] = KW_DEF("veccompne");
	builtin_list[BUILTIN_UNREACHABLE] = KW_DEF("unreachable");
	builtin_list[BUILTIN_VOLATILE_LOAD] = KW_DEF("volatile_load");
	builtin_list[BUILTIN_VOLATILE_STORE] = KW_DEF("volatile_store");
	builtin_list[BUILTIN_WASM_MEMORY_GROW] = KW_DEF("wasm_memory_grow");
	builtin_list[BUILTIN_WASM_MEMORY_SIZE] = KW_DEF("wasm_memory_size");

	for (unsigned i = 0; i < NUMBER_OF_BUILTINS; i++)
	{
		assert(builtin_list[i] && "Missing builtin");
	}

	for (unsigned i = 0; i < NUMBER_OF_TYPE_PROPERTIES; i++)
	{
		assert(type_property_list[i] && "Missing type property");
	}

	for (unsigned i = 0; i < NUMBER_OF_BUILTIN_DEFINES; i++)
	{
		assert(builtin_defines[i] && "Missing builtin define");
	}

	type = TOKEN_AT_IDENT;

	kw_at_checked = KW_DEF("@checked");
	kw_at_ensure = KW_DEF("@ensure");
	kw_at_deprecated = KW_DEF("@deprecated");
	kw_at_param = KW_DEF("@param");
	kw_at_pure = KW_DEF("@pure");
	kw_at_require = KW_DEF("@require");
	kw_at_return = KW_DEF("@return");

	attribute_list[ATTRIBUTE_ALIGN] = KW_DEF("@align");
	attribute_list[ATTRIBUTE_BIGENDIAN] = KW_DEF("@bigendian");
	attribute_list[ATTRIBUTE_BUILTIN] = KW_DEF("@builtin");
	attribute_list[ATTRIBUTE_CDECL] = KW_DEF("@cdecl");
	attribute_list[ATTRIBUTE_DEPRECATED] = KW_DEF("@deprecated");
	attribute_list[ATTRIBUTE_DYNAMIC] = KW_DEF("@dynamic");
	attribute_list[ATTRIBUTE_EXPORT] = KW_DEF("@export");
	attribute_list[ATTRIBUTE_EXTERN] = KW_DEF("@extern");
	attribute_list[ATTRIBUTE_EXTNAME] = KW_DEF("@extname");
	attribute_list[ATTRIBUTE_INLINE] = KW_DEF("@inline");
	attribute_list[ATTRIBUTE_INTERFACE] = KW_DEF("@interface");
	attribute_list[ATTRIBUTE_LITTLEENDIAN] = KW_DEF("@littleendian");
	attribute_list[ATTRIBUTE_LOCAL] = KW_DEF("@local");
	attribute_list[ATTRIBUTE_MAYDISCARD] = KW_DEF("@maydiscard");
	attribute_list[ATTRIBUTE_NAKED] = KW_DEF("@naked");
	attribute_list[ATTRIBUTE_NODISCARD] = KW_DEF("@nodiscard");
	attribute_list[ATTRIBUTE_NOINIT] = KW_DEF("@noinit");
	attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("@noinline");
	attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("@noreturn");
	attribute_list[ATTRIBUTE_NOSTRIP] = KW_DEF("@nostrip");
	attribute_list[ATTRIBUTE_OBFUSCATE] = KW_DEF("@obfuscate");
	attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("@operator");
	attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("@overlap");
	attribute_list[ATTRIBUTE_PACKED] = KW_DEF("@packed");
	attribute_list[ATTRIBUTE_PRIORITY] = KW_DEF("@priority");
	attribute_list[ATTRIBUTE_PRIVATE] = KW_DEF("@private");
	attribute_list[ATTRIBUTE_PURE] = kw_at_pure;
	attribute_list[ATTRIBUTE_PUBLIC] = KW_DEF("@public");
	attribute_list[ATTRIBUTE_REFLECT] = KW_DEF("@reflect");
	attribute_list[ATTRIBUTE_SECTION] = KW_DEF("@section");
	attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("@stdcall");
	attribute_list[ATTRIBUTE_TEST] = KW_DEF("@test");
	attribute_list[ATTRIBUTE_UNUSED] = KW_DEF("@unused");
	attribute_list[ATTRIBUTE_USED] = KW_DEF("@used");
	attribute_list[ATTRIBUTE_VECCALL] = KW_DEF("@veccall");
	attribute_list[ATTRIBUTE_WASM] = KW_DEF("@wasm");
	attribute_list[ATTRIBUTE_WEAK] = KW_DEF("@weak");
	attribute_list[ATTRIBUTE_WINMAIN] = KW_DEF("@winmain");

	for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++)
	{
		assert(attribute_list[i] && "Missing attributes");
	}

}


const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type)
{
	size_t pos = fnv1hash & symtab.bucket_mask;
	SymtabEntry *bucket = symtab.bucket[pos];
	while (bucket)
	{
		if (bucket->index == fnv1hash && len == bucket->key_len && memcmp(symbol, bucket->symbol, len) == 0)
		{
			*type = bucket->type;
			return bucket->symbol;
		}
		bucket = bucket->next;
	}
	return NULL;
}

const char *symtab_preset(const char *data, TokenType type)
{
	uint32_t len = (uint32_t)strlen(data);
	TokenType result = type;
	const char *res = symtab_add(data, len, fnv1a(data, len), &result);
	assert(result == type);
	return res;
}

const char *symtab_add(const char *data, uint32_t len, uint32_t fnv1hash, TokenType *type)
{
	size_t pos = fnv1hash & symtab.bucket_mask;
	SymtabEntry *first_bucket = symtab.bucket[pos];
	if (!first_bucket)
	{
		SymtabEntry *node = malloc_arena(sizeof(SymtabEntry));
		symtab.bucket[pos] = node;
		node->key_len = len;
		node->next = NULL;
		node->index = fnv1hash;
		node->type = *type;
		return node->symbol = str_copy(data, len);
	}
	SymtabEntry *bucket = first_bucket;
	do
	{
		if (bucket->index == fnv1hash && len == bucket->key_len && memcmp(data, bucket->symbol, len) == 0)
		{
			*type = bucket->type;
			return bucket->symbol;
		}
		bucket = bucket->next;
	} while (bucket);
	SymtabEntry *node = malloc_arena(sizeof(SymtabEntry));
	node->next = first_bucket;
	symtab.bucket[pos] = node;
	node->key_len = len;
	node->index = fnv1hash;
	node->type = *type;
	return node->symbol = str_copy(data, len);
}


void stable_init(STable *table, uint32_t initial_size)
{
	assert(initial_size && "Size must be larger than 0");
	assert (is_power_of_two(initial_size) && "Must be a power of two");

	SEntry *entries = CALLOC(initial_size * sizeof(Entry));
	table->count = 0;
	table->capacity = initial_size;
	table->max_load = initial_size * TABLE_MAX_LOAD;
	table->entries = entries;
}

void stable_clear(STable *table)
{
	memset(table->entries, 0, table->capacity * sizeof(Entry));
	table->count = 0;
}

static inline SEntry *sentry_find(SEntry *entries, uint32_t capacity, const char *key)
{
	uintptr_t hash_key = (uintptr_t)key;
	uint32_t mask = capacity - 1;
	hash_key ^= hash_key >> 16;
	uint32_t index = (uint32_t)hash_key & mask;
	while (1)
	{
		SEntry *entry = &entries[index];
		if (entry->key == key || !entry->key) return entry;
		index = (index + 1) & mask;
	}
}

static inline void stable_resize(STable *table)
{
	ASSERT(table->capacity < MAX_HASH_SIZE, "Table size too large, exceeded max hash size");

	uint32_t new_capacity = table->capacity ? (table->capacity << 2u) : 16u;
	SEntry *new_data = CALLOC(new_capacity * sizeof(SEntry));
	table->count = 0;
	uint32_t len = table->capacity;
	for (uint32_t i = 0; i < len; i++)
	{
		SEntry *entry = &table->entries[i];
		const char *key = entry->key;
		if (!key) continue;
		table->count++;
		SEntry *dest = sentry_find(new_data, new_capacity, key);
		dest->key = key;
		dest->value = entry->value;
	}
	table->entries = new_data;
	table->max_load = new_capacity * TABLE_MAX_LOAD;
	table->capacity = new_capacity;
}

void *stable_set(STable *table, const char *key, void *value)
{
	assert(value && "Cannot insert NULL");
	SEntry *entry = sentry_find(table->entries, table->capacity, key);
	void *old = entry->value;
	if (old == value) return old;
	entry->key = key;
	entry->value = value;
	if (!old)
	{
		table->count++;
		if (table->count >= table->max_load) goto RESIZE;
	}
	return old;
RESIZE:
	stable_resize(table);
	return old;
}


void *stable_get(STable *table, const char *key)
{
	if (!table->entries) return NULL;
	SEntry *entry = sentry_find(table->entries, table->capacity, key);
	return entry->key == NULL ? NULL : entry->value;
}



void htable_init(HTable *table, uint32_t initial_size)
{
	assert(initial_size && "Size must be larger than 0");
	size_t size = next_highest_power_of_2(initial_size);

	size_t mem_size = initial_size * sizeof(HTEntry);
	table->entries = calloc_arena(mem_size);

	// Tap all pages
	char *data = (char *)table->entries;
	for (int i = 0; i < mem_size; i += 4096)
	{
		data[0] = 0;
	}
	table->mask = size - 1;
}

void *htable_set(HTable *table, void *key, void *value)
{
	assert(value && "Cannot insert NULL");
	uint32_t idx = (((uintptr_t)key) ^ ((uintptr_t)key) >> 8) & table->mask;
	HTEntry **entry_ref = &table->entries[idx];
	HTEntry *entry = *entry_ref;
	if (!entry)
	{
		entry = CALLOCS(HTEntry);
		entry->key = key;
		entry->value = value;
		*entry_ref = entry;
		return NULL;
	}
	HTEntry *first_entry = entry;
	do
	{
		if (entry->key == key) return entry->value;
		entry = entry->next;
	} while (entry);

	entry = CALLOCS(HTEntry);
	entry->key = key;
	entry->value = value;
	entry->next = first_entry;
	*entry_ref = entry;
	return NULL;
}


void *htable_get(HTable *table, void *key)
{
	uint32_t idx = (((uintptr_t)key) ^ ((uintptr_t)key) >> 8) & table->mask;
	HTEntry *entry = table->entries[idx];
	if (!entry) return NULL;
	do
	{
		if (entry->key == key) return entry->value;
		entry = entry->next;
	} while (entry);
	return NULL;
}

