├── 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 | --------------------------------------------------------------------------------