├── package.json ├── LICENSE ├── src ├── map.h └── map.c └── README.md /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "map", 3 | "version": "0.1.0", 4 | "repo": "rxi/map", 5 | "description": "Type-safe generic hash map", 6 | "keywords": ["hashmap", "map", "table", "hashtable", "dict", "dictionary"], 7 | "license": "MIT", 8 | "src": ["src/map.c", "src/map.h"] 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 rxi 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #ifndef MAP_H 9 | #define MAP_H 10 | 11 | #include 12 | 13 | #define MAP_VERSION "0.1.0" 14 | 15 | struct map_node_t; 16 | typedef struct map_node_t map_node_t; 17 | 18 | typedef struct { 19 | map_node_t **buckets; 20 | unsigned nbuckets, nnodes; 21 | } map_base_t; 22 | 23 | typedef struct { 24 | unsigned bucketidx; 25 | map_node_t *node; 26 | } map_iter_t; 27 | 28 | 29 | #define map_t(T)\ 30 | struct { map_base_t base; T *ref; T tmp; } 31 | 32 | 33 | #define map_init(m)\ 34 | memset(m, 0, sizeof(*(m))) 35 | 36 | 37 | #define map_deinit(m)\ 38 | map_deinit_(&(m)->base) 39 | 40 | 41 | #define map_get(m, key)\ 42 | ( (m)->ref = map_get_(&(m)->base, key) ) 43 | 44 | 45 | #define map_set(m, key, value)\ 46 | ( (m)->tmp = (value),\ 47 | map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) 48 | 49 | 50 | #define map_remove(m, key)\ 51 | map_remove_(&(m)->base, key) 52 | 53 | 54 | #define map_iter(m)\ 55 | map_iter_() 56 | 57 | 58 | #define map_next(m, iter)\ 59 | map_next_(&(m)->base, iter) 60 | 61 | 62 | void map_deinit_(map_base_t *m); 63 | void *map_get_(map_base_t *m, const char *key); 64 | int map_set_(map_base_t *m, const char *key, void *value, int vsize); 65 | void map_remove_(map_base_t *m, const char *key); 66 | map_iter_t map_iter_(void); 67 | const char *map_next_(map_base_t *m, map_iter_t *iter); 68 | 69 | 70 | typedef map_t(void*) map_void_t; 71 | typedef map_t(char*) map_str_t; 72 | typedef map_t(int) map_int_t; 73 | typedef map_t(char) map_char_t; 74 | typedef map_t(float) map_float_t; 75 | typedef map_t(double) map_double_t; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # map 2 | A type-safe generic hashmap implementation for C. 3 | 4 | ## Installation 5 | The [map.c](src/map.c?raw=1) and [map.h](src/map.h?raw=1) files can be dropped 6 | into an existing C project and compiled along with it. 7 | 8 | 9 | ## Usage 10 | Before using a map it should first be initialised using the `map_init()` 11 | function. 12 | ```c 13 | map_int_t m; 14 | map_init(&m); 15 | ``` 16 | 17 | Values can added to a map using the `map_set()` function. 18 | ```c 19 | map_set(&m, "testkey", 123); 20 | ``` 21 | 22 | To retrieve a value from a map, the `map_get()` function can be used. 23 | `map_get()` will return a pointer to the key's value, or `NULL` if no mapping 24 | for that key exists. 25 | ```c 26 | int *val = map_get(&m, "testkey"); 27 | if (val) { 28 | printf("value: %d\n", *val); 29 | } else { 30 | printf("value not found\n"); 31 | } 32 | ``` 33 | 34 | When you are done with a map the `map_deinit()` function should be called on 35 | it. This will free any memory the map allocated during use. 36 | ```c 37 | map_deinit(&m); 38 | ``` 39 | 40 | 41 | ## Types 42 | map.h provides the following predefined map types: 43 | 44 | Contained Type | Type name 45 | ----------------|---------------------------------- 46 | void* | map_void_t 47 | char* | map_str_t 48 | int | map_int_t 49 | char | map_char_t 50 | float | map_float_t 51 | double | map_double_t 52 | 53 | To define a new map type the `map_t()` macro should be used: 54 | ```c 55 | /* Creates the type uint_map_t for storing unsigned ints */ 56 | typedef map_t(unsigned int) uint_map_t; 57 | ``` 58 | 59 | ## Functions 60 | All map functions are macro functions. The parameter `m` in each function 61 | should be a pointer to the map struct which the operation is to be performed 62 | on. The `key` parameter should always be a string value. 63 | 64 | ### map\_t(T) 65 | Creates a map struct for containing values of type `T`. 66 | ```c 67 | /* Typedefs the struct `fp_map_t` as a container for type FILE* */ 68 | typedef map_t(FILE*) fp_map_t; 69 | ``` 70 | 71 | ### map\_init(m) 72 | Initialises the map, this must be called before the map can be used. 73 | 74 | ### map\_deinit(m) 75 | Deinitialises the map, freeing the memory the map allocated during use; 76 | this should be called when we're finished with a map. 77 | 78 | ### map\_get(m, key) 79 | Returns a pointer to the value of the given `key`. If no mapping for the `key` 80 | exists then `NULL` will be returned. 81 | 82 | ### map\_set(m, key, value) 83 | Sets the given `key` to the given `value`. Returns `0` on success, otherwise 84 | `-1` is returned and the map remains unchanged. 85 | 86 | ### map\_remove(m, key) 87 | Removes the mapping of the given `key` from the map. If the `key` does not 88 | exist in the map then the function has no effect. 89 | 90 | ### map\_iter(m) 91 | Returns a `map_iter_t` which can be used with `map_next()` to iterate all the 92 | keys in the map. 93 | 94 | ### map\_next(m, iter) 95 | Uses the `map_iter_t` returned by `map_iter()` to iterate all the keys in the 96 | map. `map_next()` returns a key with each call and returns `NULL` when there 97 | are no more keys. 98 | ```c 99 | const char *key; 100 | map_iter_t iter = map_iter(&m); 101 | 102 | while ((key = map_next(&m, &iter))) { 103 | printf("%s -> %d", key, *map_get(&m, key)); 104 | } 105 | ``` 106 | 107 | ## License 108 | This library is free software; you can redistribute it and/or modify it under 109 | the terms of the MIT license. See [LICENSE](LICENSE) for details. 110 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | #include 9 | #include 10 | #include "map.h" 11 | 12 | struct map_node_t { 13 | unsigned hash; 14 | void *value; 15 | map_node_t *next; 16 | /* char key[]; */ 17 | /* char value[]; */ 18 | }; 19 | 20 | 21 | static unsigned map_hash(const char *str) { 22 | unsigned hash = 5381; 23 | while (*str) { 24 | hash = ((hash << 5) + hash) ^ *str++; 25 | } 26 | return hash; 27 | } 28 | 29 | 30 | static map_node_t *map_newnode(const char *key, void *value, int vsize) { 31 | map_node_t *node; 32 | int ksize = strlen(key) + 1; 33 | int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*)); 34 | node = malloc(sizeof(*node) + voffset + vsize); 35 | if (!node) return NULL; 36 | memcpy(node + 1, key, ksize); 37 | node->hash = map_hash(key); 38 | node->value = ((char*) (node + 1)) + voffset; 39 | memcpy(node->value, value, vsize); 40 | return node; 41 | } 42 | 43 | 44 | static int map_bucketidx(map_base_t *m, unsigned hash) { 45 | /* If the implementation is changed to allow a non-power-of-2 bucket count, 46 | * the line below should be changed to use mod instead of AND */ 47 | return hash & (m->nbuckets - 1); 48 | } 49 | 50 | 51 | static void map_addnode(map_base_t *m, map_node_t *node) { 52 | int n = map_bucketidx(m, node->hash); 53 | node->next = m->buckets[n]; 54 | m->buckets[n] = node; 55 | } 56 | 57 | 58 | static int map_resize(map_base_t *m, int nbuckets) { 59 | map_node_t *nodes, *node, *next; 60 | map_node_t **buckets; 61 | int i; 62 | /* Chain all nodes together */ 63 | nodes = NULL; 64 | i = m->nbuckets; 65 | while (i--) { 66 | node = (m->buckets)[i]; 67 | while (node) { 68 | next = node->next; 69 | node->next = nodes; 70 | nodes = node; 71 | node = next; 72 | } 73 | } 74 | /* Reset buckets */ 75 | buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); 76 | if (buckets != NULL) { 77 | m->buckets = buckets; 78 | m->nbuckets = nbuckets; 79 | } 80 | if (m->buckets) { 81 | memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); 82 | /* Re-add nodes to buckets */ 83 | node = nodes; 84 | while (node) { 85 | next = node->next; 86 | map_addnode(m, node); 87 | node = next; 88 | } 89 | } 90 | /* Return error code if realloc() failed */ 91 | return (buckets == NULL) ? -1 : 0; 92 | } 93 | 94 | 95 | static map_node_t **map_getref(map_base_t *m, const char *key) { 96 | unsigned hash = map_hash(key); 97 | map_node_t **next; 98 | if (m->nbuckets > 0) { 99 | next = &m->buckets[map_bucketidx(m, hash)]; 100 | while (*next) { 101 | if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) { 102 | return next; 103 | } 104 | next = &(*next)->next; 105 | } 106 | } 107 | return NULL; 108 | } 109 | 110 | 111 | void map_deinit_(map_base_t *m) { 112 | map_node_t *next, *node; 113 | int i; 114 | i = m->nbuckets; 115 | while (i--) { 116 | node = m->buckets[i]; 117 | while (node) { 118 | next = node->next; 119 | free(node); 120 | node = next; 121 | } 122 | } 123 | free(m->buckets); 124 | } 125 | 126 | 127 | void *map_get_(map_base_t *m, const char *key) { 128 | map_node_t **next = map_getref(m, key); 129 | return next ? (*next)->value : NULL; 130 | } 131 | 132 | 133 | int map_set_(map_base_t *m, const char *key, void *value, int vsize) { 134 | int n, err; 135 | map_node_t **next, *node; 136 | /* Find & replace existing node */ 137 | next = map_getref(m, key); 138 | if (next) { 139 | memcpy((*next)->value, value, vsize); 140 | return 0; 141 | } 142 | /* Add new node */ 143 | node = map_newnode(key, value, vsize); 144 | if (node == NULL) goto fail; 145 | if (m->nnodes >= m->nbuckets) { 146 | n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; 147 | err = map_resize(m, n); 148 | if (err) goto fail; 149 | } 150 | map_addnode(m, node); 151 | m->nnodes++; 152 | return 0; 153 | fail: 154 | if (node) free(node); 155 | return -1; 156 | } 157 | 158 | 159 | void map_remove_(map_base_t *m, const char *key) { 160 | map_node_t *node; 161 | map_node_t **next = map_getref(m, key); 162 | if (next) { 163 | node = *next; 164 | *next = (*next)->next; 165 | free(node); 166 | m->nnodes--; 167 | } 168 | } 169 | 170 | 171 | map_iter_t map_iter_(void) { 172 | map_iter_t iter; 173 | iter.bucketidx = -1; 174 | iter.node = NULL; 175 | return iter; 176 | } 177 | 178 | 179 | const char *map_next_(map_base_t *m, map_iter_t *iter) { 180 | if (iter->node) { 181 | iter->node = iter->node->next; 182 | if (iter->node == NULL) goto nextBucket; 183 | } else { 184 | nextBucket: 185 | do { 186 | if (++iter->bucketidx >= m->nbuckets) { 187 | return NULL; 188 | } 189 | iter->node = m->buckets[iter->bucketidx]; 190 | } while (iter->node == NULL); 191 | } 192 | return (char*) (iter->node + 1); 193 | } 194 | --------------------------------------------------------------------------------