Skip to content

Commit a7070ea

Browse files
authored
Add C implementations for stack and hashmap
1 parent 4213a1c commit a7070ea

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

_posts/2025-11-30-comptime-c-functions.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,208 @@ date: 2025-11-29 12:00:00 +0100
88

99
[Compiler Explorer](https://godbolt.org/z/PaoT9j1Ed)
1010

11+
```c
12+
#include <assert.h>
13+
#include <stdbool.h>
14+
#include <stddef.h>
15+
#include <stdint.h>
16+
#include <stdio.h>
17+
#include <string.h>
18+
19+
typedef enum ErrorCode {
20+
SUCCESS = 0,
21+
STACK_FULL = -1,
22+
STACK_EMPTY = -2,
23+
} ErrorCode;
24+
25+
typedef struct stack {
26+
void *data;
27+
size_t size;
28+
size_t capacity;
29+
size_t element_size;
30+
} stack;
31+
32+
static inline void stack_init(stack *s, void *buffer, size_t element_size, size_t capacity) {
33+
s->data = buffer;
34+
s->size = 0;
35+
s->capacity = capacity;
36+
s->element_size = element_size;
37+
}
38+
39+
static inline ErrorCode stack_push(stack *s, const void *element) {
40+
if (s->size >= s->capacity) {
41+
return STACK_FULL;
42+
}
43+
memcpy((unsigned char *)s->data + s->size * s->element_size, element, s->element_size);
44+
s->size++;
45+
return SUCCESS;
46+
}
47+
48+
static inline ErrorCode stack_pop(stack *s, void *out) {
49+
if (s->size == 0) {
50+
return STACK_EMPTY;
51+
}
52+
s->size--;
53+
memcpy(out, (unsigned char *)s->data + s->size * s->element_size, s->element_size);
54+
return SUCCESS;
55+
}
56+
57+
static inline bool stack_empty(const stack *s) {
58+
return s->size == 0;
59+
}
60+
61+
typedef struct {
62+
uint32_t a;
63+
uint32_t b;
64+
} Pair32;
65+
66+
int main(void) {
67+
Pair32 buffer[100];
68+
stack s;
69+
stack_init(&s, buffer, sizeof(Pair32), 100);
70+
71+
Pair32 p1 = {.a = 10, .b = 20};
72+
Pair32 p2 = {.a = 111, .b = 222};
73+
74+
assert(stack_push(&s, &p1) == SUCCESS);
75+
assert(stack_push(&s, &p2) == SUCCESS);
76+
77+
Pair32 out2;
78+
assert(stack_pop(&s, &out2) == SUCCESS);
79+
assert(out2.a == 111 && out2.b == 222);
80+
81+
Pair32 out1;
82+
assert(stack_pop(&s, &out1) == SUCCESS);
83+
assert(out1.a == 10 && out1.b == 20);
84+
85+
assert(stack_empty(&s));
86+
87+
printf("Stack test passed.\n");
88+
}
89+
```
90+
1191
# Generic Hash Map
1292
1393
[Compiler Explorer](https://godbolt.org/z/16xhne83s)
94+
95+
```c
96+
#include <assert.h>
97+
#include <stdbool.h>
98+
#include <math.h>
99+
#include <stddef.h>
100+
#include <stdio.h>
101+
#include <string.h>
102+
103+
typedef struct {
104+
bool occupied;
105+
unsigned char key_value[];
106+
} entry;
107+
108+
typedef struct {
109+
void *entries;
110+
size_t capacity, key_size, value_size, entry_size;
111+
} hashmap;
112+
113+
static inline void hashmap_init(hashmap *m, void *buf, size_t ks, size_t vs, size_t cap) {
114+
m->entries = buf;
115+
m->capacity = cap;
116+
m->key_size = ks;
117+
m->value_size = vs;
118+
m->entry_size = sizeof(entry) + ks + vs;
119+
memset(buf, 0, cap * m->entry_size);
120+
}
121+
122+
static inline size_t hash(const void *key, size_t size) {
123+
size_t h = 0;
124+
for (size_t i = 0; i < size; i++)
125+
h = h * 31 + ((unsigned char*)key)[i];
126+
return h;
127+
}
128+
129+
__attribute__((always_inline)) // Necessary for GCC, not Clang
130+
static inline void hashmap_insert(hashmap *m, const void *key, const void *val) {
131+
size_t idx = hash(key, m->key_size) % m->capacity;
132+
for (size_t i = 0; i < m->capacity; i++) {
133+
entry *e = (entry*)((unsigned char*)m->entries + ((idx + i) % m->capacity) * m->entry_size);
134+
if (!e->occupied || memcmp(e->key_value, key, m->key_size) == 0) {
135+
e->occupied = true;
136+
memcpy(e->key_value, key, m->key_size);
137+
memcpy(e->key_value + m->key_size, val, m->value_size);
138+
return;
139+
}
140+
}
141+
}
142+
143+
static inline bool hashmap_get(hashmap *m, const void *key, void *out) {
144+
size_t idx = hash(key, m->key_size) % m->capacity;
145+
for (size_t i = 0; i < m->capacity; i++) {
146+
entry *e = (entry*)((unsigned char*)m->entries + ((idx + i) % m->capacity) * m->entry_size);
147+
if (!e->occupied) return false;
148+
if (memcmp(e->key_value, key, m->key_size) == 0) {
149+
memcpy(out, e->key_value + m->key_size, m->value_size);
150+
return true;
151+
}
152+
}
153+
return false;
154+
}
155+
156+
// Type-safe wrapper for (int, char*) map
157+
typedef struct {
158+
bool occupied;
159+
int key;
160+
char *value;
161+
} IntStrMap_entry;
162+
163+
// Type-safe wrapper for (int, double) map
164+
typedef struct {
165+
bool occupied;
166+
int key;
167+
double value;
168+
} IntDblMap_entry;
169+
170+
int main(void) {
171+
// Test (int, char*) map
172+
IntStrMap_entry buf1[8] = {0};
173+
hashmap m1;
174+
hashmap_init(&m1, buf1, sizeof(int), sizeof(char*), 8);
175+
176+
int k1 = 42;
177+
char *v1 = "foo";
178+
hashmap_insert(&m1, &k1, &v1);
179+
180+
int k2 = 7;
181+
char *v2 = "bar";
182+
hashmap_insert(&m1, &k2, &v2);
183+
184+
char *r1;
185+
hashmap_get(&m1, &k1, &r1);
186+
char *r2;
187+
hashmap_get(&m1, &k2, &r2);
188+
189+
assert(strcmp(r1, "foo") == 0);
190+
assert(strcmp(r2, "bar") == 0);
191+
192+
// Test (int, double) map
193+
IntDblMap_entry buf2[8] = {0};
194+
hashmap m2;
195+
hashmap_init(&m2, buf2, sizeof(int), sizeof(double), 8);
196+
197+
int k3 = 10;
198+
double v3 = 3.14159;
199+
hashmap_insert(&m2, &k3, &v3);
200+
201+
int k4 = 20;
202+
double v4 = 2.71828;
203+
hashmap_insert(&m2, &k4, &v4);
204+
205+
double d1;
206+
hashmap_get(&m2, &k3, &d1);
207+
double d2;
208+
hashmap_get(&m2, &k4, &d2);
209+
210+
assert(fabs(d1 - 3.14159) < 0.00001);
211+
assert(fabs(d2 - 2.71828) < 0.00001);
212+
213+
printf("Hash map test passed.\n");
214+
}
215+
```

0 commit comments

Comments
 (0)