ftwin 0.8.10
napr_hash.c
Go to the documentation of this file.
1
6/*
7 * Copyright (C) 2007 François Pesce : francois.pesce (at) gmail (dot) com
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22#include "debug.h"
23#include "napr_hash.h"
24
25#define XXH_STATIC_LINKING_ONLY
26#include "xxhash.h"
27
28#define hashsize(n) ((apr_size_t)1<<(n))
29#define hashmask(n) (hashsize(n)-1)
30
31static const void *str_get_key(const void *opaque)
32{
33 return opaque;
34}
35
36static apr_size_t str_get_key_len(const void *opaque)
37{
38 const char *str = opaque;
39 return strlen(str);
40}
41
42static int str_key_cmp(const void *opaque1, const void *opaque2, apr_size_t len)
43{
44 const char *str1 = opaque1;
45 const char *str2 = opaque2;
46
47 return memcmp(str1, str2, len);
48}
49
50static apr_uint32_t str_hash(register const void *opaque, register apr_size_t len)
51{
52 /* Use XXH32 for string hashing - faster and more consistent with file hashing */
53 return XXH32(opaque, len, 0x1337cafe);
54}
55
57{
58 napr_hash_t *hash;
59 apr_size_t bucket;
60 apr_size_t element; /* of a bucket */
61};
62
63struct napr_hash_t
64{
65 /* void *table[size][ffactor] */
66 void ***table;
67 /* apr_size_t table[size] contain the number of element for each bucket */
68 apr_size_t *filling_table;
69 /* parent pool */
70 apr_pool_t *pool;
71 /* own pool that will be cleaned if a grow of the table occured */
72 apr_pool_t *own_pool;
73 /* Function to get the key from the datum */
74 get_key_callback_fn_t *get_key;
75 /* Function to get the key len from the datum */
76 get_key_len_callback_fn_t *get_key_len;
77 /* Function to compare two keys */
78 key_cmp_callback_fn_t *key_cmp;
79 /* hash function */
81
82 /* the number of element contained in all the buckets of the table */
83 apr_size_t nel;
84 /* the number of buckets */
85 apr_size_t size;
86 /* desired density */
87 apr_size_t ffactor;
88 /* the binary mask to apply to the result of a hash function that return a
89 * number < size */
90 apr_uint32_t mask;
91 /* size of the hash is hashsize(power) as mask is hashmask(power) */
92 unsigned char power;
93};
94
95extern napr_hash_t *napr_hash_str_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor)
96{
97 return napr_hash_make(pool, nel, ffactor, str_get_key, str_get_key_len, str_key_cmp, str_hash);
98}
99
100extern napr_hash_t *napr_hash_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor, get_key_callback_fn_t get_key,
103{
104 napr_hash_t *result;
105 apr_status_t status;
106
107 if (NULL == (result = apr_pcalloc(pool, sizeof(struct napr_hash_t)))) {
108 DEBUG_ERR("allocation error");
109 return NULL;
110 }
111
112 while (hashsize(result->power) < nel)
113 result->power++;
114
115 result->size = hashsize(result->power);
116 result->mask = hashmask(result->power);
117 result->ffactor = ffactor;
118 result->get_key = get_key;
119 result->get_key_len = get_key_len;
120 result->key_cmp = key_cmp;
121 result->hash = hash;
122 result->pool = pool;
123
124 if (APR_SUCCESS != (status = apr_pool_create(&(result->own_pool), pool))) {
125 char errbuf[128];
126 DEBUG_ERR("error calling apr_pool_create: %s", apr_strerror(status, errbuf, 128));
127 return NULL;
128 }
129 /*DEBUG_DBG("readjusting size to %" APR_SIZE_T_FMT " to store %" APR_SIZE_T_FMT " elements", result->size, nel); */
130 /*DEBUG_DBG("bit mask will be 0x%x", result->mask); */
131
132 if (NULL == (result->table = apr_pcalloc(result->own_pool, result->size * sizeof(void **)))) {
133 DEBUG_ERR("allocation error");
134 return NULL;
135 }
136
137 if (NULL == (result->filling_table = apr_pcalloc(result->own_pool, result->size * sizeof(apr_size_t)))) {
138 DEBUG_ERR("allocation error");
139 return NULL;
140 }
141
142 return result;
143}
144
145extern void *napr_hash_search(napr_hash_t *hash, const void *key, apr_size_t key_len, apr_uint32_t *hash_value)
146{
147 apr_uint32_t key_hash;
148 apr_size_t i, nel, bucket;
149
150 key_hash = hash->hash(key, key_len);
151
152 if (NULL != hash_value)
153 *hash_value = key_hash;
154
155 bucket = key_hash & hash->mask;
156 if (0 != (nel = hash->filling_table[bucket])) {
157 for (i = 0; i < nel; i++) {
158 /*DEBUG_DBG( "key[%p] bucket[%"APR_SIZE_T_FMT"][%"APR_SIZE_T_FMT"]=[%p]", key, bucket, i, hash->get_key(hash->table[bucket][i])); */
159 if (key_len == hash->get_key_len(hash->table[bucket][i]))
160 if (0 == (hash->key_cmp(key, hash->get_key(hash->table[bucket][i]), key_len)))
161 return hash->table[bucket][i];
162 }
163 }
164
165 return NULL;
166}
167
168static inline apr_status_t napr_hash_rebuild(napr_hash_t *hash)
169{
170 napr_hash_t *tmp;
171 apr_size_t i, j;
172 apr_status_t status;
173
174 if (NULL ==
175 (tmp =
176 napr_hash_make(hash->pool, hashsize(hash->power + 1), hash->ffactor, hash->get_key, hash->get_key_len,
177 hash->key_cmp, hash->hash))) {
178 DEBUG_ERR("error calling napr_hash_init");
179 return APR_EGENERAL;
180 }
181
182 for (i = 0; i < hash->size; i++) {
183 for (j = 0; j < hash->filling_table[i]; j++) {
184 /*
185 * no need to do doublon test here as we take data directly from a
186 * hash table
187 */
188 if (APR_SUCCESS !=
189 (status =
190 napr_hash_set(tmp, hash->table[i][j],
191 hash->hash(hash->get_key(hash->table[i][j]), hash->get_key_len(hash->table[i][j]))))) {
192 char errbuf[128];
193 DEBUG_ERR("error calling napr_hash_set: %s", apr_strerror(status, errbuf, 128));
194 return status;
195 }
196 }
197 }
198 hash->table = tmp->table;
199 hash->filling_table = tmp->filling_table;
200 hash->nel = tmp->nel;
201 hash->size = tmp->size;
202 hash->mask = tmp->mask;
203 hash->power = tmp->power;
204 apr_pool_destroy(hash->own_pool);
205 hash->own_pool = tmp->own_pool;
206
207 return APR_SUCCESS;
208}
209
210extern void napr_hash_remove(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
211{
212 apr_size_t nel, bucket, i, key_len;
213 const void *key;
214
215 bucket = hash_value & hash->mask;
216 key = hash->get_key(data);
217 key_len = hash->get_key_len(data);
218 if (0 != (nel = hash->filling_table[bucket])) {
219 for (i = 0; i < nel; i++) {
220 //DEBUG_DBG( "key[%.*s] bucket[%i]=[%.*s]", key_len, key, i, hash->get_key_len(hash->table[bucket][i]), hash->get_key(hash->table[bucket][i]));
221 if (key_len == hash->get_key_len(hash->table[bucket][i]))
222 if (0 == (hash->key_cmp(key, hash->get_key(hash->table[bucket][i]), key_len))) {
223 /* Remove it, by replacing with the last element if present */
224 if (i != nel - 1) {
225 hash->table[bucket][i] = hash->table[bucket][nel - 1];
226 hash->table[bucket][nel - 1] = NULL;
227 }
228 else {
229 hash->table[bucket][i] = NULL;
230 }
231 hash->filling_table[bucket]--;
232 hash->nel--;
233 break;
234 }
235 }
236 }
237 else {
238 DEBUG_DBG("try to remove something that is not here");
239 }
240}
241
242extern apr_status_t napr_hash_set(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
243{
244 apr_size_t nel, bucket;
245 apr_status_t status;
246
247 bucket = hash_value & hash->mask;
248
249 if ((0 == (nel = hash->filling_table[bucket])) && (NULL == hash->table[bucket])) {
250 hash->table[bucket] = (void **) apr_pcalloc(hash->own_pool, hash->ffactor * sizeof(void *));
251 }
252 // DEBUG_DBG( "set data %.*s in bucket %u at nel %u", hash->datum_get_key_len(data), hash->datum_get_key(data), bucket, nel);
253 hash->table[bucket][nel] = data;
254 hash->filling_table[bucket]++;
255 hash->nel++;
256
257 if (hash->ffactor <= hash->filling_table[bucket]) {
258 // int i;
259 // DEBUG_DBG( "rebuilding hash table, because there's %u element in bucket %u ffactor is %u (total element = %u)",
260 // hash->filling_table[bucket], bucket, hash->ffactor, hash->nel);
261 // for(i = 0; i < hash->ffactor; i++) {
262 // DEBUG_DBG( "%.*s", hash->datum_get_key_len(hash->table[bucket][i]), hash->datum_get_key(hash->table[bucket][i]));
263 // }
264
265 if (APR_SUCCESS != (status = napr_hash_rebuild(hash))) {
266 char errbuf[128];
267 DEBUG_ERR("error calling napr_hash_rebuild: %s", apr_strerror(status, errbuf, 128));
268 return status;
269 }
270 }
271
272 return APR_SUCCESS;
273}
274
275apr_pool_t *napr_hash_pool_get(const napr_hash_t *thehash)
276{
277 return thehash->pool;
278}
279
280napr_hash_index_t *napr_hash_first(apr_pool_t *pool, napr_hash_t *hash)
281{
282 napr_hash_index_t *hash_index;
283 hash_index = apr_palloc(pool, sizeof(struct napr_hash_index_t));
284 hash_index->hash = hash;
285 hash_index->bucket = 0;
286 hash_index->element = 0;
287
288 if (hash->filling_table[0] > 0)
289 return hash_index;
290
291 return napr_hash_next(hash_index);
292}
293
294napr_hash_index_t *napr_hash_next(napr_hash_index_t *index)
295{
296 if ((0 != index->hash->filling_table[index->bucket])
297 && (index->element < (index->hash->filling_table[index->bucket] - 1))) {
298 index->element++;
299 return index;
300 }
301 else {
302 index->element = 0;
303 for (index->bucket += 1; index->bucket < index->hash->size; index->bucket++) {
304 if (0 != index->hash->filling_table[index->bucket])
305 break;
306 }
307 if (index->bucket < index->hash->size)
308 return index;
309 }
310
311 return NULL;
312}
313
314void napr_hash_this(napr_hash_index_t *hi, const void **key, apr_size_t *klen, void **val)
315{
316 if (key)
317 *key = hi->hash->get_key(hi->hash->table[hi->bucket][hi->element]);
318 if (klen)
319 *klen = hi->hash->get_key_len(hi->hash->table[hi->bucket][hi->element]);
320 if (val)
321 *val = hi->hash->table[hi->bucket][hi->element];
322}
UTIL debug output macros.
#define DEBUG_DBG(str, arg...)
Display error message at the level debug.
Definition debug.h:38
#define DEBUG_ERR(str, arg...)
Display error message at the level error.
Definition debug.h:31
XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32(const void *input, size_t length, XXH32_hash_t seed)
Calculates the 32-bit hash of input using xxHash32.
apr_pool_t * napr_hash_pool_get(const napr_hash_t *thehash)
Get a pointer to the pool from which the hash table was allocated.
Definition napr_hash.c:275
napr_hash_index_t * napr_hash_first(apr_pool_t *pool, napr_hash_t *hash)
Start iterating over the entries in a hash table.
Definition napr_hash.c:280
void napr_hash_this(napr_hash_index_t *hi, const void **key, apr_size_t *klen, void **val)
Get the current entry's details from the iteration state.
Definition napr_hash.c:314
napr_hash_t * napr_hash_str_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor)
Create a hash table optimized for storing C strings as keys.
Definition napr_hash.c:95
apr_status_t napr_hash_set(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
Inserts or updates an item in the hash table.
Definition napr_hash.c:242
void * napr_hash_search(napr_hash_t *hash, const void *key, apr_size_t key_len, apr_uint32_t *hash_value)
Searches the hash table for an item.
Definition napr_hash.c:145
napr_hash_index_t * napr_hash_next(napr_hash_index_t *index)
Continue iterating over the entries in a hash table.
Definition napr_hash.c:294
void napr_hash_remove(napr_hash_t *hash, void *data, apr_uint32_t hash_value)
Removes an item from the hash table.
Definition napr_hash.c:210
napr_hash_t * napr_hash_make(apr_pool_t *pool, apr_size_t nel, apr_size_t ffactor, get_key_callback_fn_t get_key, get_key_len_callback_fn_t get_key_len, key_cmp_callback_fn_t key_cmp, hash_callback_fn_t hash)
Create a hash table with custom key handling and hashing functions.
Definition napr_hash.c:100
A high-performance hash table implementation built on APR.
int() key_cmp_callback_fn_t(const void *, const void *, apr_size_t)
Callback function to compare two keys.
Definition napr_hash.h:60
struct napr_hash_index_t napr_hash_index_t
Opaque hash table iterator structure.
Definition napr_hash.h:36
apr_uint32_t() hash_callback_fn_t(register const void *, register apr_size_t)
Callback function to compute the hash value of a key.
Definition napr_hash.h:68
apr_size_t() get_key_len_callback_fn_t(const void *)
Callback function to get the length of a key.
Definition napr_hash.h:50
const void *() get_key_callback_fn_t(const void *)
Callback function to extract the key from a stored data item.
Definition napr_hash.h:43
struct napr_hash_t napr_hash_t
Opaque hash table structure.
Definition napr_hash.h:31