xref: /NextBSD/contrib/jansson/src/hashtable.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org>
3  *
4  * This library is free software; you can redistribute it and/or modify
5  * it under the terms of the MIT license. See LICENSE for details.
6  */
7 
8 #if HAVE_CONFIG_H
9 #include <jansson_private_config.h>
10 #endif
11 
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #if HAVE_STDINT_H
16 #include <stdint.h>
17 #endif
18 
19 #include <jansson_config.h>   /* for JSON_INLINE */
20 #include "jansson_private.h"  /* for container_of() */
21 #include "hashtable.h"
22 
23 typedef struct hashtable_list list_t;
24 typedef struct hashtable_pair pair_t;
25 typedef struct hashtable_bucket bucket_t;
26 
27 extern volatile uint32_t hashtable_seed;
28 
29 /* Implementation of the hash function */
30 #include "lookup3.h"
31 
32 #define list_to_pair(list_)  container_of(list_, pair_t, list)
33 #define hash_str(key)        ((size_t)hashlittle((key), strlen(key), hashtable_seed))
34 
list_init(list_t * list)35 static JSON_INLINE void list_init(list_t *list)
36 {
37     list->next = list;
38     list->prev = list;
39 }
40 
list_insert(list_t * list,list_t * node)41 static JSON_INLINE void list_insert(list_t *list, list_t *node)
42 {
43     node->next = list;
44     node->prev = list->prev;
45     list->prev->next = node;
46     list->prev = node;
47 }
48 
list_remove(list_t * list)49 static JSON_INLINE void list_remove(list_t *list)
50 {
51     list->prev->next = list->next;
52     list->next->prev = list->prev;
53 }
54 
bucket_is_empty(hashtable_t * hashtable,bucket_t * bucket)55 static JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
56 {
57     return bucket->first == &hashtable->list && bucket->first == bucket->last;
58 }
59 
insert_to_bucket(hashtable_t * hashtable,bucket_t * bucket,list_t * list)60 static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
61                              list_t *list)
62 {
63     if(bucket_is_empty(hashtable, bucket))
64     {
65         list_insert(&hashtable->list, list);
66         bucket->first = bucket->last = list;
67     }
68     else
69     {
70         list_insert(bucket->first, list);
71         bucket->first = list;
72     }
73 }
74 
hashtable_find_pair(hashtable_t * hashtable,bucket_t * bucket,const char * key,size_t hash)75 static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
76                                    const char *key, size_t hash)
77 {
78     list_t *list;
79     pair_t *pair;
80 
81     if(bucket_is_empty(hashtable, bucket))
82         return NULL;
83 
84     list = bucket->first;
85     while(1)
86     {
87         pair = list_to_pair(list);
88         if(pair->hash == hash && strcmp(pair->key, key) == 0)
89             return pair;
90 
91         if(list == bucket->last)
92             break;
93 
94         list = list->next;
95     }
96 
97     return NULL;
98 }
99 
100 /* returns 0 on success, -1 if key was not found */
hashtable_do_del(hashtable_t * hashtable,const char * key,size_t hash)101 static int hashtable_do_del(hashtable_t *hashtable,
102                             const char *key, size_t hash)
103 {
104     pair_t *pair;
105     bucket_t *bucket;
106     size_t index;
107 
108     index = hash & hashmask(hashtable->order);
109     bucket = &hashtable->buckets[index];
110 
111     pair = hashtable_find_pair(hashtable, bucket, key, hash);
112     if(!pair)
113         return -1;
114 
115     if(&pair->list == bucket->first && &pair->list == bucket->last)
116         bucket->first = bucket->last = &hashtable->list;
117 
118     else if(&pair->list == bucket->first)
119         bucket->first = pair->list.next;
120 
121     else if(&pair->list == bucket->last)
122         bucket->last = pair->list.prev;
123 
124     list_remove(&pair->list);
125     json_decref(pair->value);
126 
127     jsonp_free(pair);
128     hashtable->size--;
129 
130     return 0;
131 }
132 
hashtable_do_clear(hashtable_t * hashtable)133 static void hashtable_do_clear(hashtable_t *hashtable)
134 {
135     list_t *list, *next;
136     pair_t *pair;
137 
138     for(list = hashtable->list.next; list != &hashtable->list; list = next)
139     {
140         next = list->next;
141         pair = list_to_pair(list);
142         json_decref(pair->value);
143         jsonp_free(pair);
144     }
145 }
146 
hashtable_do_rehash(hashtable_t * hashtable)147 static int hashtable_do_rehash(hashtable_t *hashtable)
148 {
149     list_t *list, *next;
150     pair_t *pair;
151     size_t i, index, new_size;
152 
153     jsonp_free(hashtable->buckets);
154 
155     hashtable->order++;
156     new_size = hashsize(hashtable->order);
157 
158     hashtable->buckets = jsonp_malloc(new_size * sizeof(bucket_t));
159     if(!hashtable->buckets)
160         return -1;
161 
162     for(i = 0; i < hashsize(hashtable->order); i++)
163     {
164         hashtable->buckets[i].first = hashtable->buckets[i].last =
165             &hashtable->list;
166     }
167 
168     list = hashtable->list.next;
169     list_init(&hashtable->list);
170 
171     for(; list != &hashtable->list; list = next) {
172         next = list->next;
173         pair = list_to_pair(list);
174         index = pair->hash % new_size;
175         insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list);
176     }
177 
178     return 0;
179 }
180 
181 
hashtable_init(hashtable_t * hashtable)182 int hashtable_init(hashtable_t *hashtable)
183 {
184     size_t i;
185 
186     hashtable->size = 0;
187     hashtable->order = 3;
188     hashtable->buckets = jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
189     if(!hashtable->buckets)
190         return -1;
191 
192     list_init(&hashtable->list);
193 
194     for(i = 0; i < hashsize(hashtable->order); i++)
195     {
196         hashtable->buckets[i].first = hashtable->buckets[i].last =
197             &hashtable->list;
198     }
199 
200     return 0;
201 }
202 
hashtable_close(hashtable_t * hashtable)203 void hashtable_close(hashtable_t *hashtable)
204 {
205     hashtable_do_clear(hashtable);
206     jsonp_free(hashtable->buckets);
207 }
208 
hashtable_set(hashtable_t * hashtable,const char * key,size_t serial,json_t * value)209 int hashtable_set(hashtable_t *hashtable,
210                   const char *key, size_t serial,
211                   json_t *value)
212 {
213     pair_t *pair;
214     bucket_t *bucket;
215     size_t hash, index;
216 
217     /* rehash if the load ratio exceeds 1 */
218     if(hashtable->size >= hashsize(hashtable->order))
219         if(hashtable_do_rehash(hashtable))
220             return -1;
221 
222     hash = hash_str(key);
223     index = hash & hashmask(hashtable->order);
224     bucket = &hashtable->buckets[index];
225     pair = hashtable_find_pair(hashtable, bucket, key, hash);
226 
227     if(pair)
228     {
229         json_decref(pair->value);
230         pair->value = value;
231     }
232     else
233     {
234         /* offsetof(...) returns the size of pair_t without the last,
235            flexible member. This way, the correct amount is
236            allocated. */
237 
238         size_t len = strlen(key);
239         if(len >= (size_t)-1 - offsetof(pair_t, key)) {
240             /* Avoid an overflow if the key is very long */
241             return -1;
242         }
243 
244         pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
245         if(!pair)
246             return -1;
247 
248         pair->hash = hash;
249         pair->serial = serial;
250         strcpy(pair->key, key);
251         pair->value = value;
252         list_init(&pair->list);
253 
254         insert_to_bucket(hashtable, bucket, &pair->list);
255 
256         hashtable->size++;
257     }
258     return 0;
259 }
260 
hashtable_get(hashtable_t * hashtable,const char * key)261 void *hashtable_get(hashtable_t *hashtable, const char *key)
262 {
263     pair_t *pair;
264     size_t hash;
265     bucket_t *bucket;
266 
267     hash = hash_str(key);
268     bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
269 
270     pair = hashtable_find_pair(hashtable, bucket, key, hash);
271     if(!pair)
272         return NULL;
273 
274     return pair->value;
275 }
276 
hashtable_del(hashtable_t * hashtable,const char * key)277 int hashtable_del(hashtable_t *hashtable, const char *key)
278 {
279     size_t hash = hash_str(key);
280     return hashtable_do_del(hashtable, key, hash);
281 }
282 
hashtable_clear(hashtable_t * hashtable)283 void hashtable_clear(hashtable_t *hashtable)
284 {
285     size_t i;
286 
287     hashtable_do_clear(hashtable);
288 
289     for(i = 0; i < hashsize(hashtable->order); i++)
290     {
291         hashtable->buckets[i].first = hashtable->buckets[i].last =
292             &hashtable->list;
293     }
294 
295     list_init(&hashtable->list);
296     hashtable->size = 0;
297 }
298 
hashtable_iter(hashtable_t * hashtable)299 void *hashtable_iter(hashtable_t *hashtable)
300 {
301     return hashtable_iter_next(hashtable, &hashtable->list);
302 }
303 
hashtable_iter_at(hashtable_t * hashtable,const char * key)304 void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
305 {
306     pair_t *pair;
307     size_t hash;
308     bucket_t *bucket;
309 
310     hash = hash_str(key);
311     bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
312 
313     pair = hashtable_find_pair(hashtable, bucket, key, hash);
314     if(!pair)
315         return NULL;
316 
317     return &pair->list;
318 }
319 
hashtable_iter_next(hashtable_t * hashtable,void * iter)320 void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
321 {
322     list_t *list = (list_t *)iter;
323     if(list->next == &hashtable->list)
324         return NULL;
325     return list->next;
326 }
327 
hashtable_iter_key(void * iter)328 void *hashtable_iter_key(void *iter)
329 {
330     pair_t *pair = list_to_pair((list_t *)iter);
331     return pair->key;
332 }
333 
hashtable_iter_serial(void * iter)334 size_t hashtable_iter_serial(void *iter)
335 {
336     pair_t *pair = list_to_pair((list_t *)iter);
337     return pair->serial;
338 }
339 
hashtable_iter_value(void * iter)340 void *hashtable_iter_value(void *iter)
341 {
342     pair_t *pair = list_to_pair((list_t *)iter);
343     return pair->value;
344 }
345 
hashtable_iter_set(void * iter,json_t * value)346 void hashtable_iter_set(void *iter, json_t *value)
347 {
348     pair_t *pair = list_to_pair((list_t *)iter);
349 
350     json_decref(pair->value);
351     pair->value = value;
352 }
353