constexpr u32 HASH_INIT = 5381;
u32 sdbm_hash (void* data, s64 size) {
  u32 h = HASH_INIT;
  for (s64 i = 0; i < size; i += 1) {
    h = (h << 16) + (h << 6) - h + ((u8*)data)[i];
  }
  
  return (u32)h;
}

u64 knuth_hash (u64 x) {
  constexpr u64 KNUTH_GOLDEN_RATIO_64 = 11400714819323198485ULL;
  
  return (KNUTH_GOLDEN_RATIO_64 * x);
}

u32 knuth_hash_u32 (u64 x) {
  u32 h = HASH_INIT;
  
  return (u32)((knuth_hash(x) ^ h) >> 32);
}

constexpr u64 FNV_64_PRIME = 0x100000001b3ULL;
constexpr u64 FNV_64_OFFSET_BIAS = 0xcbf29ce484222325ULL;

u64 fnv1a_hash (u64 x, u64 h = FNV_64_OFFSET_BIAS) {
  h ^= x;
  
  return h * FNV_64_PRIME;
}

// Good for hashing strings.
u64 fnv1a_hash_any (void* data, s64 size, u64 h = FNV_64_OFFSET_BIAS) {
  for (s64 i = 0; i < size; i += 1) {
    h = fnv1a_hash( ((u8*)data)[i], h);
  }
  
  return h;
}

u32 table_hash_function_fnv1a (void* key, s64 size) {
  return (u32)fnv1a_hash_any(key, size);
}

u32 table_hash_function_knuth (void* key, s64 size) {
  Assert(size == 8);
  
  return knuth_hash_u32(*(u64*)key);
}

u32 string_hash_function_fnv1a (void* key, s64 size) {
  Assert(size == sizeof(string));
  string key_as_string = *((string*)key);
  u64 hash_u64 = fnv1a_hash_any(key_as_string.data, key_as_string.count);
  // It should be xor folded to the desired range rather than shifted:
  return (u32)(hash_u64 ^ (hash_u64 >> 32));
}

bool u32_keys_match (void* key1, void* key2) {
  u32 key1_u32 = *(u32*)key1;
  u32 key2_u32 = *(u32*)key2;
  
  return key1_u32 == key2_u32;
}

bool u64_keys_match (void* key1, void* key2) {
  u64 key1_u64 = *(u64*)key1;
  u64 key2_u64 = *(u64*)key2;
  
  return key1_u64 == key2_u64;
}

bool string_keys_match (void* key1, void* key2) {
  string key1_s = *((string*)key1);
  string key2_s = *((string*)key2);
  
  return strings_match(key1_s, key2_s);
}