Last active
December 3, 2023 05:45
-
-
Save 1devm0/24e25c60a4784bf6ec7890f3afe8f86f to your computer and use it in GitHub Desktop.
A Hash Table in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
typedef uint8_t u08; | |
typedef uint32_t u32; | |
typedef uint64_t u64; | |
typedef int32_t i32; | |
typedef float f32; | |
typedef double f64; | |
// PRIME NUMBERS | |
i32 is_prime(const i32 x) { | |
if (x < 2) { return -1; } | |
if (x < 4) { return 1; } | |
if ((x % 2) == 0) { return 0; } | |
for (i32 i = 3; i <= floor(sqrt((f64) x)); i+= 2) { | |
if ((x % i) == 0) { | |
return 0; | |
} | |
} | |
return 1; | |
} | |
i32 next_prime(i32 x) { | |
while (is_prime(x) != 1) { | |
x++; | |
} | |
return x; | |
} | |
// FNV-1a hashing function | |
u64 hash_id(const char * id) { | |
u64 hash = 0xcbf29ce484222325; // FNV_offset_basis: 14695981039346656037 | |
while (*id) hash = (hash ^ (u08)*id++) * 0x100000001b3; // FNV_prime: 1099511628211 | |
return hash; | |
} | |
// key: string, value: i32 | |
typedef struct _ht_i32_item_t { | |
char * id; | |
i32 data; | |
struct _ht_i32_item_t * next; // linked list | |
} ht_i32_item_t; | |
ht_i32_item_t * ht_mk_i32_item(char * id, i32 data) { | |
ht_i32_item_t * i = calloc(1, sizeof(ht_i32_item_t)); | |
i -> id = strdup(id); | |
i -> data = data; | |
i -> next = NULL; | |
return i; | |
} | |
typedef struct { | |
ht_i32_item_t ** e; // elements | |
u64 used; | |
u64 sz; | |
} ht_i32_t; | |
// Declarations | |
void ht_i32_mk(ht_i32_t * table, u64 sz); | |
void ht_i32_resize(ht_i32_t * table); | |
void ht_i32_add(ht_i32_t * table, char * id, i32 data); | |
i32 ht_i32_get(ht_i32_t * table, char * id); | |
void ht_i32_rm(ht_i32_t * table); | |
// Implementation | |
void ht_i32_mk(ht_i32_t * table, u64 sz) { | |
// prime numbers | |
table -> sz = next_prime(sz); | |
table -> e = calloc(table -> sz, sizeof(ht_i32_item_t)); | |
table -> used = 0; | |
} | |
u32 collisions = 0; | |
void ht_i32_add(ht_i32_t * table, char * id, i32 data) { | |
ht_i32_item_t * i = ht_mk_i32_item(id, data); | |
ht_i32_resize(table); | |
u64 idx = hash_id(id) % table -> sz; | |
// Scenario 1: NULL -> current = i | |
if (!table -> e[idx]) { | |
table -> e[idx] = i; | |
table -> used++; | |
return; | |
} | |
// Scenario 2 + 3: Key exists or True Collision | |
ht_i32_item_t * current = table -> e[idx]; | |
while (current -> next && strcmp(id, current -> id)) { | |
current = current -> next; | |
} | |
if (!strcmp(id, current -> id)) { | |
free(table -> e[idx]); | |
table -> e[idx] = i; | |
return; | |
} | |
collisions++; | |
current -> next = i; | |
table -> used++; | |
return; | |
} | |
#define LOAD_FACTOR 0.75 | |
void ht_i32_resize(ht_i32_t * table) { | |
if ((f32) table -> used / table -> sz > LOAD_FACTOR) { | |
ht_i32_t * n = calloc(1, sizeof(ht_i32_t)); | |
ht_i32_mk(n, table -> sz * 3.5); | |
for (u64 u = 0; u < table -> sz; u++) { | |
ht_i32_item_t * current = table -> e[u]; | |
while (current){ | |
ht_i32_add(n, current -> id, current -> data); | |
current = current -> next; | |
} | |
} | |
ht_i32_rm(table); | |
table -> e = n -> e; | |
table -> sz = n -> sz; | |
table -> used = n -> used; | |
} | |
} | |
i32 ht_i32_get(ht_i32_t * table, char * id) { | |
u64 idx = hash_id(id) % table -> sz; | |
// 1: return current_item; | |
// 2: return traverse(list, id); | |
ht_i32_item_t * current = table -> e[idx]; | |
while (current) { | |
if (!strcmp(id, current -> id)) { | |
return current -> data; | |
} | |
current = current -> next; | |
} | |
return INT32_MAX; | |
} | |
void ht_i32_rm(ht_i32_t * table) { | |
free(table -> e); | |
table -> e = NULL; | |
table -> sz = 0; | |
table -> used = 0; | |
} | |
// Copied from some StackOverflow thread | |
static char *rand_string(char *str, size_t size) | |
{ | |
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
if (size) { | |
--size; | |
for (size_t n = 0; n < size; n++) { | |
int key = rand() % (int) (sizeof charset - 1); | |
str[n] = charset[key]; | |
} | |
str[size] = '\0'; | |
} | |
return str; | |
} | |
i32 main() { | |
ht_i32_t table; | |
ht_i32_mk(&table, 200); | |
char * str = calloc(24, sizeof(char)); | |
for (i32 u = 0; u < 1e4; u++) { | |
str = rand_string(str, 20); | |
ht_i32_add(&table, str, u); | |
} | |
ht_i32_add(&table, rand_string(str, 20),100); | |
// ht_get | |
u32 storage = 0; | |
printf("%u\n", table.used); | |
for (u32 u = 0; u < table.sz; u++) { | |
ht_i32_item_t * current = table.e[u]; | |
while (current) { | |
storage++; | |
current = current -> next; | |
} | |
} | |
printf("Retrieved: %u, Collisions: %u\n", storage, collisions); | |
ht_i32_rm(&table); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment