2 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
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.
9 #include <jansson_private_config.h>
19 #include <jansson_config.h> /* for JSON_INLINE */
20 #include "jansson_private.h" /* for container_of() */
21 #include "hashtable.h"
23 #ifndef INITIAL_HASHTABLE_ORDER
24 #define INITIAL_HASHTABLE_ORDER 3
27 typedef struct hashtable_list list_t
;
28 typedef struct hashtable_pair pair_t
;
29 typedef struct hashtable_bucket bucket_t
;
31 extern volatile uint32_t hashtable_seed
;
33 /* Implementation of the hash function */
36 #define list_to_pair(list_) container_of(list_, pair_t, list)
37 #define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
38 #define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
40 static JSON_INLINE
void list_init(list_t
*list
)
46 static JSON_INLINE
void list_insert(list_t
*list
, list_t
*node
)
49 node
->prev
= list
->prev
;
50 list
->prev
->next
= node
;
54 static JSON_INLINE
void list_remove(list_t
*list
)
56 list
->prev
->next
= list
->next
;
57 list
->next
->prev
= list
->prev
;
60 static JSON_INLINE
int bucket_is_empty(hashtable_t
*hashtable
, bucket_t
*bucket
)
62 return bucket
->first
== &hashtable
->list
&& bucket
->first
== bucket
->last
;
65 static void insert_to_bucket(hashtable_t
*hashtable
, bucket_t
*bucket
,
68 if(bucket_is_empty(hashtable
, bucket
))
70 list_insert(&hashtable
->list
, list
);
71 bucket
->first
= bucket
->last
= list
;
75 list_insert(bucket
->first
, list
);
80 static pair_t
*hashtable_find_pair(hashtable_t
*hashtable
, bucket_t
*bucket
,
81 const char *key
, size_t hash
)
86 if(bucket_is_empty(hashtable
, bucket
))
92 pair
= list_to_pair(list
);
93 if(pair
->hash
== hash
&& strcmp(pair
->key
, key
) == 0)
96 if(list
== bucket
->last
)
105 /* returns 0 on success, -1 if key was not found */
106 static int hashtable_do_del(hashtable_t
*hashtable
,
107 const char *key
, size_t hash
)
113 index
= hash
& hashmask(hashtable
->order
);
114 bucket
= &hashtable
->buckets
[index
];
116 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
120 if(&pair
->list
== bucket
->first
&& &pair
->list
== bucket
->last
)
121 bucket
->first
= bucket
->last
= &hashtable
->list
;
123 else if(&pair
->list
== bucket
->first
)
124 bucket
->first
= pair
->list
.next
;
126 else if(&pair
->list
== bucket
->last
)
127 bucket
->last
= pair
->list
.prev
;
129 list_remove(&pair
->list
);
130 list_remove(&pair
->ordered_list
);
131 json_decref(pair
->value
);
139 static void hashtable_do_clear(hashtable_t
*hashtable
)
144 for(list
= hashtable
->list
.next
; list
!= &hashtable
->list
; list
= next
)
147 pair
= list_to_pair(list
);
148 json_decref(pair
->value
);
153 static int hashtable_do_rehash(hashtable_t
*hashtable
)
157 size_t i
, index
, new_size
, new_order
;
158 struct hashtable_bucket
*new_buckets
;
160 new_order
= hashtable
->order
+ 1;
161 new_size
= hashsize(new_order
);
163 new_buckets
= jsonp_malloc(new_size
* sizeof(bucket_t
));
167 jsonp_free(hashtable
->buckets
);
168 hashtable
->buckets
= new_buckets
;
169 hashtable
->order
= new_order
;
171 for(i
= 0; i
< hashsize(hashtable
->order
); i
++)
173 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
177 list
= hashtable
->list
.next
;
178 list_init(&hashtable
->list
);
180 for(; list
!= &hashtable
->list
; list
= next
) {
182 pair
= list_to_pair(list
);
183 index
= pair
->hash
% new_size
;
184 insert_to_bucket(hashtable
, &hashtable
->buckets
[index
], &pair
->list
);
191 int hashtable_init(hashtable_t
*hashtable
)
196 hashtable
->order
= INITIAL_HASHTABLE_ORDER
;
197 hashtable
->buckets
= jsonp_malloc(hashsize(hashtable
->order
) * sizeof(bucket_t
));
198 if(!hashtable
->buckets
)
201 list_init(&hashtable
->list
);
202 list_init(&hashtable
->ordered_list
);
204 for(i
= 0; i
< hashsize(hashtable
->order
); i
++)
206 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
213 void hashtable_close(hashtable_t
*hashtable
)
215 hashtable_do_clear(hashtable
);
216 jsonp_free(hashtable
->buckets
);
219 int hashtable_set(hashtable_t
*hashtable
, const char *key
, json_t
*value
)
225 /* rehash if the load ratio exceeds 1 */
226 if(hashtable
->size
>= hashsize(hashtable
->order
))
227 if(hashtable_do_rehash(hashtable
))
230 hash
= hash_str(key
);
231 index
= hash
& hashmask(hashtable
->order
);
232 bucket
= &hashtable
->buckets
[index
];
233 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
237 json_decref(pair
->value
);
242 /* offsetof(...) returns the size of pair_t without the last,
243 flexible member. This way, the correct amount is
246 size_t len
= strlen(key
);
247 if(len
>= (size_t)-1 - offsetof(pair_t
, key
)) {
248 /* Avoid an overflow if the key is very long */
252 pair
= jsonp_malloc(offsetof(pair_t
, key
) + len
+ 1);
257 strncpy(pair
->key
, key
, len
+ 1);
259 list_init(&pair
->list
);
260 list_init(&pair
->ordered_list
);
262 insert_to_bucket(hashtable
, bucket
, &pair
->list
);
263 list_insert(&hashtable
->ordered_list
, &pair
->ordered_list
);
270 void *hashtable_get(hashtable_t
*hashtable
, const char *key
)
276 hash
= hash_str(key
);
277 bucket
= &hashtable
->buckets
[hash
& hashmask(hashtable
->order
)];
279 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
286 int hashtable_del(hashtable_t
*hashtable
, const char *key
)
288 size_t hash
= hash_str(key
);
289 return hashtable_do_del(hashtable
, key
, hash
);
292 void hashtable_clear(hashtable_t
*hashtable
)
296 hashtable_do_clear(hashtable
);
298 for(i
= 0; i
< hashsize(hashtable
->order
); i
++)
300 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
304 list_init(&hashtable
->list
);
305 list_init(&hashtable
->ordered_list
);
309 void *hashtable_iter(hashtable_t
*hashtable
)
311 return hashtable_iter_next(hashtable
, &hashtable
->ordered_list
);
314 void *hashtable_iter_at(hashtable_t
*hashtable
, const char *key
)
320 hash
= hash_str(key
);
321 bucket
= &hashtable
->buckets
[hash
& hashmask(hashtable
->order
)];
323 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
327 return &pair
->ordered_list
;
330 void *hashtable_iter_next(hashtable_t
*hashtable
, void *iter
)
332 list_t
*list
= (list_t
*)iter
;
333 if(list
->next
== &hashtable
->ordered_list
)
338 void *hashtable_iter_key(void *iter
)
340 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
344 void *hashtable_iter_value(void *iter
)
346 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
350 void hashtable_iter_set(void *iter
, json_t
*value
)
352 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
354 json_decref(pair
->value
);