├── LICENSE ├── README.md ├── chained_hash.c ├── chained_hash.h ├── chained_hashv.c ├── chained_hashv.h ├── vect.c └── vect.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Andrei Ciobanu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chained-hash-table-c 2 | 3 | A chained hash table implemented in C. 4 | 5 | Code associated with the following article: 6 | https://www.andreinc.net/2021/10/02/implementing-hash-tables-in-c-part-1 7 | 8 | 9 | -------------------------------------------------------------------------------- /chained_hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "chained_hash.h" 8 | 9 | ch_hash *ch_hash_new(ch_key_ops k_ops, ch_val_ops v_ops) { 10 | ch_hash *hash; 11 | hash = malloc(sizeof(*hash)); 12 | 13 | if(NULL == hash) { 14 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | hash->size = 0; 19 | hash->capacity = CH_HASH_CAPACITY_INIT; 20 | hash->key_ops = k_ops; 21 | hash->val_ops = v_ops; 22 | 23 | hash->buckets = malloc(hash->capacity * sizeof(*(hash->buckets))); 24 | if (NULL == hash->buckets) { 25 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 26 | exit(EXIT_FAILURE); 27 | } 28 | for(int i = 0; i < hash->capacity; i++) { 29 | // Initially all the buckets are NULL 30 | // Memory will be allocated for them when needed 31 | hash->buckets[i] = NULL; 32 | } 33 | 34 | return hash; 35 | } 36 | 37 | void ch_hash_free(ch_hash *hash) { 38 | 39 | ch_node *crt; 40 | ch_node *next; 41 | 42 | for(int i = 0; i < hash->capacity; ++i) { 43 | // Free memory for each bucket 44 | crt = hash->buckets[i]; 45 | while(NULL!=crt) { 46 | next = crt->next; 47 | 48 | // Free memory for key and value 49 | hash->key_ops.free(crt->key, hash->key_ops.arg); 50 | hash->val_ops.free(crt->val, hash->val_ops.arg); 51 | 52 | // Free the node 53 | free(crt); 54 | crt = next; 55 | } 56 | } 57 | // Free the buckets and the hash structure itself 58 | free(hash->buckets); 59 | free(hash); 60 | } 61 | 62 | static ch_node* ch_hash_get_node(ch_hash *hash, const void *key) { 63 | 64 | ch_node *result = NULL; 65 | ch_node *crt = NULL; 66 | uint32_t h; 67 | size_t bucket_idx; 68 | 69 | h = hash->key_ops.hash(key, hash->key_ops.arg); 70 | bucket_idx = h % hash->capacity; 71 | crt = hash->buckets[bucket_idx]; 72 | 73 | while(NULL!=crt) { 74 | // Iterated through the linked list to determine if the element is present 75 | if (crt->hash == h && hash->key_ops.eq(crt->key, key, hash->val_ops.arg)) { 76 | result = crt; 77 | break; 78 | } 79 | crt = crt->next; 80 | } 81 | 82 | return result; 83 | } 84 | 85 | void* ch_hash_get(ch_hash *hash, const void *k) { 86 | ch_node *result = NULL; 87 | if (NULL!=(result=ch_hash_get_node(hash, k))) { 88 | return result->val; 89 | } 90 | return NULL; 91 | } 92 | 93 | static void ch_hash_grow(ch_hash *hash) { 94 | 95 | ch_node **new_buckets; 96 | ch_node *crt; 97 | size_t new_capacity; 98 | size_t new_idx; 99 | 100 | new_capacity = hash->capacity * CH_HASH_CAPACITY_MULT; 101 | new_buckets = malloc(sizeof(*new_buckets) * new_capacity); 102 | if (NULL==new_buckets) { 103 | fprintf(stderr, "Cannot resize buckets array. Hash table won't be resized.\n"); 104 | return; 105 | } 106 | for(int i = 0; i < new_capacity; ++i) { 107 | new_buckets[i] = NULL; 108 | } 109 | 110 | // Rehash 111 | // For each bucket 112 | for(int i = 0; i < hash->capacity; i++) { 113 | // For each linked list 114 | crt = hash->buckets[i]; 115 | while(NULL!=crt) { 116 | // Finding the new bucket 117 | new_idx = crt->hash % new_capacity; 118 | ch_node *cur = crt; 119 | crt = crt->next; 120 | cur->next = new_buckets[new_idx]; 121 | new_buckets[new_idx] = cur; 122 | } 123 | } 124 | 125 | hash->capacity = new_capacity; 126 | 127 | // Free the old buckets 128 | free(hash->buckets); 129 | 130 | // Update with the new buckets 131 | hash->buckets = new_buckets; 132 | } 133 | 134 | void ch_hash_put(ch_hash *hash, const void *k, const void *v) { 135 | ch_node *crt; 136 | size_t bucket_idx; 137 | crt = ch_hash_get_node(hash, k); 138 | if (crt) { 139 | // Key already exists 140 | // We need to update the value 141 | hash->val_ops.free(crt->val, hash->val_ops.arg); 142 | crt->val = v ? hash->val_ops.cp(v, hash->val_ops.arg) : 0; 143 | } 144 | else { 145 | // Key doesn't exist 146 | // - We create a node 147 | // - We add a node to the correspoding bucket 148 | crt = malloc(sizeof(*crt)); 149 | if (NULL == crt) { 150 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 151 | exit(EXIT_FAILURE); 152 | } 153 | crt->hash = hash->key_ops.hash(k, hash->key_ops.arg); 154 | crt->key = hash->key_ops.cp(k, hash->key_ops.arg); 155 | crt->val = hash->val_ops.cp(v, hash->val_ops.arg); 156 | 157 | bucket_idx = crt->hash % hash->capacity; 158 | crt->next = hash->buckets[bucket_idx]; 159 | hash->buckets[bucket_idx] = crt; 160 | 161 | // Element has been added succesfuly 162 | hash->size++; 163 | 164 | // Grow if needed 165 | if (hash->size > hash->capacity * CH_HASH_GROWTH) { 166 | ch_hash_grow(hash); 167 | } 168 | } 169 | } 170 | 171 | bool ch_hash_contains(ch_hash *hash, const void *k) { 172 | return ch_hash_get_node(hash, k) ? true : false; 173 | } 174 | 175 | static uint32_t ch_node_numcol(ch_node* node) { 176 | uint32_t result = 0; 177 | if (node) { 178 | while(node->next!=NULL) { 179 | result++; 180 | node = node->next; 181 | } 182 | } 183 | return result; 184 | } 185 | 186 | uint32_t ch_hash_numcol(ch_hash *hash) { 187 | uint32_t result = 0; 188 | for(int i = 0; i < hash->capacity; ++i) { 189 | result += ch_node_numcol(hash->buckets[i]); 190 | } 191 | return result; 192 | } 193 | 194 | void ch_hash_print(ch_hash *hash, void (*print_key)(const void *k), void (*print_val)(const void *v)) { 195 | 196 | ch_node *crt; 197 | 198 | printf("Hash Capacity: %lu\n", hash->capacity); 199 | printf("Hash Size: %lu\n", hash->size); 200 | 201 | printf("Hash Buckets:\n"); 202 | for(int i = 0; i < hash->capacity; i++) { 203 | crt = hash->buckets[i]; 204 | printf("\tbucket[%d]:\n", i); 205 | while(NULL!=crt) { 206 | printf("\t\thash=%" PRIu32 ", key=", crt->hash); 207 | print_key(crt->key); 208 | printf(", value="); 209 | print_val(crt->val); 210 | printf("\n"); 211 | crt=crt->next; 212 | } 213 | } 214 | } 215 | 216 | // String operations 217 | 218 | static uint32_t ch_hash_fmix32(uint32_t h) { 219 | h ^= h >> 16; 220 | h *= 0x3243f6a9U; 221 | h ^= h >> 16; 222 | return h; 223 | } 224 | 225 | uint32_t ch_string_hash(const void *data, void *arg) { 226 | 227 | //djb2 228 | uint32_t hash = (const uint32_t) 5381; 229 | const char *str = (const char*) data; 230 | char c; 231 | while((c=*str++)) { 232 | hash = ((hash << 5) + hash) + c; 233 | } 234 | 235 | return ch_hash_fmix32(hash); 236 | } 237 | 238 | 239 | void* ch_string_cp(const void *data, void *arg) { 240 | const char *input = (const char*) data; 241 | size_t input_length = strlen(input) + 1; 242 | char *result; 243 | result = malloc(sizeof(*result) * input_length); 244 | if (NULL==result) { 245 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 246 | exit(EXIT_FAILURE); 247 | } 248 | strcpy(result, input); 249 | return result; 250 | } 251 | 252 | bool ch_string_eq(const void *data1, const void *data2, void *arg) { 253 | const char *str1 = (const char*) data1; 254 | const char *str2 = (const char*) data2; 255 | return !(strcmp(str1, str2)) ? true : false; 256 | } 257 | 258 | void ch_string_free(void *data, void *arg) { 259 | free(data); 260 | } 261 | 262 | void ch_string_print(const void *data) { 263 | printf("%s", (const char*) data); 264 | } 265 | 266 | ch_key_ops ch_key_ops_string = { ch_string_hash, ch_string_cp, ch_string_free, ch_string_eq, NULL}; 267 | ch_val_ops ch_val_ops_string = { ch_string_cp, ch_string_free, ch_string_eq, NULL}; -------------------------------------------------------------------------------- /chained_hash.h: -------------------------------------------------------------------------------- 1 | #define CH_HASH_CAPACITY_INIT (32) 2 | #define CH_HASH_CAPACITY_MULT (2) 3 | #define CH_HASH_GROWTH (1) 4 | 5 | typedef struct ch_key_ops_s { 6 | uint32_t (*hash)(const void *data, void *arg); 7 | void* (*cp)(const void *data, void *arg); 8 | void (*free)(void *data, void *arg); 9 | bool (*eq)(const void *data1, const void *data2, void *arg); 10 | void *arg; 11 | } ch_key_ops; 12 | 13 | typedef struct ch_val_ops_s { 14 | void* (*cp)(const void *data, void *arg); 15 | void (*free)(void *data, void *arg); 16 | bool (*eq)(const void *data1, const void *data2, void *arg); 17 | void *arg; 18 | } ch_val_ops; 19 | 20 | typedef struct ch_node_s { 21 | uint32_t hash; 22 | void *key; 23 | void *val; 24 | struct ch_node_s *next; 25 | } ch_node; 26 | 27 | typedef struct ch_hash_s { 28 | size_t capacity; 29 | size_t size; 30 | ch_node **buckets; 31 | ch_key_ops key_ops; 32 | ch_val_ops val_ops; 33 | } ch_hash; 34 | 35 | 36 | // Creates a new hash table 37 | ch_hash *ch_hash_new(ch_key_ops k_ops, ch_val_ops v_ops); 38 | 39 | // Free the memory associated with the hash (and all of its contents) 40 | void ch_hash_free(ch_hash *hash); 41 | 42 | // Gets the value coresponding to a key 43 | // If the key is not found returns NULL 44 | void* ch_hash_get(ch_hash *hash, const void *k); 45 | 46 | // Checks if a key exists or not in the hash table 47 | bool ch_hash_contains(ch_hash *hash, const void *k); 48 | 49 | // Adds a pair to the table 50 | void ch_hash_put(ch_hash *hash, const void *k, const void *v); 51 | 52 | // Prints the contents of the hash table 53 | void ch_hash_print(ch_hash *hash, void (*print_key)(const void *k), void (*print_val)(const void *v)); 54 | 55 | // Get the total number of collisions 56 | uint32_t ch_hash_numcol(ch_hash *hash); 57 | 58 | // String operations 59 | 60 | uint32_t ch_string_hash(const void *data, void *arg); 61 | void* ch_string_cp(const void *data, void *arg); 62 | bool ch_string_eq(const void *data1, const void *data2, void *arg); 63 | void ch_string_free(void *data, void *arg); 64 | void ch_string_print(const void *data); 65 | 66 | extern ch_key_ops ch_key_ops_string; 67 | extern ch_val_ops ch_val_ops_string; -------------------------------------------------------------------------------- /chained_hashv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "chained_hashv.h" 8 | 9 | ch_hashv *ch_hashv_new(ch_key_ops k_ops, ch_val_ops v_ops) { 10 | 11 | ch_hashv *hash; 12 | hash = malloc(sizeof(*hash)); 13 | 14 | if(NULL == hash) { 15 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | hash->size = 0; 20 | hash->capacity = CH_HASH_CAPACITY_INIT; 21 | hash->key_ops = k_ops; 22 | hash->val_ops = v_ops; 23 | hash->buckets = malloc(hash->capacity * sizeof(*(hash->buckets))); 24 | 25 | if (NULL == hash->buckets) { 26 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 27 | exit(EXIT_FAILURE); 28 | } 29 | for(int i = 0; i < hash->capacity; i++) { 30 | // Initially all the buckets are NULL 31 | // Memory will be allocated for them when needed 32 | hash->buckets[i] = NULL; 33 | } 34 | 35 | return hash; 36 | } 37 | 38 | void ch_hash_free(ch_hashv *htable) { 39 | ch_vect *crt; 40 | ch_node *crt_el; 41 | for(int i = 0; i < htable->capacity; ++i) { 42 | // Free memory for each bucket 43 | crt = htable->buckets[i]; 44 | if (NULL!=crt) { 45 | for(int j = 0; j < crt->size; j++) { 46 | crt_el = crt->array[j]; 47 | htable->key_ops.free(crt_el->key, htable->key_ops.arg); 48 | htable->val_ops.free(crt_el->val, htable->val_ops.arg); 49 | free(crt_el); 50 | } 51 | } 52 | ch_vect_free(crt); 53 | } 54 | // Free the buckets and the hash structure itself 55 | free(htable->buckets); 56 | free(htable); 57 | } 58 | 59 | static ch_node* ch_hashv_get_node(ch_hashv *htable, const void *key) { 60 | 61 | ch_node *result = NULL; 62 | ch_node *crt_node = NULL; 63 | ch_vect *crt_bucket = NULL; 64 | 65 | uint32_t computed_hash; 66 | size_t bucket_idx; 67 | 68 | computed_hash = htable->key_ops.hash(key, htable->key_ops.arg); 69 | bucket_idx = computed_hash % htable->capacity; 70 | crt_bucket = htable->buckets[bucket_idx]; 71 | 72 | if (NULL!=crt_bucket) { 73 | for(int i = 0; i < crt_bucket->size; ++i) { 74 | crt_node = crt_bucket->array[i]; 75 | if (crt_node->hash == computed_hash) { 76 | if (htable->key_ops.eq(crt_node->key, key, htable->key_ops.arg)) { 77 | result = crt_node; 78 | break; 79 | } 80 | } 81 | } 82 | } 83 | return result; 84 | } 85 | 86 | void* ch_hashv_get(ch_hashv *htable, const void *k) { 87 | ch_node *result = ch_hashv_get_node(htable, k); 88 | 89 | if (NULL!=result) { 90 | return result->val; 91 | } 92 | 93 | return NULL; 94 | } 95 | 96 | static void ch_hash_grow(ch_hashv *htable) { 97 | 98 | ch_vect **new_buckets; 99 | ch_vect *crt_bucket; 100 | ch_node *crt_element; 101 | size_t new_capacity; 102 | size_t new_idx; 103 | 104 | new_capacity = htable->capacity * CH_HASH_CAPACITY_MULT; 105 | new_buckets = malloc(sizeof(*new_buckets) * new_capacity); 106 | 107 | if (NULL==new_buckets) { 108 | fprintf(stderr, "Cannot resize buckets array. Hash table won't be resized.\n"); 109 | return; 110 | } 111 | 112 | for(int i = 0; i < new_capacity; ++i) { 113 | new_buckets[i] = NULL; 114 | } 115 | 116 | // Rehash 117 | // For each (old) bucket 118 | for(int i = 0; i < htable->capacity; i++) { 119 | crt_bucket = htable->buckets[i]; 120 | // For each element from the old bucket 121 | if (NULL!=crt_bucket) { 122 | for(int j = 0; j < crt_bucket->size; j++) { 123 | // For each element from the 124 | crt_element = crt_bucket->array[j]; 125 | // Compute the new id for the new bucket 126 | new_idx = crt_element->hash % new_capacity; 127 | // If the bucket doesn't exist yet, we create yet 128 | if (NULL==new_buckets[new_idx]) { 129 | new_buckets[new_idx] = ch_vect_new_default(); 130 | } 131 | // Add the element to the corresponding bucket 132 | ch_vect_append(new_buckets[new_idx], crt_element); 133 | } 134 | } 135 | } 136 | 137 | htable->capacity = new_capacity; 138 | 139 | // Free the old buckets 140 | free(htable->buckets); 141 | 142 | // Update with the new buckets 143 | htable->buckets = new_buckets; 144 | } 145 | 146 | void ch_hashv_put(ch_hashv *htable, const void *k, const void *v) { 147 | 148 | ch_node *crt; 149 | size_t bucket_idx; 150 | 151 | crt = ch_hashv_get_node(htable, k); 152 | 153 | if (NULL!=crt) { 154 | // Key already exists 155 | // We need to update the value 156 | htable->val_ops.free(crt->val, htable->val_ops.arg); 157 | crt->val = v ? htable->val_ops.cp(v, htable->val_ops.arg) : 0; 158 | } 159 | 160 | else { 161 | // Key doesn't exist 162 | // - We create a node 163 | // - We add a node to the correspoding bucket 164 | crt = malloc(sizeof(*crt)); 165 | if (NULL == crt) { 166 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 167 | exit(EXIT_FAILURE); 168 | } 169 | crt->hash = htable->key_ops.hash(k, htable->key_ops.arg); 170 | crt->key = htable->key_ops.cp(k, htable->key_ops.arg); 171 | crt->val = htable->val_ops.cp(v, htable->val_ops.arg); 172 | 173 | bucket_idx = crt->hash % htable->capacity; 174 | if (NULL==htable->buckets[bucket_idx]) { 175 | htable->buckets[bucket_idx] = ch_vect_new_default(); 176 | } 177 | ch_vect_append(htable->buckets[bucket_idx], crt); 178 | 179 | // Element has been added successfully 180 | htable->size++; 181 | 182 | // Grow if needed 183 | if (htable->size > htable->capacity * CH_HASH_GROWTH) { 184 | ch_hash_grow(htable); 185 | } 186 | } 187 | } 188 | 189 | bool ch_hashv_contains(ch_hashv *htable, const void *k) { 190 | return ch_hashv_get_node(htable, k) ? true : false; 191 | } 192 | 193 | static uint32_t ch_node_numcol(ch_vect* bucket) { 194 | return (bucket->size == 0) ? 0 : bucket->size-1; 195 | } 196 | 197 | uint32_t ch_hashv_numcol(ch_hashv *htable) { 198 | uint32_t result = 0; 199 | for(int i = 0; i < htable->capacity; ++i) { 200 | result += ch_node_numcol(htable->buckets[i]); 201 | } 202 | return result; 203 | } 204 | 205 | void ch_hashv_print(ch_hashv *htable, void (*print_key)(const void *k), void (*print_val)(const void *v)) { 206 | 207 | ch_vect *crt_bucket; 208 | ch_node *crt_el; 209 | 210 | printf("Hash Capacity: %lu\n", htable->capacity); 211 | printf("Hash Size: %lu\n", htable->size); 212 | 213 | printf("Hash Buckets:\n"); 214 | for(int i = 0; i < htable->capacity; i++) { 215 | crt_bucket = htable->buckets[i]; 216 | printf("\tbucket[%d]:\n", i); 217 | if (NULL!=crt_bucket) { 218 | for(int j = 0; j < crt_bucket->size; j++) { 219 | crt_el = crt_bucket->array[j]; 220 | printf("\t\thash=%" PRIu32 ", key=", crt_el->hash); 221 | print_key(crt_el->key); 222 | printf(", value="); 223 | print_val(crt_el->val); 224 | printf("\n"); 225 | } 226 | } 227 | } 228 | } 229 | 230 | // String operations 231 | 232 | static uint32_t ch_hashv_fmix32(uint32_t h) { 233 | h ^= h >> 16; 234 | h *= 0x3243f6a9U; 235 | h ^= h >> 16; 236 | return h; 237 | } 238 | 239 | uint32_t ch_string_hash(const void *data, void *arg) { 240 | //djb2 241 | uint32_t hash = (const uint32_t) 5381; 242 | const char *str = (const char*) data; 243 | char c; 244 | while((c=*str++)) { 245 | hash = ((hash << 5) + hash) + c; 246 | } 247 | return ch_hashv_fmix32(hash); 248 | } 249 | 250 | 251 | void* ch_string_cp(const void *data, void *arg) { 252 | const char *input = (const char*) data; 253 | size_t input_length = strlen(input) + 1; 254 | char *result; 255 | result = malloc(sizeof(*result) * input_length); 256 | if (NULL==result) { 257 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 258 | exit(EXIT_FAILURE); 259 | } 260 | strcpy(result, input); 261 | return result; 262 | } 263 | 264 | bool ch_string_eq(const void *data1, const void *data2, void *arg) { 265 | const char *str1 = (const char*) data1; 266 | const char *str2 = (const char*) data2; 267 | return !(strcmp(str1, str2)) ? true : false; 268 | } 269 | 270 | void ch_string_free(void *data, void *arg) { 271 | free(data); 272 | } 273 | 274 | void ch_string_print(const void *data) { 275 | printf("%s", (const char*) data); 276 | } 277 | 278 | ch_key_ops ch_key_ops_string = { ch_string_hash, ch_string_cp, ch_string_free, ch_string_eq, NULL}; 279 | ch_val_ops ch_val_ops_string = { ch_string_cp, ch_string_free, ch_string_eq, NULL }; -------------------------------------------------------------------------------- /chained_hashv.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vect.h" 6 | 7 | #define CH_HASH_CAPACITY_INIT (1024) 8 | #define CH_HASH_CAPACITY_MULT (2) 9 | #define CH_HASH_GROWTH (1) 10 | 11 | typedef struct ch_key_ops_s { 12 | uint32_t (*hash)(const void *data, void *arg); 13 | void* (*cp)(const void *data, void *arg); 14 | void (*free)(void *data, void *arg); 15 | bool (*eq)(const void *data1, const void *data2, void *arg); 16 | void *arg; 17 | } ch_key_ops; 18 | 19 | typedef struct ch_val_ops_s { 20 | void* (*cp)(const void *data, void *arg); 21 | void (*free)(void *data, void *arg); 22 | bool (*eq)(const void *data1, const void *data2, void *arg); 23 | void *arg; 24 | } ch_val_ops; 25 | 26 | typedef struct ch_node_s { 27 | uint32_t hash; 28 | void *key; 29 | void *val; 30 | } ch_node; 31 | 32 | typedef struct ch_hashv_s { 33 | size_t capacity; 34 | size_t size; 35 | ch_vect **buckets; 36 | ch_key_ops key_ops; 37 | ch_val_ops val_ops; 38 | } ch_hashv; 39 | 40 | 41 | // Creates a new hash table 42 | ch_hashv *ch_hashv_new(ch_key_ops k_ops, ch_val_ops v_ops); 43 | 44 | // Free the memory associated with the hash (and all of its contents) 45 | void ch_hashv_free(ch_hashv *htable); 46 | 47 | // Gets the value coresponding to a key 48 | // If the key is not found returns NULL 49 | void* ch_hashv_get(ch_hashv *htable, const void *k); 50 | 51 | // Checks if a key exists or not in the hash table 52 | bool ch_hashv_contains(ch_hashv *htable, const void *k); 53 | 54 | // Adds a pair to the table 55 | void ch_hashv_put(ch_hashv *htable, const void *k, const void *v); 56 | 57 | // Prints the contents of the hash table 58 | void ch_hashv_print(ch_hashv *htable, void (*print_key)(const void *k), void (*print_val)(const void *v)); 59 | 60 | // Get the total number of collisions 61 | uint32_t ch_hashv_numcol(ch_hashv *hash); 62 | 63 | // String operations 64 | 65 | uint32_t ch_string_hash(const void *data, void *arg); 66 | void* ch_string_cp(const void *data, void *arg); 67 | bool ch_string_eq(const void *data1, const void *data2, void *arg); 68 | void ch_string_free(void *data, void *arg); 69 | void ch_string_print(const void *data); 70 | 71 | extern ch_key_ops ch_key_ops_string; 72 | extern ch_val_ops ch_val_ops_string; -------------------------------------------------------------------------------- /vect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "vect.h" 8 | 9 | ch_vect* ch_vect_new(size_t capacity) { 10 | ch_vect *result; 11 | result = malloc(sizeof(*result)); 12 | if (NULL==result) { 13 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 14 | exit(EXIT_FAILURE); 15 | } 16 | result->capacity = capacity; 17 | result->size = 0; 18 | result->array = malloc(result->capacity * sizeof(*(result->array))); 19 | if (NULL == result->array) { 20 | fprintf(stderr,"malloc() failed in file %s at line # %d", __FILE__,__LINE__); 21 | exit(EXIT_FAILURE); 22 | } 23 | return result; 24 | } 25 | 26 | ch_vect* ch_vect_new_default() { 27 | return ch_vect_new(VECT_INIT_CAPACITY); 28 | } 29 | 30 | void ch_vect_free(ch_vect *vect) { 31 | free(vect->array); 32 | free(vect); 33 | } 34 | 35 | void* ch_vect_get(ch_vect *vect, size_t idx) { 36 | if (idx >= vect->size) { 37 | fprintf(stderr, "cannot get index %lu from vector.\n", idx); 38 | exit(EXIT_FAILURE); 39 | } 40 | return vect->array[idx]; 41 | } 42 | 43 | void ch_vect_set(ch_vect *vect, size_t idx, void *data) { 44 | if (idx >= vect->size) { 45 | fprintf(stderr, "cannot get index %lu from vector.\n", idx); 46 | exit(EXIT_FAILURE); 47 | } 48 | vect->array[idx] = data; 49 | } 50 | 51 | void ch_vect_append(ch_vect *vect, void *data) { 52 | if (!(vect->size < vect->capacity)) { 53 | // Check for a potential overflow 54 | uint64_t tmp = (uint64_t) VECT_GROWTH_MULTI * (uint64_t) vect->capacity; 55 | if (tmp > SIZE_MAX) { 56 | fprintf(stderr, "size overflow\n"); 57 | exit(EXIT_FAILURE); 58 | } 59 | size_t new_capacity = (size_t) tmp; 60 | //void *new_array = malloc(new_capacity * sizeof(*(vect->array))); 61 | vect->array = realloc(vect->array, new_capacity * sizeof(*(vect->array))); 62 | if (NULL==vect->array) { 63 | fprintf(stderr,"realloc() failed in file %s at line # %d", __FILE__,__LINE__); 64 | exit(EXIT_FAILURE); 65 | } 66 | vect->capacity = new_capacity; 67 | } 68 | vect->array[vect->size] = data; 69 | vect->size++; 70 | } -------------------------------------------------------------------------------- /vect.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define VECT_INIT_CAPACITY (32) 4 | #define VECT_GROWTH_MULTI (2) 5 | 6 | typedef struct ch_vect_s { 7 | size_t capacity; 8 | size_t size; 9 | void **array; 10 | } ch_vect; 11 | 12 | ch_vect* ch_vect_new(size_t capacity); 13 | ch_vect* ch_vect_new_default(); 14 | void ch_vect_free(ch_vect *vect); 15 | void* ch_vect_get(ch_vect *vect, size_t idx); 16 | void ch_vect_set(ch_vect *vect, size_t idx, void *data); 17 | void ch_vect_append(ch_vect *vect, void *data); --------------------------------------------------------------------------------