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
) {
45 static JSON_INLINE
void list_insert(list_t
*list
, list_t
*node
) {
47 node
->prev
= list
->prev
;
48 list
->prev
->next
= node
;
52 static JSON_INLINE
void list_remove(list_t
*list
) {
53 list
->prev
->next
= list
->next
;
54 list
->next
->prev
= list
->prev
;
57 static JSON_INLINE
int bucket_is_empty(hashtable_t
*hashtable
, bucket_t
*bucket
) {
58 return bucket
->first
== &hashtable
->list
&& bucket
->first
== bucket
->last
;
61 static void insert_to_bucket(hashtable_t
*hashtable
, bucket_t
*bucket
,
63 if (bucket_is_empty(hashtable
, bucket
)) {
64 list_insert(&hashtable
->list
, list
);
65 bucket
->first
= bucket
->last
= list
;
67 list_insert(bucket
->first
, list
);
72 static pair_t
*hashtable_find_pair(hashtable_t
*hashtable
, bucket_t
*bucket
,
73 const char *key
, size_t hash
) {
77 if (bucket_is_empty(hashtable
, bucket
))
82 pair
= list_to_pair(list
);
83 if (pair
->hash
== hash
&& strcmp(pair
->key
, key
) == 0)
86 if (list
== bucket
->last
)
95 /* returns 0 on success, -1 if key was not found */
96 static int hashtable_do_del(hashtable_t
*hashtable
,
97 const char *key
, size_t hash
) {
102 index
= hash
& hashmask(hashtable
->order
);
103 bucket
= &hashtable
->buckets
[index
];
105 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
109 if (&pair
->list
== bucket
->first
&& &pair
->list
== bucket
->last
)
110 bucket
->first
= bucket
->last
= &hashtable
->list
;
112 else if (&pair
->list
== bucket
->first
)
113 bucket
->first
= pair
->list
.next
;
115 else if (&pair
->list
== bucket
->last
)
116 bucket
->last
= pair
->list
.prev
;
118 list_remove(&pair
->list
);
119 list_remove(&pair
->ordered_list
);
120 json_decref(pair
->value
);
128 static void hashtable_do_clear(hashtable_t
*hashtable
) {
132 for (list
= hashtable
->list
.next
; list
!= &hashtable
->list
; list
= next
) {
134 pair
= list_to_pair(list
);
135 json_decref(pair
->value
);
140 static int hashtable_do_rehash(hashtable_t
*hashtable
) {
143 size_t i
, index
, new_size
, new_order
;
144 struct hashtable_bucket
*new_buckets
;
146 new_order
= hashtable
->order
+ 1;
147 new_size
= hashsize(new_order
);
149 new_buckets
= jsonp_malloc(new_size
* sizeof(bucket_t
));
153 jsonp_free(hashtable
->buckets
);
154 hashtable
->buckets
= new_buckets
;
155 hashtable
->order
= new_order
;
157 for (i
= 0; i
< hashsize(hashtable
->order
); i
++) {
158 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
162 list
= hashtable
->list
.next
;
163 list_init(&hashtable
->list
);
165 for (; list
!= &hashtable
->list
; list
= next
) {
167 pair
= list_to_pair(list
);
168 index
= pair
->hash
% new_size
;
169 insert_to_bucket(hashtable
, &hashtable
->buckets
[index
], &pair
->list
);
176 int hashtable_init(hashtable_t
*hashtable
) {
180 hashtable
->order
= INITIAL_HASHTABLE_ORDER
;
181 hashtable
->buckets
= jsonp_malloc(hashsize(hashtable
->order
) * sizeof(bucket_t
));
182 if (!hashtable
->buckets
)
185 list_init(&hashtable
->list
);
186 list_init(&hashtable
->ordered_list
);
188 for (i
= 0; i
< hashsize(hashtable
->order
); i
++) {
189 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
196 void hashtable_close(hashtable_t
*hashtable
) {
197 hashtable_do_clear(hashtable
);
198 jsonp_free(hashtable
->buckets
);
201 int hashtable_set(hashtable_t
*hashtable
, const char *key
, json_t
*value
) {
206 /* rehash if the load ratio exceeds 1 */
207 if (hashtable
->size
>= hashsize(hashtable
->order
))
208 if (hashtable_do_rehash(hashtable
))
211 hash
= hash_str(key
);
212 index
= hash
& hashmask(hashtable
->order
);
213 bucket
= &hashtable
->buckets
[index
];
214 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
217 json_decref(pair
->value
);
220 /* offsetof(...) returns the size of pair_t without the last,
221 flexible member. This way, the correct amount is
224 size_t len
= strlen(key
);
225 if (len
>= (size_t) - 1 - offsetof(pair_t
, key
)) {
226 /* Avoid an overflow if the key is very long */
230 pair
= jsonp_malloc(offsetof(pair_t
, key
) + len
+ 1);
235 strncpy(pair
->key
, key
, len
+ 1);
237 list_init(&pair
->list
);
238 list_init(&pair
->ordered_list
);
240 insert_to_bucket(hashtable
, bucket
, &pair
->list
);
241 list_insert(&hashtable
->ordered_list
, &pair
->ordered_list
);
248 void *hashtable_get(hashtable_t
*hashtable
, const char *key
) {
253 hash
= hash_str(key
);
254 bucket
= &hashtable
->buckets
[hash
& hashmask(hashtable
->order
)];
256 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
263 int hashtable_del(hashtable_t
*hashtable
, const char *key
) {
264 size_t hash
= hash_str(key
);
265 return hashtable_do_del(hashtable
, key
, hash
);
268 void hashtable_clear(hashtable_t
*hashtable
) {
271 hashtable_do_clear(hashtable
);
273 for (i
= 0; i
< hashsize(hashtable
->order
); i
++) {
274 hashtable
->buckets
[i
].first
= hashtable
->buckets
[i
].last
=
278 list_init(&hashtable
->list
);
279 list_init(&hashtable
->ordered_list
);
283 void *hashtable_iter(hashtable_t
*hashtable
) {
284 return hashtable_iter_next(hashtable
, &hashtable
->ordered_list
);
287 void *hashtable_iter_at(hashtable_t
*hashtable
, const char *key
) {
292 hash
= hash_str(key
);
293 bucket
= &hashtable
->buckets
[hash
& hashmask(hashtable
->order
)];
295 pair
= hashtable_find_pair(hashtable
, bucket
, key
, hash
);
299 return &pair
->ordered_list
;
302 void *hashtable_iter_next(hashtable_t
*hashtable
, void *iter
) {
303 list_t
*list
= (list_t
*)iter
;
304 if (list
->next
== &hashtable
->ordered_list
)
309 void *hashtable_iter_key(void *iter
) {
310 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
314 void *hashtable_iter_value(void *iter
) {
315 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
319 void hashtable_iter_set(void *iter
, json_t
*value
) {
320 pair_t
*pair
= ordered_list_to_pair((list_t
*)iter
);
322 json_decref(pair
->value
);