#ifndef _KERNEL_LIBK_HASH_H
#define _KERNEL_LIBK_HASH_H

#include <libk/std.h>

struct hash_node_link {
  struct hash_node_link* next;
  uint32_t hash;
};

#define hash_entry(ptr, type, member) ((type*)((char*)(ptr) - offsetof (type, member)))

static inline uint32_t hash_fnv32 (const void* data, size_t len) {
  uint32_t hash = 2166136261U;
  const uint8_t* bp = (const uint8_t*)data;
  const uint8_t* be = bp + len;

  while (bp < be) {
    hash ^= (uint32_t)*bp++;
    hash *= 16777619U;
  }
  return hash;
}

#define hash_insert(table, new_link, hash_val, table_size, buckets_name)                           \
  do {                                                                                             \
    uint32_t __idx = (hash_val) % (table_size);                                                    \
    (new_link)->hash = (hash_val);                                                                 \
    (new_link)->next = (table)->buckets_name[__idx];                                               \
    (table)->buckets_name[__idx] = (new_link);                                                     \
  } while (0)

#define hash_find(table, key_ptr, key_size, hash_val, table_size, buckets_name, type, member,      \
                  key_field, out_link)                                                             \
  do {                                                                                             \
    uint32_t __idx = (hash_val) % (table_size);                                                    \
    struct hash_node_link* __curr = (table)->buckets_name[__idx];                                  \
    (out_link) = NULL;                                                                             \
    while (__curr) {                                                                               \
      if (__curr->hash == (hash_val)) {                                                            \
        type* __entry = hash_entry (__curr, type, member);                                         \
        if (memcmp (__entry->key_field, (key_ptr), (key_size)) == 0) {                             \
          (out_link) = __curr;                                                                     \
          break;                                                                                   \
        }                                                                                          \
      }                                                                                            \
      __curr = __curr->next;                                                                       \
    }                                                                                              \
  } while (0)

#endif // _KERNEL_LIBK_HASH_H
