├── README.md
├── UNLICENSE
├── test.c
├── tinybuf.h
├── tinyhashmap.h
└── tinyhashmap64.h
/README.md:
--------------------------------------------------------------------------------
1 | # C Data Structures
2 |
3 | Simple and convenient data structure single-file public domain libraries for C/C++
4 |
5 | * [TinyHashMap](#tinyhashmap---simple-hash-map) - Simple Hash Map (in 111 lines of code)
6 | * [TinyBuf](#tinybuf---simple-dynamic-array) - Simple Dynamic Array (in 50 lines of code)
7 |
8 |
9 | ## TinyHashMap - Simple Hash Map
10 |
11 | Implements a hash map with 32-bit or 64-bit (see [TinyHashMap64](#64-bit-keys)) keys.
12 | Based on the implementation from the public domain Bitwise project by Per Vognsen - https://github.com/pervognsen/bitwise
13 |
14 | It's a super simple type safe hash map for C with no need to predeclare any type or anything.
15 | Will always allocate memory for twice the amount of max elements so larger structs should be stored as pointers or indices to an array.
16 | Can be used in C++ with POD types (without any constructor/destructor).
17 |
18 | ### Usage
19 | ```c
20 | #include "tinyhashmap.h"
21 | ```
22 | See [tinyhashmap.h](tinyhashmap.h)
23 |
24 | #### Set 2 elements with string keys and mytype_t values
25 | ```c
26 | mytype_t* map = NULL;
27 | HMAP_SET_STR(map, "foo", foo_element);
28 | HMAP_SET_STR(map, "bar", bar_element);
29 | ```
30 | Now `HMAP_LEN(map) == 2`, `HMAP_GET_STR(map, "foo") == foo_element`, `HMAP_GET_STR(map, "bar") == bar_element`
31 |
32 | #### Check if keys exist
33 | ```c
34 | bool has_foo = HMAP_HAS_STR(map, "foo");
35 | bool has_baz = HMAP_HAS_STR(map, "baz");
36 | ```
37 | Now `has_foo == true`, `has_baz == false`
38 |
39 | #### Removing a key
40 | ```c
41 | bool removed = HMAP_DEL_STR(map, "bar");
42 | bool removed_again = HMAP_DEL_STR(map, "bar");
43 | ```
44 | Now `HMAP_LEN(map) == 1`, `removed == true`, `removed_again == false`
45 |
46 | #### Add/modify via pointer
47 | ```c
48 | mytype_t* p_elem = HMAP_PTR_STR(map, "qux");
49 | p_elem->a = 123;
50 | ```
51 | New keys initially have memory uninitialized
52 | Pointers can get invalidated when a key is added/removed
53 |
54 | #### Looking up the index for a given key
55 | ```c
56 | ptrdiff_t idx_foo = HMAP_IDX_STR(map, "foo");
57 | ptrdiff_t idx_invalid = HMAP_IDX_STR(map, "invalid");
58 | ```
59 | Now `idx_foo >= 0`, `idx_invalid == -1`, `map[idx_foo] == foo_element`
60 | Indices can change when a key is added/removed
61 |
62 | #### Clear all elements (keep memory allocated)
63 | ```c
64 | HMAP_CLEAR(map);
65 | ```
66 | Now `HMAP_LEN(map) == 0`, `HMAP_CAP(map) == 16`
67 |
68 | #### Reserve memory for at least N elements
69 | ```c
70 | HMAP_FIT(map, 30);
71 | ```
72 | Now `HMAP_LEN(map) == 0`, `HMAP_CAP(map) == 64`
73 |
74 | #### Add elements with custom hash keys
75 | ```c
76 | HMAP_SET(map, my_uint32_hash(key1), some_element);
77 | HMAP_SET(map, my_uint32_hash(key2), other_element);
78 | ```
79 | Now `HMAP_LEN(map) == 2`
80 |
81 | #### Iterate elements (random order, order can change on insert)
82 | ```c
83 | for (size_t i = 0, cap = HMAP_CAP(map); i != cap, i++)
84 | if (HMAP_KEY(map, i))
85 | ```
86 | Inside that if, `map[i]` is the value of key `HMAP_KEY(map, i)`
87 |
88 | #### Set a custom null value (is zeroed by default)
89 | ```c
90 | HMAP_SETNULLVAL(map, map_null);
91 | ```
92 | Now `HMAP_GET_STR(map, "invalid") == map_null`
93 |
94 | #### Free allocated memory
95 | ```c
96 | HMAP_FREE(map);
97 | ```
98 | Now `map == NULL`, `HMAP_LEN(map) == 0`, `HMAP_CAP(map) == 0`
99 |
100 | #### To handle running out of memory
101 | ```c
102 | bool ran_out_of_memory = !HMAP_TRYFIT(map, 1000);
103 | ```
104 | You can check memory availability with this before setting an element (with `SET`, `PTR` or `NULLVAL`).
105 | When out of memory, `map` will stay unmodified.
106 |
107 | ### Filtered string keys
108 | A neat trick with string based keys is to filter the strings while hashing.
109 | For example, this hash function ignores white space and case:
110 | ```c
111 | static uint32_t hash_nocase_nospace(const char* str)
112 | {
113 | unsigned char c;
114 | uint32_t hash = (uint32_t)0x811c9dc5;
115 | while ((c = (unsigned char)*(str++)) != '\0')
116 | if (c > ' ')
117 | hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c));
118 | return (hash ? hash : 1);
119 | }
120 | ```
121 | Then `HMAP_GET(map, hash_nocase_nospace("TEST A"))` and `HMAP_PTR(map, hash_nocase_nospace("testa"))` return the same.
122 |
123 | ### Notes
124 | Be careful not to supply modifying statements to the macro arguments.
125 | Something like `HMAP_FIT(map, i++);` would have unintended results.
126 |
127 | ### 64-bit Keys
128 | Include `tinyhashmap64.h` instead and use the prefix `HMAP64_` instead of `HMAP_`. See [tinyhashmap64.h](tinyhashmap64.h)
129 |
130 |
131 | ## TinyBuf - Simple Dynamic Array
132 |
133 | Implements stretchy buffers as invented (?) by Sean Barrett.
134 | Based on the implementation from the public domain Bitwise project by Per Vognsen - https://github.com/pervognsen/bitwise
135 |
136 | It's a super simple type safe dynamic array for C with no need to predeclare any type or anything.
137 | The first time an element is added, memory for 16 elements are allocated. Then every time length is about to exceed capacity, capacity is doubled.
138 | Can be used in C++ with POD types (without any constructor/destructor).
139 |
140 | ### Usage
141 | ```c
142 | #include "tinybuf.h"
143 | ```
144 | See [tinybuf.h](tinybuf.h)
145 |
146 | #### Setup and adding 2 elements
147 | ```c
148 | mytype_t* buf = NULL;
149 | BUF_PUSH(buf, some_element);
150 | BUF_PUSH(buf, other_element);
151 | ```
152 | Now `BUF_LEN(buf) == 2`, `buf[0] == some_element`, `buf[1] == other_element`
153 |
154 | #### Free allocated memory
155 | ```c
156 | BUF_FREE(buf);
157 | ```
158 | Now `buf == NULL`, `BUF_LEN(buf) == 0`, `BUF_CAP(buf) == 0`
159 |
160 | #### Explicitly increase allocated memory and set capacity
161 | ```c
162 | BUF_FIT(buf, 100);
163 | ```
164 | Now `BUF_LEN(buf) == 0`, `BUF_CAP(buf) == 100`
165 |
166 | #### Resize buffer (does not initialize or zero memory!)
167 | ```c
168 | BUF_RESIZE(buf, 200);
169 | ```
170 | Now `BUF_LEN(buf) == 200`, `BUF_CAP(buf) == 200`
171 |
172 | #### Remove an element in the middle, keeping order
173 | ```c
174 | BUF_REMOVE(buf, 30);
175 | ```
176 | Now `BUF_LEN(buf) == 199`, everything past 30 shifted 1 up
177 |
178 | #### Remove an element in the middle, swapping the last element into it
179 | ```c
180 | BUF_SWAPREMOVE(buf, 10);
181 | ```
182 | Now `BUF_LEN(buf) == 198`, element 198 was copied into 10
183 |
184 | #### Insert an element into the middle of the array
185 | ```c
186 | BUF_INSERT(buf, 100, some_element);
187 | ```
188 | Now `BUF_LEN(buf) == 199`, everything past 99 shifted 1 down
189 |
190 | #### Make a gap of a given size in the middle of the array
191 | ```c
192 | mytype_t* ptr_gap = BUF_MAKEGAP(buf, 20, 11);
193 | ```
194 | Now `BUF_LEN(buf) == 210`, everything past 19 shifted 11 down
195 |
196 | #### Add multiple elements at the end (uninitialized)
197 | ```c
198 | mytype_t* ptr1 = BUF_ADD(buf, 10);
199 | ```
200 | Now `BUF_LEN(buf) == 220`, `ptr1` points to `buf[210]`
201 |
202 | #### Add multiple elements at the end (zeroing memory)
203 | ```c
204 | mytype_t* ptr2 = BUF_ADDZEROED(buf, 10);
205 | ```
206 | Now `BUF_LEN(buf) == 230`, `ptr2` points to `buf[220]`
207 |
208 | #### To handle running out of memory
209 | ```c
210 | bool ran_out_of_memory = !BUF_TRYFIT(buf, 1000);
211 | ```
212 | before `RESIZE or `PUSH.
213 | When out of memory, buf will stay unmodified.
214 |
215 | ### Notes
216 | Be careful not to supply modifying statements to the macro arguments.
217 | Something like `BUF_REMOVE(buf, i--);` would have unintended results.
218 |
219 |
220 | # Public Domain (Unlicense)
221 |
222 | This is free and unencumbered software released into the public domain.
223 |
224 | Anyone is free to copy, modify, publish, use, compile, sell, or
225 | distribute this software, either in source code form or as a compiled
226 | binary, for any purpose, commercial or non-commercial, and by any
227 | means.
228 |
229 | In jurisdictions that recognize copyright laws, the author or authors
230 | of this software dedicate any and all copyright interest in the
231 | software to the public domain. We make this dedication for the benefit
232 | of the public at large and to the detriment of our heirs and
233 | successors. We intend this dedication to be an overt act of
234 | relinquishment in perpetuity of all present and future rights to this
235 | software under copyright law.
236 |
237 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
238 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
239 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
240 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
241 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
242 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
243 | OTHER DEALINGS IN THE SOFTWARE.
244 |
245 | For more information, please refer to
246 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/test.c:
--------------------------------------------------------------------------------
1 | /* C Data Structures Test - public domain - Bernhard Schelling 2020
2 | https://github.com/schellingb/c-data-structures
3 | no warranty implied; use at your own risk
4 |
5 | This is some test code for this project.
6 | Simple and convenient data structure single-file public domain libraries for C/C++
7 |
8 | PUBLIC DOMAIN (UNLICENSE)
9 |
10 | This is free and unencumbered software released into the public domain.
11 |
12 | Anyone is free to copy, modify, publish, use, compile, sell, or
13 | distribute this software, either in source code form or as a compiled
14 | binary, for any purpose, commercial or non-commercial, and by any
15 | means.
16 |
17 | In jurisdictions that recognize copyright laws, the author or authors
18 | of this software dedicate any and all copyright interest in the
19 | software to the public domain. We make this dedication for the benefit
20 | of the public at large and to the detriment of our heirs and
21 | successors. We intend this dedication to be an overt act of
22 | relinquishment in perpetuity of all present and future rights to this
23 | software under copyright law.
24 |
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
31 | OTHER DEALINGS IN THE SOFTWARE.
32 |
33 | For more information, please refer to
34 | */
35 |
36 | #include "tinybuf.h"
37 | #include "tinyhashmap.h"
38 | #include "tinyhashmap64.h"
39 |
40 | #include
41 | #define CDS_ASSERT(cond) (void)((cond) ? ((int)0) : (*(volatile int*)0 = 0xbad|fprintf(stderr, "FAILED ASSERT (%s)\n", #cond )))
42 |
43 | typedef struct { int a, b, c; } mytype_t;
44 |
45 | static void test_buf()
46 | {
47 | mytype_t *buf = NULL, *ptr;
48 | mytype_t some_element = { 1, 2, 3 };
49 | mytype_t other_element = { 500, 10, 99 };
50 | int i, ran_out_of_memory;
51 |
52 | /* Add elements: */
53 | BUF_PUSH(buf, some_element);
54 | BUF_PUSH(buf, other_element);
55 | CDS_ASSERT(BUF_LEN(buf) == 2);
56 | CDS_ASSERT(!memcmp(&buf[0], &some_element, sizeof(some_element)));
57 | CDS_ASSERT(!memcmp(&buf[1], &other_element, sizeof(other_element)));
58 |
59 | /* Free allocated memory: */
60 | BUF_FREE(buf);
61 | CDS_ASSERT(buf == NULL);
62 | CDS_ASSERT(BUF_LEN(buf) == 0);
63 | CDS_ASSERT(BUF_CAP(buf) == 0);
64 |
65 | /* Explicitly increase allocated memory and set capacity: */
66 | BUF_FIT(buf, 100);
67 | CDS_ASSERT(BUF_LEN(buf) == 0);
68 | CDS_ASSERT(BUF_CAP(buf) == 100);
69 |
70 | /* Resize buffer (does not initialize or zero memory!): */
71 | BUF_RESIZE(buf, 200);
72 | CDS_ASSERT(BUF_LEN(buf) == 200);
73 | CDS_ASSERT(BUF_CAP(buf) == 200);
74 |
75 | /* Remove an element in the middle, keeping order: */
76 | for (i = 0; i != (int)BUF_LEN(buf); i++) buf[i].a = i;
77 | BUF_REMOVE(buf, 30);
78 | CDS_ASSERT(BUF_LEN(buf) == 199);
79 | CDS_ASSERT(buf[29].a == 29);
80 | CDS_ASSERT(buf[30].a == 31);
81 |
82 | /* Remove an element in the middle, swapping the last element into it: */
83 | for (i = 0; i != (int)BUF_LEN(buf); i++) buf[i].a = i;
84 | BUF_SWAPREMOVE(buf, 10);
85 | CDS_ASSERT(BUF_LEN(buf) == 198);
86 | CDS_ASSERT(buf[0].a == 0 && buf[9].a == 9 && buf[10].a == 198 && buf[11].a == 11 && buf[197].a == 197);
87 |
88 | /* Insert an element into the middle of the array: */
89 | for (i = 0; i != (int)BUF_LEN(buf); i++) buf[i].a = i;
90 | BUF_INSERT(buf, 100, some_element);
91 | CDS_ASSERT(BUF_LEN(buf) == 199);
92 | CDS_ASSERT(!memcmp(&buf[100], &some_element, sizeof(some_element)));
93 | CDS_ASSERT(buf[0].a == 0 && buf[99].a == 99 && buf[101].a == 100 && buf[198].a == 197);
94 |
95 | /* Make a gap of a given size in the middle of the array: */
96 | for (i = 0; i != (int)BUF_LEN(buf); i++) buf[i].a = i;
97 | ptr = BUF_MAKEGAP(buf, 20, 11);
98 | CDS_ASSERT(BUF_LEN(buf) == 210 && ptr == &buf[20]);
99 | CDS_ASSERT(buf[0].a == 0 && buf[19].a == 19 && buf[31].a == 20 && buf[199].a == 188 && buf[209].a == 198);
100 |
101 | /* Add multiple elements at the end (uninitialized): */
102 | BUF_ADD(buf, 10)[5] = some_element;
103 | CDS_ASSERT(BUF_LEN(buf) == 220);
104 | CDS_ASSERT(!memcmp(&buf[215], &some_element, sizeof(some_element)));
105 |
106 | /* Add multiple elements at the end (zero memory): */
107 | ptr = BUF_ADDZEROED(buf, 10);
108 | CDS_ASSERT(BUF_LEN(buf) == 230 && ptr == &buf[220]);
109 | CDS_ASSERT(*(char*)&buf[220] == 0 && !memcmp(&buf[220], (char*)&buf[220] + 1, sizeof(*buf)*10-1));
110 |
111 | /* Handle running out of memory: */
112 | ran_out_of_memory = !BUF_TRYFIT(buf, 1000);
113 | CDS_ASSERT(!ran_out_of_memory);
114 | CDS_ASSERT(BUF_LEN(buf) == 230);
115 | CDS_ASSERT(BUF_CAP(buf) == 1000);
116 | ran_out_of_memory = !BUF_TRYFIT(buf, (sizeof(void*) > 4 ? 0xFFFF000000000000 : 0xFFFF0000) / sizeof(mytype_t));
117 | CDS_ASSERT(ran_out_of_memory);
118 | CDS_ASSERT(BUF_LEN(buf) == 230);
119 | CDS_ASSERT(BUF_CAP(buf) == 1000);
120 |
121 | BUF_FREE(buf);
122 | }
123 |
124 | static uint32_t hash_nocase_nospace(const char* str)
125 | {
126 | unsigned char c;
127 | uint32_t hash = (uint32_t)0x811c9dc5;
128 | while ((c = (unsigned char)*(str++)) != '\0')
129 | if (c > ' ')
130 | hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c));
131 | return (hash ? hash : 1);
132 | }
133 |
134 | static void test_hmap()
135 | {
136 | mytype_t* map = NULL;
137 | mytype_t some_element = { 1, 2, 3 };
138 | mytype_t other_element = { 500, 10, 99 };
139 | mytype_t map_null = { -1, -1, -1 };
140 | mytype_t* p_elem;
141 | int has_foo, has_baz, removed, removed_again, it_found_count, ran_out_of_memory;
142 | size_t i, cap;
143 | ptrdiff_t idx_foo, idx_invalid;
144 |
145 | /* Set 2 elements with string keys and mytype_t values: */
146 | HMAP_SET_STR(map, "foo", some_element);
147 | HMAP_SET_STR(map, "bar", other_element);
148 | CDS_ASSERT(HMAP_LEN(map) == 2);
149 | CDS_ASSERT(!memcmp(HMAP_PTR_STR(map, "foo"), &some_element, sizeof(some_element)));
150 | CDS_ASSERT(!memcmp(HMAP_PTR_STR(map, "bar"), &other_element, sizeof(other_element)));
151 |
152 | /* Check if keys exist: */
153 | has_foo = HMAP_HAS_STR(map, "foo");
154 | has_baz = HMAP_HAS_STR(map, "baz");
155 | CDS_ASSERT(has_foo);
156 | CDS_ASSERT(!has_baz);
157 |
158 | /* Removing a key: */
159 | removed = HMAP_DEL_STR(map, "bar");
160 | removed_again = HMAP_DEL_STR(map, "bar");
161 | CDS_ASSERT(HMAP_LEN(map) == 1);
162 | CDS_ASSERT(removed);
163 | CDS_ASSERT(!removed_again);
164 |
165 | /* Add/modify via pointer: */
166 | p_elem = HMAP_PTR_STR(map, "qux");
167 | p_elem->a = 123;
168 | CDS_ASSERT(HMAP_GET_STR(map, "qux").a == 123);
169 |
170 | /* Looking up an index of a key: */
171 | idx_foo = HMAP_IDX_STR(map, "foo");
172 | idx_invalid = HMAP_IDX_STR(map, "invalid");
173 | CDS_ASSERT(idx_foo >= 0 && idx_invalid == -1);
174 |
175 | /* Clear all elements (keep memory allocated): */
176 | HMAP_CLEAR(map);
177 | CDS_ASSERT(HMAP_LEN(map) == 0);
178 | CDS_ASSERT(HMAP_CAP(map) == 16);
179 |
180 | /* Reserve memory for at least N elements: */
181 | HMAP_FIT(map, 30);
182 | CDS_ASSERT(HMAP_LEN(map) == 0);
183 | CDS_ASSERT(HMAP_CAP(map) == 64);
184 |
185 | /* Add elements with custom hash keys: */
186 | HMAP_SET(map, /*my_uint32_hash(key1)*/ 0x11111111, some_element);
187 | HMAP_SET(map, /*my_uint32_hash(key2)*/ 0x22222222, other_element);
188 | CDS_ASSERT(HMAP_LEN(map) == 2);
189 |
190 | /* Iterate elements (random order, order can change on insert): */
191 | it_found_count = 0;
192 | for (i = 0, cap = HMAP_CAP(map); i != cap; i++)
193 | {
194 | uint32_t key;
195 | mytype_t val;
196 | if (!HMAP_KEY(map, i)) continue;
197 | key = HMAP_KEY(map, i);
198 | val = map[i];
199 | CDS_ASSERT(key == 0x11111111 || key == 0x22222222);
200 | CDS_ASSERT(key != 0x11111111 || !memcmp(&val, &some_element, sizeof(some_element)));
201 | CDS_ASSERT(key != 0x22222222 || !memcmp(&val, &other_element, sizeof(other_element)));
202 | it_found_count++;
203 | }
204 | CDS_ASSERT(it_found_count == 2);
205 |
206 | /* Set a custom null value (is zeroed by default): */
207 | HMAP_SETNULLVAL(map, map_null);
208 | CDS_ASSERT(!memcmp(&map[HMAP_IDX_STR(map, "invalid")], &map_null, sizeof(map_null)));
209 |
210 | /* Free allocated memory: */
211 | HMAP_FREE(map);
212 | CDS_ASSERT(map == NULL);
213 | CDS_ASSERT(HMAP_LEN(map) == 0);
214 | CDS_ASSERT(HMAP_CAP(map) == 0);
215 |
216 | /* Handle running out of memory: */
217 | ran_out_of_memory = !HMAP_TRYFIT(map, 1000);
218 | CDS_ASSERT(!ran_out_of_memory);
219 | CDS_ASSERT(HMAP_LEN(map) == 0);
220 | CDS_ASSERT(HMAP_CAP(map) == 2048);
221 | ran_out_of_memory = !HMAP_TRYFIT(map, (sizeof(void*) > 4 ? 0x7FFF000000000000 : 0x7FFF0000) / sizeof(mytype_t));
222 | CDS_ASSERT(ran_out_of_memory);
223 | CDS_ASSERT(HMAP_LEN(map) == 0);
224 | CDS_ASSERT(HMAP_CAP(map) == 2048);
225 |
226 | /* Filtered string keys: */
227 | HMAP_SET(map, hash_nocase_nospace("TEST A"), some_element);
228 | CDS_ASSERT(HMAP_IDX(map, hash_nocase_nospace("TEST A")) == HMAP_IDX(map, hash_nocase_nospace("testa")));
229 | CDS_ASSERT(HMAP_IDX(map, hash_nocase_nospace("TEST A")) != HMAP_IDX(map, hash_nocase_nospace("TEST B")));
230 |
231 | HMAP_FREE(map);
232 | }
233 |
234 | static void test_hmap64()
235 | {
236 | mytype_t* map = NULL;
237 | mytype_t some_element = { 1, 2, 3 };
238 | mytype_t other_element = { 500, 10, 99 };
239 | mytype_t map_null = { -1, -1, -1 };
240 | mytype_t* p_elem;
241 | int has_foo, has_baz, removed, removed_again, it_found_count, ran_out_of_memory;
242 | size_t i, cap;
243 | ptrdiff_t idx_foo, idx_invalid;
244 |
245 | /* Set 2 elements with string keys and mytype_t values: */
246 | HMAP64_SET_STR(map, "foo", some_element);
247 | HMAP64_SET_STR(map, "bar", other_element);
248 | CDS_ASSERT(HMAP64_LEN(map) == 2);
249 | CDS_ASSERT(!memcmp(HMAP64_PTR_STR(map, "foo"), &some_element, sizeof(some_element)));
250 | CDS_ASSERT(!memcmp(HMAP64_PTR_STR(map, "bar"), &other_element, sizeof(other_element)));
251 |
252 | /* Check if keys exist: */
253 | has_foo = HMAP64_HAS_STR(map, "foo");
254 | has_baz = HMAP64_HAS_STR(map, "baz");
255 | CDS_ASSERT(has_foo);
256 | CDS_ASSERT(!has_baz);
257 |
258 | /* Removing a key: */
259 | removed = HMAP64_DEL_STR(map, "bar");
260 | removed_again = HMAP64_DEL_STR(map, "bar");
261 | CDS_ASSERT(HMAP64_LEN(map) == 1);
262 | CDS_ASSERT(removed);
263 | CDS_ASSERT(!removed_again);
264 |
265 | /* Add/modify via pointer: */
266 | p_elem = HMAP64_PTR_STR(map, "qux");
267 | p_elem->a = 123;
268 | CDS_ASSERT(HMAP64_GET_STR(map, "qux").a == 123);
269 |
270 | /* Looking up an index of a key: */
271 | idx_foo = HMAP64_IDX_STR(map, "foo");
272 | idx_invalid = HMAP64_IDX_STR(map, "invalid");
273 | CDS_ASSERT(idx_foo >= 0 && idx_invalid == -1);
274 |
275 | /* Clear all elements (keep memory allocated): */
276 | HMAP64_CLEAR(map);
277 | CDS_ASSERT(HMAP64_LEN(map) == 0);
278 | CDS_ASSERT(HMAP64_CAP(map) == 16);
279 |
280 | /* Reserve memory for at least N elements: */
281 | HMAP64_FIT(map, 30);
282 | CDS_ASSERT(HMAP64_LEN(map) == 0);
283 | CDS_ASSERT(HMAP64_CAP(map) == 64);
284 |
285 | /* Add elements with custom hash keys: */
286 | HMAP64_SET(map, /*my_uint64_hash(key1)*/ 0x1111111111111111, some_element);
287 | HMAP64_SET(map, /*my_uint64_hash(key2)*/ 0x2222222222222222, other_element);
288 | CDS_ASSERT(HMAP64_LEN(map) == 2);
289 |
290 | /* Iterate elements (random order, order can change on insert): */
291 | it_found_count = 0;
292 | for (i = 0, cap = HMAP64_CAP(map); i != cap; i++)
293 | {
294 | uint64_t key;
295 | mytype_t val;
296 | if (!HMAP64_KEY(map, i)) continue;
297 | key = HMAP64_KEY(map, i);
298 | val = map[i];
299 | CDS_ASSERT(key == 0x1111111111111111 || key == 0x2222222222222222);
300 | CDS_ASSERT(key != 0x1111111111111111 || !memcmp(&val, &some_element, sizeof(some_element)));
301 | CDS_ASSERT(key != 0x2222222222222222 || !memcmp(&val, &other_element, sizeof(other_element)));
302 | it_found_count++;
303 | }
304 | CDS_ASSERT(it_found_count == 2);
305 |
306 | /* Set a custom null value (is zeroed by default): */
307 | HMAP64_SETNULLVAL(map, map_null);
308 | CDS_ASSERT(!memcmp(&map[HMAP64_IDX_STR(map, "invalid")], &map_null, sizeof(map_null)));
309 |
310 | /* Free allocated memory: */
311 | HMAP64_FREE(map);
312 | CDS_ASSERT(map == NULL);
313 | CDS_ASSERT(HMAP64_LEN(map) == 0);
314 | CDS_ASSERT(HMAP64_CAP(map) == 0);
315 |
316 | /* Handle running out of memory: */
317 | ran_out_of_memory = !HMAP64_TRYFIT(map, 1000);
318 | CDS_ASSERT(!ran_out_of_memory);
319 | CDS_ASSERT(HMAP64_LEN(map) == 0);
320 | CDS_ASSERT(HMAP64_CAP(map) == 2048);
321 | ran_out_of_memory = !HMAP64_TRYFIT(map, (sizeof(void*) > 4 ? 0x7FFF000000000000 : 0x7FFF0000) / sizeof(mytype_t));
322 | CDS_ASSERT(ran_out_of_memory);
323 | CDS_ASSERT(HMAP64_LEN(map) == 0);
324 | CDS_ASSERT(HMAP64_CAP(map) == 2048);
325 |
326 | HMAP64_FREE(map);
327 | }
328 |
329 | int main(int argc, char *argv[])
330 | {
331 | (void)argc; (void)argv;
332 | printf("Testing buf...\n");
333 | test_buf();
334 | printf("Testing hmap...\n");
335 | test_hmap();
336 | printf("Testing hmap64...\n");
337 | test_hmap64();
338 | printf("Done!\n");
339 | return 0;
340 | }
341 |
--------------------------------------------------------------------------------
/tinybuf.h:
--------------------------------------------------------------------------------
1 | /* TinyBuf - simple dynamic array - public domain - Bernhard Schelling 2020
2 | https://github.com/schellingb/c-data-structures
3 | no warranty implied; use at your own risk
4 |
5 | This file implements stretchy buffers as invented (?) by Sean Barrett.
6 | Based on the implementation from the public domain Bitwise project
7 | by Per Vognsen - https://github.com/pervognsen/bitwise
8 |
9 | It's a super simple type safe dynamic array for C with no need
10 | to predeclare any type or anything.
11 | The first time an element is added, memory for 16 elements are allocated.
12 | Then every time length is about to exceed capacity, capacity is doubled.
13 | Can be used in C++ with POD types (without any constructor/destructor).
14 |
15 | Be careful not to supply modifying statements to the macro arguments.
16 | Something like BUF_REMOVE(buf, i--); would have unintended results.
17 |
18 | Sample usage:
19 |
20 | mytype_t* buf = NULL;
21 | BUF_PUSH(buf, some_element);
22 | BUF_PUSH(buf, other_element);
23 | -- now BUF_LEN(buf) == 2, buf[0] == some_element, buf[1] == other_element
24 |
25 | -- Free allocated memory:
26 | BUF_FREE(buf);
27 | -- now buf == NULL, BUF_LEN(buf) == 0, BUF_CAP(buf) == 0
28 |
29 | -- Explicitly increase allocated memory and set capacity:
30 | BUF_FIT(buf, 100);
31 | -- now BUF_LEN(buf) == 0, BUF_CAP(buf) == 100
32 |
33 | -- Resize buffer (does not initialize or zero memory!):
34 | BUF_RESIZE(buf, 200);
35 | -- now BUF_LEN(buf) == 200, BUF_CAP(buf) == 200
36 |
37 | -- Remove an element in the middle, keeping order:
38 | BUF_REMOVE(buf, 30);
39 | -- now BUF_LEN(buf) == 199, everything past 30 shifted 1 up
40 |
41 | -- Remove an element in the middle, swapping the last element into it:
42 | BUF_SWAPREMOVE(buf, 10);
43 | -- now BUF_LEN(buf) == 198, element 198 was copied into 10
44 |
45 | -- Insert an element into the middle of the array:
46 | BUF_INSERT(buf, 100, some_element);
47 | -- now BUF_LEN(buf) == 199, everything past 99 shifted 1 down
48 |
49 | -- Make a gap of a given size in the middle of the array:
50 | mytype_t* ptr_gap = BUF_MAKEGAP(buf, 20, 11);
51 | -- now BUF_LEN(buf) == 210, everything past 19 shifted 11 down
52 |
53 | -- Add multiple elements at the end (uninitialized):
54 | mytype_t* ptr1 = BUF_ADD(buf, 10);
55 | -- now BUF_LEN(buf) == 220, ptr1 points to buf[210]
56 |
57 | -- Add multiple elements at the end (zeroing memory):
58 | mytype_t* ptr2 = BUF_ADDZEROED(buf, 10);
59 | -- now BUF_LEN(buf) == 230, ptr2 points to buf[220]
60 |
61 | -- To handle running out of memory:
62 | bool ran_out_of_memory = !BUF_TRYFIT(buf, 1000);
63 | -- before RESIZE or PUSH. When out of memory, buf will stay unmodified.
64 |
65 | PUBLIC DOMAIN (UNLICENSE)
66 |
67 | This is free and unencumbered software released into the public domain.
68 |
69 | Anyone is free to copy, modify, publish, use, compile, sell, or
70 | distribute this software, either in source code form or as a compiled
71 | binary, for any purpose, commercial or non-commercial, and by any
72 | means.
73 |
74 | In jurisdictions that recognize copyright laws, the author or authors
75 | of this software dedicate any and all copyright interest in the
76 | software to the public domain. We make this dedication for the benefit
77 | of the public at large and to the detriment of our heirs and
78 | successors. We intend this dedication to be an overt act of
79 | relinquishment in perpetuity of all present and future rights to this
80 | software under copyright law.
81 |
82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
83 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
84 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
85 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
86 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
87 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
88 | OTHER DEALINGS IN THE SOFTWARE.
89 |
90 | For more information, please refer to
91 | */
92 |
93 | #ifndef TINYBUF_H
94 | #define TINYBUF_H
95 |
96 | #include /* for malloc, realloc */
97 | #include /* for memmove, memset */
98 | #include /* for size_t */
99 |
100 | /* Query functions */
101 | #define BUF_LEN(b) ((b) ? BUF__HDR(b)->len : 0)
102 | #define BUF_CAP(b) ((b) ? BUF__HDR(b)->cap : 0)
103 | #define BUF_END(b) ((b) + BUF_LEN(b))
104 | #define BUF_SIZEOF(b) ((b) ? BUF_LEN(b) * sizeof(*b) : 0)
105 |
106 | /* Modifying functions */
107 | #define BUF_FREE(b) ((b) ? (free(BUF__HDR(b)), (*(void**)(&(b)) = (void*)0)) : 0)
108 | #define BUF_FIT(b, n) ((size_t)(n) <= BUF_CAP(b) ? 0 : (*(void**)(&(b)) = buf__grow((b), (size_t)(n), sizeof(*(b)))))
109 | #define BUF_PUSH(b, val) (BUF_FIT((b), BUF_LEN(b) + 1), (b)[BUF__HDR(b)->len++] = (val))
110 | #define BUF_POP(b) (b)[--BUF__HDR(b)->len]
111 | #define BUF_RESIZE(b, sz) (BUF_FIT((b), (sz)), ((b) ? BUF__HDR(b)->len = (sz) : 0))
112 | #define BUF_CLEAR(b) ((b) ? BUF__HDR(b)->len = 0 : 0)
113 | #define BUF_TRYFIT(b, n) (BUF_FIT((b), (n)), (((b) && BUF_CAP(b) >= (size_t)(n)) || !(n)))
114 |
115 | /* Utility functions */
116 | #define BUF_SHIFT(b, to, fr, n) ((b) ? memmove((b) + (to), (b) + (fr), (n) * sizeof(*(b))) : 0)
117 | #define BUF_REMOVE(b, idx) (BUF_SHIFT(b, idx, (idx) + 1, --BUF__HDR(b)->len - (idx)))
118 | #define BUF_SWAPREMOVE(b, idx) (BUF_SHIFT(b, idx, --BUF__HDR(b)->len, 1))
119 | #define BUF_MAKEGAP(b, idx, sz) (BUF_RESIZE((b), BUF_LEN(b)+(sz)), BUF_SHIFT(b, (idx)+(sz), idx, BUF_LEN(b)-(idx)-(sz)), (b)+(idx))
120 | #define BUF_INSERT(b, idx, val) (*BUF_MAKEGAP(b, idx, 1) = (val))
121 | #define BUF_ZERO(b, idx, n) (memset((b)+(idx), 0, (n)*sizeof(*(b))))
122 | #define BUF_ADD(b, n) (BUF_FIT(b, BUF_LEN(b) + (n)), (b) + (BUF__HDR(b)->len += (n)) - (n))
123 | #define BUF_ADDZEROED(b, n) (BUF_FIT(b, BUF_LEN(b) + (n)), BUF_ZERO(b, BUF_LEN(b), n), (b) + (BUF__HDR(b)->len += (n)) - (n))
124 |
125 | #define BUF__HDR(b) (((struct buf__hdr *)(b))-1)
126 | struct buf__hdr { size_t len, cap; };
127 |
128 | #ifdef __GNUC__
129 | __attribute__((__unused__))
130 | #elif defined(_MSC_VER)
131 | #pragma warning(push)
132 | #pragma warning(disable:4505) //unreferenced local function has been removed
133 | #endif
134 | static void *buf__grow(void *buf, size_t new_len, size_t elem_size)
135 | {
136 | struct buf__hdr *new_hdr;
137 | size_t new_cap = 2 * BUF_CAP(buf), new_size;
138 | if (new_cap < new_len) new_cap = new_len;
139 | if (new_cap < 16) new_cap = 16;
140 | new_size = sizeof(struct buf__hdr) + new_cap*elem_size;
141 | if (buf)
142 | {
143 | new_hdr = (struct buf__hdr *)realloc(BUF__HDR(buf), new_size);
144 | if (!new_hdr)
145 | return buf; /* out of memory, return unchanged */
146 | }
147 | else
148 | {
149 | new_hdr = (struct buf__hdr *)malloc(new_size);
150 | if (!new_hdr)
151 | return (void*)0; /* out of memory */
152 | new_hdr->len = 0;
153 | }
154 | new_hdr->cap = new_cap;
155 | return new_hdr + 1;
156 | }
157 | #if defined(_MSC_VER)
158 | #pragma warning(pop)
159 | #endif
160 |
161 | #endif
162 |
--------------------------------------------------------------------------------
/tinyhashmap.h:
--------------------------------------------------------------------------------
1 | /* TinyHashMap - simple hash map - public domain - Bernhard Schelling 2020
2 | https://github.com/schellingb/c-data-structures
3 | no warranty implied; use at your own risk
4 |
5 | This file implements a hash map with 32-bit keys.
6 | Based on the implementation from the public domain Bitwise project
7 | by Per Vognsen - https://github.com/pervognsen/bitwise
8 |
9 | It's a super simple type safe hash map for C with no need
10 | to predeclare any type or anything.
11 | Will always allocate memory for twice the amount of max elements
12 | so larger structs should be stored as pointers or indices to an array.
13 | Can be used in C++ with POD types (without any constructor/destructor).
14 |
15 | Be careful not to supply modifying statements to the macro arguments.
16 | Something like HMAP_FIT(map, i++); would have unintended results.
17 |
18 | Sample usage:
19 |
20 | -- Set 2 elements with string keys and mytype_t values:
21 | mytype_t* map = NULL;
22 | HMAP_SET_STR(map, "foo", foo_element);
23 | HMAP_SET_STR(map, "bar", bar_element);
24 | -- now HMAP_LEN(map) == 2, HMAP_GET_STR(map, "foo") == foo_element
25 |
26 | -- Check if keys exist:
27 | bool has_foo = HMAP_HAS_STR(map, "foo");
28 | bool has_baz = HMAP_HAS_STR(map, "baz");
29 | -- now has_foo == true, has_baz == false
30 |
31 | -- Removing a key:
32 | bool removed = HMAP_DEL_STR(map, "bar");
33 | bool removed_again = HMAP_DEL_STR(map, "bar");
34 | -- now HMAP_LEN(map) == 1, removed == true, removed_again == false
35 |
36 | -- Add/modify via pointer:
37 | mytype_t* p_elem = HMAP_PTR_STR(map, "qux");
38 | p_elem->a = 123;
39 | -- New keys initially have memory uninitialized
40 | -- Pointers can get invalidated when a key is added/removed
41 |
42 | -- Looking up the index for a given key:
43 | ptrdiff_t idx_foo = HMAP_IDX_STR(map, "foo");
44 | ptrdiff_t idx_invalid = HMAP_IDX_STR(map, "invalid");
45 | -- now idx_foo >= 0, idx_invalid == -1, map[idx_foo] == foo_element
46 | -- Indices can change when a key is added/removed
47 |
48 | -- Clear all elements (keep memory allocated):
49 | HMAP_CLEAR(map);
50 | -- now HMAP_LEN(map) == 0, HMAP_CAP(map) == 16
51 |
52 | -- Reserve memory for at least N elements:
53 | HMAP_FIT(map, 30);
54 | -- now HMAP_LEN(map) == 0, HMAP_CAP(map) == 64
55 |
56 | -- Add elements with custom hash keys:
57 | HMAP_SET(map, my_uint32_hash(key1), some_element);
58 | HMAP_SET(map, my_uint32_hash(key2), other_element);
59 | -- now HMAP_LEN(map) == 2, _GET/_HAS/_DEL/_PTR/_IDX also exist
60 |
61 | -- Iterate elements (random order, order can change on insert):
62 | for (size_t i = 0, cap = HMAP_CAP(map); i != cap, i++)
63 | if (HMAP_KEY(map, i))
64 | ------ here map[i] is the value of key HMAP_KEY(map, i)
65 |
66 | -- Set a custom null value (is zeroed by default):
67 | HMAP_SETNULLVAL(map, map_null);
68 | -- now HMAP_GET_STR(map, "invalid") == map_null
69 |
70 | -- Free allocated memory:
71 | HMAP_FREE(map);
72 | -- now map == NULL, HMAP_LEN(map) == 0, HMAP_CAP(map) == 0
73 |
74 | -- To handle running out of memory:
75 | bool ran_out_of_memory = !HMAP_TRYFIT(map, 1000);
76 | -- before setting an element (with SET, PTR or NULLVAL).
77 | -- When out of memory, map will stay unmodified.
78 |
79 | PUBLIC DOMAIN (UNLICENSE)
80 |
81 | This is free and unencumbered software released into the public domain.
82 |
83 | Anyone is free to copy, modify, publish, use, compile, sell, or
84 | distribute this software, either in source code form or as a compiled
85 | binary, for any purpose, commercial or non-commercial, and by any
86 | means.
87 |
88 | In jurisdictions that recognize copyright laws, the author or authors
89 | of this software dedicate any and all copyright interest in the
90 | software to the public domain. We make this dedication for the benefit
91 | of the public at large and to the detriment of our heirs and
92 | successors. We intend this dedication to be an overt act of
93 | relinquishment in perpetuity of all present and future rights to this
94 | software under copyright law.
95 |
96 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
97 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
98 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
99 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
100 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
101 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
102 | OTHER DEALINGS IN THE SOFTWARE.
103 |
104 | For more information, please refer to
105 | */
106 |
107 | #ifndef TINYHASHMAP_H
108 | #define TINYHASHMAP_H
109 |
110 | #include /* for malloc, realloc */
111 | #include /* for memcpy, memset */
112 | #include /* for ptrdiff_t, size_t */
113 | #if defined(_MSC_VER) && (_MSC_VER < 1600)
114 | typedef unsigned __int32 uint32_t;
115 | #else
116 | #include /* for uint32_t */
117 | #endif
118 |
119 | #define HMAP_LEN(b) ((b) ? HMAP__HDR(b)->len : 0)
120 | #define HMAP_MAX(b) ((b) ? HMAP__HDR(b)->maxlen : 0)
121 | #define HMAP_CAP(b) ((b) ? HMAP__HDR(b)->maxlen + 1 : 0)
122 | #define HMAP_KEY(b, idx) (HMAP__HDR(b)->keys[idx])
123 | #define HMAP_SETNULLVAL(b, val) (HMAP__FIT1(b), b[-1] = (val))
124 | #define HMAP_CLEAR(b) ((b) ? (memset(HMAP__HDR(b)->keys, 0, HMAP_CAP(b) * sizeof(uint32_t)), HMAP__HDR(b)->len = 0) : 0)
125 | #define HMAP_FREE(b) ((b) ? (free(HMAP__HDR(b)->keys), free(HMAP__HDR(b)), (b) = NULL) : 0)
126 | #define HMAP_FIT(b, n) ((!(n) || ((b) && (size_t)(n) * 2 <= HMAP_MAX(b))) ? 0 : HMAP__GROW(b, n))
127 | #define HMAP_TRYFIT(b, n) (HMAP_FIT((b), (n)), (!(n) || ((b) && (size_t)(n) * 2 <= HMAP_MAX(b))))
128 |
129 | #define HMAP_SET(b, key, val) (HMAP__FIT1(b), b[hmap__idx(HMAP__HDR(b), (key), 1, 0)] = (val))
130 | #define HMAP_GET(b, key) (HMAP__FIT1(b), b[hmap__idx(HMAP__HDR(b), (key), 0, 0)])
131 | #define HMAP_HAS(b, key) ((b) ? hmap__idx(HMAP__HDR(b), (key), 0, 0) != -1 : 0)
132 | #define HMAP_DEL(b, key) ((b) ? hmap__idx(HMAP__HDR(b), (key), 0, sizeof(*(b))) != -1 : 0)
133 | #define HMAP_PTR(b, key) (HMAP__FIT1(b), &b[hmap__idx(HMAP__HDR(b), (key), 1, 0)])
134 | #define HMAP_IDX(b, key) ((b) ? hmap__idx(HMAP__HDR(b), (key), 0, 0) : -1)
135 |
136 | #ifdef __GNUC__
137 | #define HMAP__UNUSED __attribute__((__unused__))
138 | #else
139 | #define HMAP__UNUSED
140 | #endif
141 |
142 | #ifdef _MSC_VER
143 | #pragma warning(push)
144 | #pragma warning(disable:4505) //unreferenced local function has been removed
145 | #endif
146 |
147 | #define HMAP_SET_STR(b, string_key, val) HMAP_SET(b, hash_string(string_key), val)
148 | #define HMAP_GET_STR(b, string_key) HMAP_GET(b, hash_string(string_key))
149 | #define HMAP_HAS_STR(b, string_key) HMAP_HAS(b, hash_string(string_key))
150 | #define HMAP_DEL_STR(b, string_key) HMAP_DEL(b, hash_string(string_key))
151 | #define HMAP_PTR_STR(b, string_key) HMAP_PTR(b, hash_string(string_key))
152 | #define HMAP_IDX_STR(b, string_key) HMAP_IDX(b, hash_string(string_key))
153 |
154 | HMAP__UNUSED static uint32_t hash_string(const char* str)
155 | {
156 | unsigned char c;
157 | uint32_t hash = (uint32_t)0x811c9dc5;
158 | while ((c = (unsigned char)*(str++)) != '\0')
159 | hash = ((hash * (uint32_t)0x01000193) ^ (uint32_t)c);
160 | return (hash ? hash : 1);
161 | }
162 |
163 | struct hmap__hdr { size_t len, maxlen; uint32_t *keys; };
164 | #define HMAP__HDR(b) (((struct hmap__hdr *)&(b)[-1])-1)
165 | #define HMAP__GROW(b, n) (*(void**)(&(b)) = hmap__grow(HMAP__HDR(b), (void*)(b), sizeof(*(b)), (size_t)(n)))
166 | #define HMAP__FIT1(b) ((b) && HMAP_LEN(b) * 2 <= HMAP_MAX(b) ? 0 : HMAP__GROW(b, 0))
167 |
168 | HMAP__UNUSED static void* hmap__grow(struct hmap__hdr *old_hdr, void* old_ptr, size_t elem_size, size_t reserve)
169 | {
170 | struct hmap__hdr *new_hdr;
171 | char *new_vals;
172 | size_t new_max = (old_ptr ? old_hdr->maxlen * 2 + 1 : 15);
173 | while (new_max && new_max / 2 <= reserve)
174 | if (!(new_max = new_max * 2 + 1))
175 | return old_ptr; /* overflow */
176 |
177 | new_hdr = (struct hmap__hdr *)malloc(sizeof(struct hmap__hdr) + (new_max + 2) * elem_size);
178 | if (!new_hdr)
179 | return old_ptr; /* out of memory */
180 |
181 | new_hdr->maxlen = new_max;
182 | new_hdr->keys = (uint32_t *)calloc(new_max + 1, sizeof(uint32_t));
183 | if (!new_hdr->keys)
184 | return (free(new_hdr), old_ptr); /* out of memory */
185 |
186 | new_vals = ((char*)(new_hdr + 1)) + elem_size;
187 | if (old_ptr)
188 | {
189 | size_t i;
190 | char* old_vals = ((char*)(old_hdr + 1)) + elem_size;
191 | for (i = 0; i <= old_hdr->maxlen; i++)
192 | {
193 | uint32_t key, j;
194 | if (!old_hdr->keys[i])
195 | continue;
196 | for (key = old_hdr->keys[i], j = key;; j++)
197 | {
198 | if (!new_hdr->keys[j &= new_hdr->maxlen])
199 | {
200 | new_hdr->keys[j] = key;
201 | memcpy(new_vals + j * elem_size, old_vals + i * elem_size, elem_size);
202 | break;
203 | }
204 | }
205 | }
206 | memcpy(new_vals - elem_size, old_vals - elem_size, elem_size);
207 | new_hdr->len = old_hdr->len;
208 | free(old_hdr->keys);
209 | free(old_hdr);
210 | }
211 | else
212 | {
213 | memset(new_vals - elem_size, 0, elem_size);
214 | new_hdr->len = 0;
215 | }
216 | return new_vals;
217 | }
218 |
219 | HMAP__UNUSED static ptrdiff_t hmap__idx(struct hmap__hdr* hdr, uint32_t key, int add, size_t del)
220 | {
221 | uint32_t i;
222 |
223 | if (!key)
224 | return (ptrdiff_t)-1;
225 |
226 | for (i = key;; i++)
227 | {
228 | if (hdr->keys[i &= hdr->maxlen] == key)
229 | {
230 | if (del)
231 | {
232 | hdr->len--;
233 | hdr->keys[i] = 0;
234 | while ((key = hdr->keys[i = (i + 1) & hdr->maxlen]) != 0)
235 | {
236 | if ((key = (uint32_t)hmap__idx(hdr, key, 1, 0)) == i) continue;
237 | hdr->len--;
238 | hdr->keys[i] = 0;
239 | memcpy(((char*)(hdr + 1)) + (key + 1) * del,
240 | ((char*)(hdr + 1)) + (i + 1) * del, del);
241 | }
242 | }
243 | return (ptrdiff_t)i;
244 | }
245 | if (!hdr->keys[i])
246 | {
247 | if (add) { hdr->len++; hdr->keys[i] = key; return (ptrdiff_t)i; }
248 | return (ptrdiff_t)-1;
249 | }
250 | }
251 | }
252 |
253 | #ifdef _MSC_VER
254 | #pragma warning(pop)
255 | #endif
256 |
257 | #endif
258 |
--------------------------------------------------------------------------------
/tinyhashmap64.h:
--------------------------------------------------------------------------------
1 | /* TinyHashMap64 - simple hash map - public domain - Bernhard Schelling 2020
2 | https://github.com/schellingb/c-data-structures
3 | no warranty implied; use at your own risk
4 |
5 | This file implements a hash map with 64-bit keys.
6 | Based on the implementation from the public domain Bitwise project
7 | by Per Vognsen - https://github.com/pervognsen/bitwise
8 |
9 | It's a super simple type safe hash map for C with no need
10 | to predeclare any type or anything.
11 | Will always allocate memory for twice the amount of max elements
12 | so larger structs should be stored as pointers or indices to an array.
13 | Can be used in C++ with POD types (without any constructor/destructor).
14 |
15 | Be careful not to supply modifying statements to the macro arguments.
16 | Something like HMAP64_FIT(map, i++); would have unintended results.
17 |
18 | Sample usage:
19 |
20 | -- Set 2 elements with string keys and mytype_t values:
21 | mytype_t* map = NULL;
22 | HMAP64_SET_STR(map, "foo", foo_element);
23 | HMAP64_SET_STR(map, "bar", bar_element);
24 | -- now HMAP64_LEN(map) == 2, HMAP64_GET_STR(map, "foo") == foo_element
25 |
26 | -- Check if keys exist:
27 | bool has_foo = HMAP64_HAS_STR(map, "foo");
28 | bool has_baz = HMAP64_HAS_STR(map, "baz");
29 | -- now has_foo == true, has_baz == false
30 |
31 | -- Removing a key:
32 | bool removed = HMAP64_DEL_STR(map, "bar");
33 | bool removed_again = HMAP64_DEL_STR(map, "bar");
34 | -- now HMAP64_LEN(map) == 1, removed == true, removed_again == false
35 |
36 | -- Add/modify via pointer:
37 | mytype_t* p_elem = HMAP64_PTR_STR(map, "qux");
38 | p_elem->a = 123;
39 | -- New keys initially have memory uninitialized
40 | -- Pointers can get invalidated when a key is added/removed
41 |
42 | -- Looking up the index for a given key:
43 | ptrdiff_t idx_foo = HMAP64_IDX_STR(map, "foo");
44 | ptrdiff_t idx_invalid = HMAP64_IDX_STR(map, "invalid");
45 | -- now idx_foo >= 0, idx_invalid == -1, map[idx_foo] == foo_element
46 | -- Indices can change when a key is added/removed
47 |
48 | -- Clear all elements (keep memory allocated):
49 | HMAP64_CLEAR(map);
50 | -- now HMAP64_LEN(map) == 0, HMAP64_CAP(map) == 16
51 |
52 | -- Reserve memory for at least N elements:
53 | HMAP64_FIT(map, 30);
54 | -- now HMAP64_LEN(map) == 0, HMAP64_CAP(map) == 64
55 |
56 | -- Add elements with custom hash keys:
57 | HMAP64_SET(map, my_uint64_hash(key1), some_element);
58 | HMAP64_SET(map, my_uint64_hash(key2), other_element);
59 | -- now HMAP64_LEN(map) == 2, _GET/_HAS/_DEL/_PTR/_IDX also exist
60 |
61 | -- Iterate elements (random order, order can change on insert):
62 | for (size_t i = 0, cap = HMAP64_CAP(map); i != cap, i++)
63 | if (HMAP64_KEY(map, i))
64 | ------ here map[i] is the value of key HMAP64_KEY(map, i)
65 |
66 | -- Set a custom null value (is zeroed by default):
67 | HMAP64_SETNULLVAL(map, map_null);
68 | -- now HMAP64_GET_STR(map, "invalid") == map_null
69 |
70 | -- Free allocated memory:
71 | HMAP64_FREE(map);
72 | -- now map == NULL, HMAP64_LEN(map) == 0, HMAP64_CAP(map) == 0
73 |
74 | -- To handle running out of memory:
75 | bool ran_out_of_memory = !HMAP64_TRYFIT(map, 1000);
76 | -- before setting an element (with SET, PTR or NULLVAL).
77 | -- When out of memory, map will stay unmodified.
78 |
79 | PUBLIC DOMAIN (UNLICENSE)
80 |
81 | This is free and unencumbered software released into the public domain.
82 |
83 | Anyone is free to copy, modify, publish, use, compile, sell, or
84 | distribute this software, either in source code form or as a compiled
85 | binary, for any purpose, commercial or non-commercial, and by any
86 | means.
87 |
88 | In jurisdictions that recognize copyright laws, the author or authors
89 | of this software dedicate any and all copyright interest in the
90 | software to the public domain. We make this dedication for the benefit
91 | of the public at large and to the detriment of our heirs and
92 | successors. We intend this dedication to be an overt act of
93 | relinquishment in perpetuity of all present and future rights to this
94 | software under copyright law.
95 |
96 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
97 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
98 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
99 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
100 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
101 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
102 | OTHER DEALINGS IN THE SOFTWARE.
103 |
104 | For more information, please refer to
105 | */
106 |
107 | #ifndef TINYHASHMAP64_H
108 | #define TINYHASHMAP64_H
109 |
110 | #include /* for malloc, realloc */
111 | #include /* for memcpy, memset */
112 | #include /* for ptrdiff_t, size_t */
113 | #if defined(_MSC_VER) && (_MSC_VER < 1600)
114 | typedef unsigned __int64 uint64_t;
115 | #else
116 | #include /* for uint64_t */
117 | #endif
118 |
119 | #define HMAP64_LEN(b) ((b) ? HMAP64__HDR(b)->len : 0)
120 | #define HMAP64_MAX(b) ((b) ? HMAP64__HDR(b)->maxlen : 0)
121 | #define HMAP64_CAP(b) ((b) ? HMAP64__HDR(b)->maxlen + 1 : 0)
122 | #define HMAP64_KEY(b, idx) (HMAP64__HDR(b)->keys[idx])
123 | #define HMAP64_SETNULLVAL(b, val) (HMAP64__FIT1(b), b[-1] = (val))
124 | #define HMAP64_CLEAR(b) ((b) ? (memset(HMAP64__HDR(b)->keys, 0, HMAP64_CAP(b) * sizeof(uint64_t)), HMAP64__HDR(b)->len = 0) : 0)
125 | #define HMAP64_FREE(b) ((b) ? (free(HMAP64__HDR(b)->keys), free(HMAP64__HDR(b)), (b) = NULL) : 0)
126 | #define HMAP64_FIT(b, n) ((!(n) || ((b) && (size_t)(n) * 2 <= HMAP64_MAX(b))) ? 0 : HMAP64__GROW(b, n))
127 | #define HMAP64_TRYFIT(b, n) (HMAP64_FIT((b), (n)), (!(n) || ((b) && (size_t)(n) * 2 <= HMAP64_MAX(b))))
128 |
129 | #define HMAP64_SET(b, key, val) (HMAP64__FIT1(b), b[hmap64__idx(HMAP64__HDR(b), (key), 1, 0)] = (val))
130 | #define HMAP64_GET(b, key) (HMAP64__FIT1(b), b[hmap64__idx(HMAP64__HDR(b), (key), 0, 0)])
131 | #define HMAP64_HAS(b, key) ((b) ? hmap64__idx(HMAP64__HDR(b), (key), 0, 0) != -1 : 0)
132 | #define HMAP64_DEL(b, key) ((b) ? hmap64__idx(HMAP64__HDR(b), (key), 0, sizeof(*(b))) != -1 : 0)
133 | #define HMAP64_PTR(b, key) (HMAP64__FIT1(b), &b[hmap64__idx(HMAP64__HDR(b), (key), 1, 0)])
134 | #define HMAP64_IDX(b, key) ((b) ? hmap64__idx(HMAP64__HDR(b), (key), 0, 0) : -1)
135 |
136 | #ifdef __GNUC__
137 | #define HMAP__UNUSED __attribute__((__unused__))
138 | #else
139 | #define HMAP__UNUSED
140 | #endif
141 |
142 | #ifdef _MSC_VER
143 | #pragma warning(push)
144 | #pragma warning(disable:4505) //unreferenced local function has been removed
145 | #endif
146 |
147 | #define HMAP64_SET_STR(b, string_key, val) HMAP64_SET(b, hash64_string(string_key), val)
148 | #define HMAP64_GET_STR(b, string_key) HMAP64_GET(b, hash64_string(string_key))
149 | #define HMAP64_HAS_STR(b, string_key) HMAP64_HAS(b, hash64_string(string_key))
150 | #define HMAP64_DEL_STR(b, string_key) HMAP64_DEL(b, hash64_string(string_key))
151 | #define HMAP64_PTR_STR(b, string_key) HMAP64_PTR(b, hash64_string(string_key))
152 | #define HMAP64_IDX_STR(b, string_key) HMAP64_IDX(b, hash64_string(string_key))
153 |
154 | HMAP__UNUSED static uint64_t hash64_string(const char* str)
155 | {
156 | unsigned char c;
157 | uint64_t hash = (uint64_t)0xcbf29ce484222325;
158 | while ((c = (unsigned char)*(str++)) != '\0')
159 | hash = ((hash * (uint64_t)0x100000001b3) ^ (uint64_t)c);
160 | return (hash ? hash : 1);
161 | }
162 |
163 | struct hmap64__hdr { size_t len, maxlen; uint64_t *keys; };
164 | #define HMAP64__HDR(b) (((struct hmap64__hdr *)&(b)[-1])-1)
165 | #define HMAP64__GROW(b, n) (*(void**)(&(b)) = hmap64__grow(HMAP64__HDR(b), (void*)(b), sizeof(*(b)), (size_t)(n)))
166 | #define HMAP64__FIT1(b) ((b) && HMAP64_LEN(b) * 2 <= HMAP64_MAX(b) ? 0 : HMAP64__GROW(b, 0))
167 |
168 | HMAP__UNUSED static void* hmap64__grow(struct hmap64__hdr *old_hdr, void* old_ptr, size_t elem_size, size_t res)
169 | {
170 | struct hmap64__hdr *new_hdr;
171 | char *new_vals;
172 | size_t new_max = (old_ptr ? old_hdr->maxlen * 2 + 1 : 16);
173 | while (new_max && new_max / 2 <= res)
174 | if (!(new_max = new_max * 2 + 1))
175 | return old_ptr; /* overflow */
176 |
177 | new_hdr = (struct hmap64__hdr *)malloc(sizeof(struct hmap64__hdr) + (new_max + 2) * elem_size);
178 | if (!new_hdr)
179 | return old_ptr; /* out of memory */
180 |
181 | new_hdr->maxlen = new_max;
182 | new_hdr->keys = (uint64_t *)calloc(new_max + 1, sizeof(uint64_t));
183 | if (!new_hdr->keys)
184 | return (free(new_hdr), old_ptr); /* out of memory */
185 |
186 | new_vals = ((char*)(new_hdr + 1)) + elem_size;
187 | if (old_ptr)
188 | {
189 | size_t i;
190 | char* old_vals = ((char*)(old_hdr + 1)) + elem_size;
191 | for (i = 0; i <= old_hdr->maxlen; i++)
192 | {
193 | uint64_t key, j;
194 | if (!old_hdr->keys[i])
195 | continue;
196 | for (key = old_hdr->keys[i], j = key;; j++)
197 | {
198 | if (!new_hdr->keys[j &= new_hdr->maxlen])
199 | {
200 | new_hdr->keys[j] = key;
201 | memcpy(new_vals + j * elem_size, old_vals + i * elem_size, elem_size);
202 | break;
203 | }
204 | }
205 | }
206 | memcpy(new_vals - elem_size, old_vals - elem_size, elem_size);
207 | new_hdr->len = old_hdr->len;
208 | free(old_hdr->keys);
209 | free(old_hdr);
210 | }
211 | else
212 | {
213 | memset(new_vals - elem_size, 0, elem_size);
214 | new_hdr->len = 0;
215 | }
216 | return new_vals;
217 | }
218 |
219 | HMAP__UNUSED static ptrdiff_t hmap64__idx(struct hmap64__hdr* hdr, uint64_t key, int add, size_t del)
220 | {
221 | uint64_t i;
222 |
223 | if (!key)
224 | return (ptrdiff_t)-1;
225 |
226 | for (i = key;; i++)
227 | {
228 | if (hdr->keys[i &= hdr->maxlen] == key)
229 | {
230 | if (del)
231 | {
232 | hdr->len--;
233 | hdr->keys[i] = 0;
234 | while ((key = hdr->keys[i = (i + 1) & hdr->maxlen]) != 0)
235 | {
236 | if ((key = (uint64_t)hmap64__idx(hdr, key, 1, 0)) == i) continue;
237 | hdr->len--;
238 | hdr->keys[i] = 0;
239 | memcpy(((char*)(hdr + 1)) + (key + 1) * del,
240 | ((char*)(hdr + 1)) + (i + 1) * del, del);
241 | }
242 | }
243 | return (ptrdiff_t)i;
244 | }
245 | if (!hdr->keys[i])
246 | {
247 | if (add) { hdr->len++; hdr->keys[i] = key; return (ptrdiff_t)i; }
248 | return (ptrdiff_t)-1;
249 | }
250 | }
251 | }
252 |
253 | #ifdef _MSC_VER
254 | #pragma warning(pop)
255 | #endif
256 |
257 | #endif
258 |
--------------------------------------------------------------------------------