- fix Building without Nagra not possible at Nagra_Merlin https://trac.streamboard...
[oscam.git] / tommyDS_hashlin / tommyhashlin.c
blob911a69b0dcea7998143fd97557fa8f5048a9a6a6
1 /*
2 * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
28 #include "tommyhashlin.h"
29 #include "tommylist.h"
31 #include <assert.h> /* for assert */
33 /******************************************************************************/
34 /* hashlin */
36 /**
37 * Reallocation states.
39 #define TOMMY_HASHLIN_STATE_STABLE 0
40 #define TOMMY_HASHLIN_STATE_GROW 1
41 #define TOMMY_HASHLIN_STATE_SHRINK 2
43 /**
44 * Set the hashtable in stable state.
46 tommy_inline void tommy_hashlin_stable(tommy_hashlin* hashlin)
48 hashlin->state = TOMMY_HASHLIN_STATE_STABLE;
50 /* setup low_mask/max/split to allow tommy_hashlin_bucket_ref() */
51 /* and tommy_hashlin_foreach() to work regardless we are in stable state */
52 hashlin->low_max = hashlin->bucket_max;
53 hashlin->low_mask = hashlin->bucket_mask;
54 hashlin->split = 0;
57 void tommy_hashlin_init(tommy_hashlin* hashlin)
59 tommy_uint_t i;
61 /* fixed initial size */
62 hashlin->bucket_bit = TOMMY_HASHLIN_BIT;
63 hashlin->bucket_max = 1 << hashlin->bucket_bit;
64 hashlin->bucket_mask = hashlin->bucket_max - 1;
65 hashlin->bucket[0] = tommy_cast(tommy_hashlin_node**, tommy_calloc(hashlin->bucket_max, sizeof(tommy_hashlin_node*)));
66 for (i = 1; i < TOMMY_HASHLIN_BIT; ++i)
67 hashlin->bucket[i] = hashlin->bucket[0];
69 /* stable state */
70 tommy_hashlin_stable(hashlin);
72 hashlin->count = 0;
75 void tommy_hashlin_done(tommy_hashlin* hashlin)
77 tommy_uint_t i;
79 tommy_free(hashlin->bucket[0]);
80 for (i = TOMMY_HASHLIN_BIT; i < hashlin->bucket_bit; ++i) {
81 tommy_hashlin_node** segment = hashlin->bucket[i];
82 tommy_free(&segment[((tommy_ptrdiff_t)1) << i]);
86 /**
87 * Grow one step.
89 tommy_inline void hashlin_grow_step(tommy_hashlin* hashlin)
91 /* grow if more than 50% full */
92 if (hashlin->state != TOMMY_HASHLIN_STATE_GROW
93 && hashlin->count > hashlin->bucket_max / 2
94 ) {
95 /* if we are stable, setup a new grow state */
96 /* otherwise continue with the already setup shrink one */
97 /* but in backward direction */
98 if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) {
99 tommy_hashlin_node** segment;
101 /* set the lower size */
102 hashlin->low_max = hashlin->bucket_max;
103 hashlin->low_mask = hashlin->bucket_mask;
105 /* allocate the new vector using malloc() and not calloc() */
106 /* because data is fully initialized in the split process */
107 segment = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->low_max * sizeof(tommy_hashlin_node*)));
109 /* store it adjusting the offset */
110 /* cast to ptrdiff_t to ensure to get a negative value */
111 hashlin->bucket[hashlin->bucket_bit] = &segment[-(tommy_ptrdiff_t)hashlin->low_max];
113 /* grow the hash size */
114 ++hashlin->bucket_bit;
115 hashlin->bucket_max = 1 << hashlin->bucket_bit;
116 hashlin->bucket_mask = hashlin->bucket_max - 1;
118 /* start from the beginning going forward */
119 hashlin->split = 0;
122 /* grow state */
123 hashlin->state = TOMMY_HASHLIN_STATE_GROW;
126 /* if we are growing */
127 if (hashlin->state == TOMMY_HASHLIN_STATE_GROW) {
128 /* compute the split target required to finish the reallocation before the next resize */
129 tommy_size_t split_target = 2 * hashlin->count;
131 /* reallocate buckets until the split target */
132 while (hashlin->split + hashlin->low_max < split_target) {
133 tommy_hashlin_node** split[2];
134 tommy_hashlin_node* j;
135 tommy_size_t mask;
137 /* get the low bucket */
138 split[0] = tommy_hashlin_pos(hashlin, hashlin->split);
140 /* get the high bucket */
141 split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max);
143 /* save the low bucket */
144 j = *split[0];
146 /* reinitialize the buckets */
147 *split[0] = 0;
148 *split[1] = 0;
150 /* the bit used to identify the bucket */
151 mask = hashlin->low_max;
153 /* flush the bucket */
154 while (j) {
155 tommy_hashlin_node* j_next = j->next;
156 tommy_size_t pos = (j->index & mask) != 0;
157 if (*split[pos])
158 tommy_list_insert_tail_not_empty(*split[pos], j);
159 else
160 tommy_list_insert_first(split[pos], j);
161 j = j_next;
164 /* go forward */
165 ++hashlin->split;
167 /* if we have finished, change the state */
168 if (hashlin->split == hashlin->low_max) {
169 /* go in stable mode */
170 tommy_hashlin_stable(hashlin);
171 break;
178 * Shrink one step.
180 tommy_inline void hashlin_shrink_step(tommy_hashlin* hashlin)
182 /* shrink if less than 12.5% full */
183 if (hashlin->state != TOMMY_HASHLIN_STATE_SHRINK
184 && hashlin->count < hashlin->bucket_max / 8
186 /* avoid to shrink the first bucket */
187 if (hashlin->bucket_bit > TOMMY_HASHLIN_BIT) {
188 /* if we are stable, setup a new shrink state */
189 /* otherwise continue with the already setup grow one */
190 /* but in backward direction */
191 if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) {
192 /* set the lower size */
193 hashlin->low_max = hashlin->bucket_max / 2;
194 hashlin->low_mask = hashlin->bucket_mask / 2;
196 /* start from the half going backward */
197 hashlin->split = hashlin->low_max;
200 /* start reallocation */
201 hashlin->state = TOMMY_HASHLIN_STATE_SHRINK;
205 /* if we are shrinking */
206 if (hashlin->state == TOMMY_HASHLIN_STATE_SHRINK) {
207 /* compute the split target required to finish the reallocation before the next resize */
208 tommy_size_t split_target = 8 * hashlin->count;
210 /* reallocate buckets until the split target */
211 while (hashlin->split + hashlin->low_max > split_target) {
212 tommy_hashlin_node** split[2];
214 /* go backward position */
215 --hashlin->split;
217 /* get the low bucket */
218 split[0] = tommy_hashlin_pos(hashlin, hashlin->split);
220 /* get the high bucket */
221 split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max);
223 /* concat the high bucket into the low one */
224 tommy_list_concat(split[0], split[1]);
226 /* if we have finished, clean up and change the state */
227 if (hashlin->split == 0) {
228 tommy_hashlin_node** segment;
230 /* shrink the hash size */
231 --hashlin->bucket_bit;
232 hashlin->bucket_max = 1 << hashlin->bucket_bit;
233 hashlin->bucket_mask = hashlin->bucket_max - 1;
235 /* free the last segment */
236 segment = hashlin->bucket[hashlin->bucket_bit];
237 tommy_free(&segment[((tommy_ptrdiff_t)1) << hashlin->bucket_bit]);
239 /* go in stable mode */
240 tommy_hashlin_stable(hashlin);
241 break;
247 void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash)
249 tommy_list_insert_tail(tommy_hashlin_bucket_ref(hashlin, hash), node, data);
251 node->index = hash;
253 ++hashlin->count;
255 hashlin_grow_step(hashlin);
258 void tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node)
260 tommy_list_remove_existing(tommy_hashlin_bucket_ref(hashlin, node->index), node);
262 --hashlin->count;
264 hashlin_shrink_step(hashlin);
267 void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash)
269 tommy_hashlin_node** let_ptr = tommy_hashlin_bucket_ref(hashlin, hash);
270 tommy_hashlin_node* node = *let_ptr;
272 while (node) {
273 /* we first check if the hash matches, as in the same bucket we may have multiples hash values */
274 if (node->index == hash && cmp(cmp_arg, node->data) == 0) {
275 tommy_list_remove_existing(let_ptr, node);
277 --hashlin->count;
279 hashlin_shrink_step(hashlin);
281 return node->data;
283 node = node->next;
286 return 0;
289 void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func)
291 tommy_size_t bucket_max;
292 tommy_size_t pos;
294 /* number of valid buckets */
295 bucket_max = hashlin->low_max + hashlin->split;
297 for (pos = 0; pos < bucket_max; ++pos) {
298 tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos);
300 while (node) {
301 void* data = node->data;
302 node = node->next;
303 func(data);
308 void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg)
310 tommy_size_t bucket_max;
311 tommy_size_t pos;
313 /* number of valid buckets */
314 bucket_max = hashlin->low_max + hashlin->split;
316 for (pos = 0; pos < bucket_max; ++pos) {
317 tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos);
319 while (node) {
320 void* data = node->data;
321 node = node->next;
322 func(arg, data);
327 tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin)
329 return hashlin->bucket_max * (tommy_size_t)sizeof(hashlin->bucket[0][0])
330 + hashlin->count * (tommy_size_t)sizeof(tommy_hashlin_node);