1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #include <curl/curl.h> 26 27 #include "hash.h" 28 #include "llist.h" 29 #include "curl_memory.h" 30 31 /* The last #include file should be: */ 32 #include "memdebug.h" 33 34 static void 35 hash_element_dtor(void *user, void *element) 36 { 37 struct curl_hash *h = (struct curl_hash *) user; 38 struct curl_hash_element *e = (struct curl_hash_element *) element; 39 40 if(e->ptr) { 41 h->dtor(e->ptr); 42 e->ptr = NULL; 43 } 44 45 e->key_len = 0; 46 47 free(e); 48 } 49 50 /* Initializes a hash structure. 51 * Return 1 on error, 0 is fine. 52 * 53 * @unittest: 1602 54 * @unittest: 1603 55 */ 56 int 57 Curl_hash_init(struct curl_hash *h, 58 int slots, 59 hash_function hfunc, 60 comp_function comparator, 61 curl_hash_dtor dtor) 62 { 63 int i; 64 65 if(!slots || !hfunc || !comparator ||!dtor) { 66 return 1; /* failure */ 67 } 68 69 h->hash_func = hfunc; 70 h->comp_func = comparator; 71 h->dtor = dtor; 72 h->size = 0; 73 h->slots = slots; 74 75 h->table = malloc(slots * sizeof(struct curl_llist)); 76 if(h->table) { 77 for(i = 0; i < slots; ++i) 78 Curl_llist_init(&h->table[i], (curl_llist_dtor) hash_element_dtor); 79 return 0; /* fine */ 80 } 81 h->slots = 0; 82 return 1; /* failure */ 83 } 84 85 static struct curl_hash_element * 86 mk_hash_element(const void *key, size_t key_len, const void *p) 87 { 88 /* allocate the struct plus memory after it to store the key */ 89 struct curl_hash_element *he = malloc(sizeof(struct curl_hash_element) + 90 key_len); 91 if(he) { 92 /* copy the key */ 93 memcpy(he->key, key, key_len); 94 he->key_len = key_len; 95 he->ptr = (void *) p; 96 } 97 return he; 98 } 99 100 #define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)] 101 102 /* Insert the data in the hash. If there already was a match in the hash, 103 * that data is replaced. 104 * 105 * @unittest: 1305 106 * @unittest: 1602 107 * @unittest: 1603 108 */ 109 void * 110 Curl_hash_add(struct curl_hash *h, void *key, size_t key_len, void *p) 111 { 112 struct curl_hash_element *he; 113 struct curl_llist_element *le; 114 struct curl_llist *l = FETCH_LIST(h, key, key_len); 115 116 for(le = l->head; le; le = le->next) { 117 he = (struct curl_hash_element *) le->ptr; 118 if(h->comp_func(he->key, he->key_len, key, key_len)) { 119 Curl_llist_remove(l, le, (void *)h); 120 --h->size; 121 break; 122 } 123 } 124 125 he = mk_hash_element(key, key_len, p); 126 if(he) { 127 Curl_llist_insert_next(l, l->tail, he, &he->list); 128 ++h->size; 129 return p; /* return the new entry */ 130 } 131 132 return NULL; /* failure */ 133 } 134 135 /* Remove the identified hash entry. 136 * Returns non-zero on failure. 137 * 138 * @unittest: 1603 139 */ 140 int Curl_hash_delete(struct curl_hash *h, void *key, size_t key_len) 141 { 142 struct curl_llist_element *le; 143 struct curl_hash_element *he; 144 struct curl_llist *l = FETCH_LIST(h, key, key_len); 145 146 for(le = l->head; le; le = le->next) { 147 he = le->ptr; 148 if(h->comp_func(he->key, he->key_len, key, key_len)) { 149 Curl_llist_remove(l, le, (void *) h); 150 --h->size; 151 return 0; 152 } 153 } 154 return 1; 155 } 156 157 /* Retrieves a hash element. 158 * 159 * @unittest: 1603 160 */ 161 void * 162 Curl_hash_pick(struct curl_hash *h, void *key, size_t key_len) 163 { 164 struct curl_llist_element *le; 165 struct curl_hash_element *he; 166 struct curl_llist *l; 167 168 if(h) { 169 l = FETCH_LIST(h, key, key_len); 170 for(le = l->head; le; le = le->next) { 171 he = le->ptr; 172 if(h->comp_func(he->key, he->key_len, key, key_len)) { 173 return he->ptr; 174 } 175 } 176 } 177 178 return NULL; 179 } 180 181 #if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST) 182 void 183 Curl_hash_apply(curl_hash *h, void *user, 184 void (*cb)(void *user, void *ptr)) 185 { 186 struct curl_llist_element *le; 187 int i; 188 189 for(i = 0; i < h->slots; ++i) { 190 for(le = (h->table[i])->head; 191 le; 192 le = le->next) { 193 curl_hash_element *el = le->ptr; 194 cb(user, el->ptr); 195 } 196 } 197 } 198 #endif 199 200 /* Destroys all the entries in the given hash and resets its attributes, 201 * prepping the given hash for [static|dynamic] deallocation. 202 * 203 * @unittest: 1305 204 * @unittest: 1602 205 * @unittest: 1603 206 */ 207 void 208 Curl_hash_destroy(struct curl_hash *h) 209 { 210 int i; 211 212 for(i = 0; i < h->slots; ++i) { 213 Curl_llist_destroy(&h->table[i], (void *) h); 214 } 215 216 Curl_safefree(h->table); 217 h->size = 0; 218 h->slots = 0; 219 } 220 221 /* Removes all the entries in the given hash. 222 * 223 * @unittest: 1602 224 */ 225 void 226 Curl_hash_clean(struct curl_hash *h) 227 { 228 Curl_hash_clean_with_criterium(h, NULL, NULL); 229 } 230 231 /* Cleans all entries that pass the comp function criteria. */ 232 void 233 Curl_hash_clean_with_criterium(struct curl_hash *h, void *user, 234 int (*comp)(void *, void *)) 235 { 236 struct curl_llist_element *le; 237 struct curl_llist_element *lnext; 238 struct curl_llist *list; 239 int i; 240 241 if(!h) 242 return; 243 244 for(i = 0; i < h->slots; ++i) { 245 list = &h->table[i]; 246 le = list->head; /* get first list entry */ 247 while(le) { 248 struct curl_hash_element *he = le->ptr; 249 lnext = le->next; 250 /* ask the callback function if we shall remove this entry or not */ 251 if(comp == NULL || comp(user, he->ptr)) { 252 Curl_llist_remove(list, le, (void *) h); 253 --h->size; /* one less entry in the hash now */ 254 } 255 le = lnext; 256 } 257 } 258 } 259 260 size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) 261 { 262 const char *key_str = (const char *) key; 263 const char *end = key_str + key_length; 264 unsigned long h = 5381; 265 266 while(key_str < end) { 267 h += h << 5; 268 h ^= (unsigned long) *key_str++; 269 } 270 271 return (h % slots_num); 272 } 273 274 size_t Curl_str_key_compare(void *k1, size_t key1_len, 275 void *k2, size_t key2_len) 276 { 277 if((key1_len == key2_len) && !memcmp(k1, k2, key1_len)) 278 return 1; 279 280 return 0; 281 } 282 283 void Curl_hash_start_iterate(struct curl_hash *hash, 284 struct curl_hash_iterator *iter) 285 { 286 iter->hash = hash; 287 iter->slot_index = 0; 288 iter->current_element = NULL; 289 } 290 291 struct curl_hash_element * 292 Curl_hash_next_element(struct curl_hash_iterator *iter) 293 { 294 int i; 295 struct curl_hash *h = iter->hash; 296 297 /* Get the next element in the current list, if any */ 298 if(iter->current_element) 299 iter->current_element = iter->current_element->next; 300 301 /* If we have reached the end of the list, find the next one */ 302 if(!iter->current_element) { 303 for(i = iter->slot_index; i < h->slots; i++) { 304 if(h->table[i].head) { 305 iter->current_element = h->table[i].head; 306 iter->slot_index = i + 1; 307 break; 308 } 309 } 310 } 311 312 if(iter->current_element) { 313 struct curl_hash_element *he = iter->current_element->ptr; 314 return he; 315 } 316 iter->current_element = NULL; 317 return NULL; 318 } 319 320 #if 0 /* useful function for debugging hashes and their contents */ 321 void Curl_hash_print(struct curl_hash *h, 322 void (*func)(void *)) 323 { 324 struct curl_hash_iterator iter; 325 struct curl_hash_element *he; 326 int last_index = -1; 327 328 if(!h) 329 return; 330 331 fprintf(stderr, "=Hash dump=\n"); 332 333 Curl_hash_start_iterate(h, &iter); 334 335 he = Curl_hash_next_element(&iter); 336 while(he) { 337 if(iter.slot_index != last_index) { 338 fprintf(stderr, "index %d:", iter.slot_index); 339 if(last_index >= 0) { 340 fprintf(stderr, "\n"); 341 } 342 last_index = iter.slot_index; 343 } 344 345 if(func) 346 func(he->ptr); 347 else 348 fprintf(stderr, " [%p]", (void *)he->ptr); 349 350 he = Curl_hash_next_element(&iter); 351 } 352 fprintf(stderr, "\n"); 353 } 354 #endif 355