C Fundamentals
Algorithms · data structures · cryptography · systems — pure C11, zero deps
Loading...
Searching...
No Matches
hash_table.c
Go to the documentation of this file.
1/**
2 * @file hash_table.c
3 *
4 * Open-addressing hash table with linear probing and tombstone deletion.
5 * Capacity is a power of two so we can replace `% capacity` with a mask.
6 */
7
8#include "hash_table.h"
9#include <stdint.h>
10#include <stdlib.h>
11#include <string.h>
12
13#define HT_INITIAL_CAPACITY 16
14#define HT_MAX_LOAD 0.75
15
17
18typedef struct {
19 char *key;
20 int value;
22} ht_slot_t;
23
24struct hash_table {
26 size_t capacity; /* power of two */
27 size_t size; /* number of OCCUPIED slots */
28};
29
30/* djb2 — Daniel J. Bernstein. Multiply-by-33 with seed 5381. */
31static uint64_t djb2(const char *s) {
32 uint64_t h = 5381;
33 int c;
34 while ((c = (unsigned char)*s++)) h = ((h << 5) + h) + (uint64_t)c;
35 return h;
36}
37
38static bool ht_resize(hash_table_t *ht, size_t new_cap);
39
41 hash_table_t *ht = malloc(sizeof(*ht));
42 if (!ht) return NULL;
44 ht->size = 0;
45 ht->slots = calloc(ht->capacity, sizeof(*ht->slots));
46 if (!ht->slots) { free(ht); return NULL; }
47 return ht;
48}
49
51 if (!ht) return;
52 for (size_t i = 0; i < ht->capacity; i++) {
53 if (ht->slots[i].state == SLOT_OCCUPIED) free(ht->slots[i].key);
54 }
55 free(ht->slots);
56 free(ht);
57}
58
59/** Returns the slot index for `key` (insert position or existing match). */
60static size_t probe(const ht_slot_t *slots, size_t cap, const char *key) {
61 size_t mask = cap - 1;
62 size_t i = (size_t)djb2(key) & mask;
63 size_t first_tombstone = (size_t)-1;
64
65 while (slots[i].state != SLOT_EMPTY) {
66 if (slots[i].state == SLOT_OCCUPIED && strcmp(slots[i].key, key) == 0) {
67 return i;
68 }
69 if (slots[i].state == SLOT_TOMBSTONE && first_tombstone == (size_t)-1) {
70 first_tombstone = i;
71 }
72 i = (i + 1) & mask;
73 }
74 return first_tombstone == (size_t)-1 ? i : first_tombstone;
75}
76
77bool ht_set(hash_table_t *ht, const char *key, int value) {
78 if (!ht || !key) return false;
79
80 if ((double)(ht->size + 1) / (double)ht->capacity > HT_MAX_LOAD) {
81 if (!ht_resize(ht, ht->capacity * 2)) return false;
82 }
83
84 size_t i = probe(ht->slots, ht->capacity, key);
85 if (ht->slots[i].state == SLOT_OCCUPIED) {
86 ht->slots[i].value = value;
87 return true;
88 }
89
90 char *dup = strdup(key);
91 if (!dup) return false;
92 ht->slots[i].key = dup;
93 ht->slots[i].value = value;
94 ht->slots[i].state = SLOT_OCCUPIED;
95 ht->size++;
96 return true;
97}
98
99bool ht_get(const hash_table_t *ht, const char *key, int *out) {
100 if (!ht || !key) return false;
101 size_t i = probe(ht->slots, ht->capacity, key);
102 if (ht->slots[i].state != SLOT_OCCUPIED) return false;
103 if (out) *out = ht->slots[i].value;
104 return true;
105}
106
107bool ht_remove(hash_table_t *ht, const char *key) {
108 if (!ht || !key) return false;
109 size_t i = probe(ht->slots, ht->capacity, key);
110 if (ht->slots[i].state != SLOT_OCCUPIED) return false;
111 free(ht->slots[i].key);
112 ht->slots[i].key = NULL;
113 ht->slots[i].state = SLOT_TOMBSTONE;
114 ht->size--;
115 return true;
116}
117
118bool ht_contains(const hash_table_t *ht, const char *key) {
119 return ht_get(ht, key, NULL);
120}
121
122size_t ht_size(const hash_table_t *ht) { return ht ? ht->size : 0; }
123size_t ht_capacity(const hash_table_t *ht) { return ht ? ht->capacity : 0; }
124
125static bool ht_resize(hash_table_t *ht, size_t new_cap) {
126 ht_slot_t *new_slots = calloc(new_cap, sizeof(*new_slots));
127 if (!new_slots) return false;
128
129 /* Rehash: struct-copy occupied slots into new_slots — the key pointer is
130 transferred (no realloc), tombstones are dropped on the floor. */
131 for (size_t i = 0; i < ht->capacity; i++) {
132 if (ht->slots[i].state == SLOT_OCCUPIED) {
133 size_t j = probe(new_slots, new_cap, ht->slots[i].key);
134 new_slots[j] = ht->slots[i];
135 }
136 }
137 free(ht->slots);
138 ht->slots = new_slots;
139 ht->capacity = new_cap;
140 return true;
141}
#define HT_MAX_LOAD
Definition hash_table.c:14
static bool ht_resize(hash_table_t *ht, size_t new_cap)
Definition hash_table.c:125
bool ht_contains(const hash_table_t *ht, const char *key)
Definition hash_table.c:118
size_t ht_capacity(const hash_table_t *ht)
Definition hash_table.c:123
static uint64_t djb2(const char *s)
Definition hash_table.c:31
slot_state_t
Definition hash_table.c:16
@ SLOT_EMPTY
Definition hash_table.c:16
@ SLOT_OCCUPIED
Definition hash_table.c:16
@ SLOT_TOMBSTONE
Definition hash_table.c:16
bool ht_set(hash_table_t *ht, const char *key, int value)
Definition hash_table.c:77
static size_t probe(const ht_slot_t *slots, size_t cap, const char *key)
Definition hash_table.c:60
#define HT_INITIAL_CAPACITY
Definition hash_table.c:13
bool ht_remove(hash_table_t *ht, const char *key)
Definition hash_table.c:107
hash_table_t * ht_create(void)
Definition hash_table.c:40
bool ht_get(const hash_table_t *ht, const char *key, int *out)
Definition hash_table.c:99
size_t ht_size(const hash_table_t *ht)
Definition hash_table.c:122
void ht_destroy(hash_table_t *ht)
Definition hash_table.c:50
Open-addressing string→int hash table with linear probing.
ht_slot_t * slots
Definition hash_table.c:25
size_t capacity
Definition hash_table.c:26
size_t size
Definition hash_table.c:27
char * key
Definition hash_table.c:19
slot_state_t state
Definition hash_table.c:21