/src/open5gs/lib/core/ogs-hash.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Copyright (C) 2019-2020 by Sukchan Lee <acetcom@gmail.com> |
19 | | * |
20 | | * This file is part of Open5GS. |
21 | | * |
22 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
23 | | * you may not use this file except in compliance with the License. |
24 | | * You may obtain a copy of the License at |
25 | | * |
26 | | * http://www.apache.org/licenses/LICENSE-2.0 |
27 | | * |
28 | | * Unless required by applicable law or agreed to in writing, software |
29 | | * distributed under the License is distributed on an "AS IS" BASIS, |
30 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
31 | | * See the License for the specific language governing permissions and |
32 | | * limitations under the License. |
33 | | */ |
34 | | |
35 | | #include "ogs-core.h" |
36 | | |
37 | | typedef struct ogs_hash_entry_t ogs_hash_entry_t; |
38 | | struct ogs_hash_entry_t { |
39 | | ogs_hash_entry_t *next; |
40 | | unsigned int hash; |
41 | | const void *key; |
42 | | int klen; |
43 | | const void *val; |
44 | | }; |
45 | | |
46 | | struct ogs_hash_index_t { |
47 | | ogs_hash_t *ht; |
48 | | ogs_hash_entry_t *this, *next; |
49 | | unsigned int index; |
50 | | }; |
51 | | |
52 | | struct ogs_hash_t { |
53 | | ogs_hash_entry_t **array; |
54 | | ogs_hash_index_t iterator; /* For ogs_hash_first(NULL, ...) */ |
55 | | unsigned int count, max, seed; |
56 | | ogs_hashfunc_t hash_func; |
57 | | ogs_hash_entry_t *free; /* List of recycled entries */ |
58 | | }; |
59 | | |
60 | 6 | #define INITIAL_MAX 15 /* tunable == 2^n - 1 */ |
61 | | |
62 | | static ogs_hash_entry_t **alloc_array(ogs_hash_t *ht, unsigned int max) |
63 | 6 | { |
64 | 6 | ogs_hash_entry_t **ptr = ogs_calloc(1, sizeof(*ht->array) * (max + 1)); |
65 | 6 | ogs_assert(ptr); |
66 | 6 | return ptr; |
67 | 6 | } |
68 | | |
69 | | ogs_hash_t *ogs_hash_make(void) |
70 | 6 | { |
71 | 6 | ogs_hash_t *ht; |
72 | 6 | ogs_time_t now = ogs_get_monotonic_time(); |
73 | | |
74 | 6 | ht = ogs_malloc(sizeof(ogs_hash_t)); |
75 | 6 | if (!ht) { |
76 | 0 | ogs_error("ogs_malloc() failed"); |
77 | 0 | return NULL; |
78 | 0 | } |
79 | | |
80 | 6 | ht->free = NULL; |
81 | 6 | ht->count = 0; |
82 | 6 | ht->max = INITIAL_MAX; |
83 | 6 | ht->seed = (unsigned int)((now >> 32) ^ now ^ |
84 | 6 | (uintptr_t)ht ^ (uintptr_t)&now) - 1; |
85 | 6 | ht->array = alloc_array(ht, ht->max); |
86 | 6 | ht->hash_func = NULL; |
87 | | |
88 | 6 | return ht; |
89 | 6 | } |
90 | | |
91 | | ogs_hash_t *ogs_hash_make_custom(ogs_hashfunc_t hash_func) |
92 | 0 | { |
93 | 0 | ogs_hash_t *ht = ogs_hash_make(); |
94 | 0 | if (!ht) { |
95 | 0 | ogs_error("ogs_hash_make() failed"); |
96 | 0 | return NULL; |
97 | 0 | } |
98 | 0 | ht->hash_func = hash_func; |
99 | 0 | return ht; |
100 | 0 | } |
101 | | |
102 | | void ogs_hash_destroy(ogs_hash_t *ht) |
103 | 0 | { |
104 | 0 | ogs_hash_entry_t *he = NULL, *next_he = NULL; |
105 | |
|
106 | 0 | ogs_assert(ht); |
107 | 0 | ogs_assert(ht->array); |
108 | | |
109 | 0 | ogs_hash_clear(ht); |
110 | |
|
111 | 0 | he = ht->free; |
112 | 0 | while(he) { |
113 | 0 | next_he = he->next; |
114 | |
|
115 | 0 | ogs_free(he); |
116 | 0 | he = next_he; |
117 | 0 | } |
118 | |
|
119 | 0 | ogs_free(ht->array); |
120 | 0 | ogs_free(ht); |
121 | 0 | } |
122 | | |
123 | | ogs_hash_index_t *ogs_hash_next(ogs_hash_index_t *hi) |
124 | 0 | { |
125 | 0 | ogs_assert(hi); |
126 | | |
127 | 0 | hi->this = hi->next; |
128 | 0 | while (!hi->this) { |
129 | 0 | if (hi->index > hi->ht->max) |
130 | 0 | return NULL; |
131 | | |
132 | 0 | hi->this = hi->ht->array[hi->index++]; |
133 | 0 | } |
134 | 0 | hi->next = hi->this->next; |
135 | 0 | return hi; |
136 | 0 | } |
137 | | |
138 | | ogs_hash_index_t *ogs_hash_first(ogs_hash_t *ht) |
139 | 0 | { |
140 | 0 | ogs_hash_index_t *hi; |
141 | |
|
142 | 0 | ogs_assert(ht); |
143 | | |
144 | 0 | hi = &ht->iterator; |
145 | |
|
146 | 0 | hi->ht = ht; |
147 | 0 | hi->index = 0; |
148 | 0 | hi->this = NULL; |
149 | 0 | hi->next = NULL; |
150 | 0 | return ogs_hash_next(hi); |
151 | 0 | } |
152 | | |
153 | | void ogs_hash_this(ogs_hash_index_t *hi, |
154 | | const void **key, int *klen, void **val) |
155 | 0 | { |
156 | 0 | ogs_assert(hi); |
157 | | |
158 | 0 | if (key) *key = hi->this->key; |
159 | 0 | if (klen) *klen = hi->this->klen; |
160 | 0 | if (val) *val = (void *)hi->this->val; |
161 | 0 | } |
162 | | |
163 | | const void *ogs_hash_this_key(ogs_hash_index_t *hi) |
164 | 0 | { |
165 | 0 | const void *key; |
166 | |
|
167 | 0 | ogs_hash_this(hi, &key, NULL, NULL); |
168 | 0 | return key; |
169 | 0 | } |
170 | | |
171 | | int ogs_hash_this_key_len(ogs_hash_index_t *hi) |
172 | 0 | { |
173 | 0 | int klen; |
174 | |
|
175 | 0 | ogs_hash_this(hi, NULL, &klen, NULL); |
176 | 0 | return klen; |
177 | 0 | } |
178 | | |
179 | | void *ogs_hash_this_val(ogs_hash_index_t *hi) |
180 | 0 | { |
181 | 0 | void *val; |
182 | |
|
183 | 0 | ogs_hash_this(hi, NULL, NULL, &val); |
184 | 0 | return val; |
185 | 0 | } |
186 | | |
187 | | static void expand_array(ogs_hash_t *ht) |
188 | 0 | { |
189 | 0 | ogs_hash_index_t *hi; |
190 | 0 | ogs_hash_entry_t **new_array; |
191 | 0 | unsigned int new_max; |
192 | |
|
193 | 0 | new_max = ht->max * 2 + 1; |
194 | 0 | new_array = alloc_array(ht, new_max); |
195 | 0 | for (hi = ogs_hash_first(ht); hi; hi = ogs_hash_next(hi)) { |
196 | 0 | unsigned int i = hi->this->hash & new_max; |
197 | 0 | hi->this->next = new_array[i]; |
198 | 0 | new_array[i] = hi->this; |
199 | 0 | } |
200 | 0 | ogs_free(ht->array); |
201 | 0 | ht->array = new_array; |
202 | 0 | ht->max = new_max; |
203 | 0 | } |
204 | | |
205 | | static unsigned int hashfunc_default( |
206 | | const char *char_key, int *klen, unsigned int hash) |
207 | 0 | { |
208 | 0 | const unsigned char *key = (const unsigned char *)char_key; |
209 | 0 | const unsigned char *p; |
210 | 0 | int i; |
211 | | |
212 | | /* |
213 | | * This is the popular `times 33' hash algorithm which is used by |
214 | | * perl and also appears in Berkeley DB. This is one of the best |
215 | | * known hash functions for strings because it is both computed |
216 | | * very fast and distributes very well. |
217 | | * |
218 | | * The originator may be Dan Bernstein but the code in Berkeley DB |
219 | | * cites Chris Torek as the source. The best citation I have found |
220 | | * is "Chris Torek, Hash function for text in C, Usenet message |
221 | | * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich |
222 | | * Salz's USENIX 1992 paper about INN which can be found at |
223 | | * <http://citeseer.nj.nec.com/salz92internetnews.html>. |
224 | | * |
225 | | * The magic of number 33, i.e. why it works better than many other |
226 | | * constants, prime or not, has never been adequately explained by |
227 | | * anyone. So I try an explanation: if one experimentally tests all |
228 | | * multipliers between 1 and 256 (as I did while writing a low-level |
229 | | * data structure library some time ago) one detects that even |
230 | | * numbers are not useable at all. The remaining 128 odd numbers |
231 | | * (except for the number 1) work more or less all equally well. |
232 | | * They all distribute in an acceptable way and this way fill a hash |
233 | | * table with an average percent of approx. 86%. |
234 | | * |
235 | | * If one compares the chi^2 values of the variants (see |
236 | | * Bob Jenkins ``Hashing Frequently Asked Questions'' at |
237 | | * http://burtleburtle.net/bob/hash/hashfaq.html for a description |
238 | | * of chi^2), the number 33 not even has the best value. But the |
239 | | * number 33 and a few other equally good numbers like 17, 31, 63, |
240 | | * 127 and 129 have nevertheless a great advantage to the remaining |
241 | | * numbers in the large set of possible multipliers: their multiply |
242 | | * operation can be replaced by a faster operation based on just one |
243 | | * shift plus either a single addition or subtraction operation. And |
244 | | * because a hash function has to both distribute good _and_ has to |
245 | | * be very fast to compute, those few numbers should be preferred. |
246 | | * |
247 | | * -- Ralf S. Engelschall <rse@engelschall.com> |
248 | | */ |
249 | |
|
250 | 0 | if (*klen == OGS_HASH_KEY_STRING) { |
251 | 0 | for (p = key; *p; p++) { |
252 | 0 | hash = hash * 33 + *p; |
253 | 0 | } |
254 | 0 | *klen = p - key; |
255 | 0 | } |
256 | 0 | else { |
257 | 0 | for (p = key, i = *klen; i; i--, p++) { |
258 | 0 | hash = hash * 33 + *p; |
259 | 0 | } |
260 | 0 | } |
261 | |
|
262 | 0 | return hash; |
263 | 0 | } |
264 | | |
265 | | unsigned int ogs_hashfunc_default(const char *char_key, int *klen) |
266 | 0 | { |
267 | 0 | return hashfunc_default(char_key, klen, 0); |
268 | 0 | } |
269 | | |
270 | | static ogs_hash_entry_t **find_entry(ogs_hash_t *ht, |
271 | | const void *key, int klen, const void *val, const char *file_line) |
272 | 0 | { |
273 | 0 | ogs_hash_entry_t **hep, *he; |
274 | 0 | unsigned int hash; |
275 | |
|
276 | 0 | if (ht->hash_func) |
277 | 0 | hash = ht->hash_func(key, &klen); |
278 | 0 | else |
279 | 0 | hash = hashfunc_default(key, &klen, ht->seed); |
280 | | |
281 | | /* scan linked list */ |
282 | 0 | for (hep = &ht->array[hash & ht->max], he = *hep; |
283 | 0 | he; hep = &he->next, he = *hep) { |
284 | 0 | if (he->hash == hash |
285 | 0 | && he->klen == klen |
286 | 0 | && memcmp(he->key, key, klen) == 0) |
287 | 0 | break; |
288 | 0 | } |
289 | 0 | if (he || !val) |
290 | 0 | return hep; |
291 | | |
292 | | /* add a new entry for non-NULL values */ |
293 | 0 | if ((he = ht->free) != NULL) |
294 | 0 | ht->free = he->next; |
295 | 0 | else { |
296 | 0 | he = ogs_malloc(sizeof(*he)); |
297 | 0 | ogs_assert(he); |
298 | 0 | } |
299 | 0 | he->next = NULL; |
300 | 0 | he->hash = hash; |
301 | 0 | he->key = key; |
302 | 0 | he->klen = klen; |
303 | 0 | he->val = val; |
304 | 0 | *hep = he; |
305 | 0 | ht->count++; |
306 | 0 | return hep; |
307 | 0 | } |
308 | | |
309 | | void *ogs_hash_get_debug(ogs_hash_t *ht, |
310 | | const void *key, int klen, const char *file_line) |
311 | 0 | { |
312 | 0 | ogs_hash_entry_t *he; |
313 | |
|
314 | 0 | ogs_assert(ht); |
315 | 0 | ogs_assert(key); |
316 | 0 | ogs_assert(klen); |
317 | | |
318 | 0 | he = *find_entry(ht, key, klen, NULL, file_line); |
319 | 0 | if (he) |
320 | 0 | return (void *)he->val; |
321 | 0 | else |
322 | 0 | return NULL; |
323 | 0 | } |
324 | | |
325 | | void ogs_hash_set_debug(ogs_hash_t *ht, |
326 | | const void *key, int klen, const void *val, const char *file_line) |
327 | 0 | { |
328 | 0 | ogs_hash_entry_t **hep; |
329 | |
|
330 | 0 | ogs_assert(ht); |
331 | 0 | ogs_assert(key); |
332 | 0 | ogs_assert(klen); |
333 | | |
334 | 0 | hep = find_entry(ht, key, klen, val, file_line); |
335 | 0 | if (*hep) { |
336 | 0 | if (!val) { |
337 | | /* delete entry */ |
338 | 0 | ogs_hash_entry_t *old = *hep; |
339 | 0 | *hep = (*hep)->next; |
340 | 0 | old->next = ht->free; |
341 | 0 | ht->free = old; |
342 | 0 | --ht->count; |
343 | 0 | } else { |
344 | | /* replace entry */ |
345 | 0 | (*hep)->val = val; |
346 | | /* check that the collision rate isn't too high */ |
347 | 0 | if (ht->count > ht->max) { |
348 | 0 | expand_array(ht); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | /* else key not present and val==NULL */ |
353 | 0 | } |
354 | | |
355 | | void *ogs_hash_get_or_set_debug(ogs_hash_t *ht, |
356 | | const void *key, int klen, const void *val, const char *file_line) |
357 | 0 | { |
358 | 0 | ogs_hash_entry_t **hep; |
359 | |
|
360 | 0 | ogs_assert(ht); |
361 | 0 | ogs_assert(key); |
362 | 0 | ogs_assert(klen); |
363 | | |
364 | 0 | hep = find_entry(ht, key, klen, val, file_line); |
365 | 0 | if (*hep) { |
366 | 0 | val = (*hep)->val; |
367 | | /* check that the collision rate isn't too high */ |
368 | 0 | if (ht->count > ht->max) { |
369 | 0 | expand_array(ht); |
370 | 0 | } |
371 | 0 | return (void *)val; |
372 | 0 | } |
373 | | /* else key not present and val==NULL */ |
374 | 0 | return NULL; |
375 | 0 | } |
376 | | |
377 | | unsigned int ogs_hash_count(ogs_hash_t *ht) |
378 | 0 | { |
379 | 0 | ogs_assert(ht); |
380 | 0 | return ht->count; |
381 | 0 | } |
382 | | |
383 | | void ogs_hash_clear(ogs_hash_t *ht) |
384 | 0 | { |
385 | 0 | ogs_hash_index_t *hi; |
386 | |
|
387 | 0 | ogs_assert(ht); |
388 | | |
389 | 0 | for (hi = ogs_hash_first(ht); hi; hi = ogs_hash_next(hi)) |
390 | 0 | ogs_hash_set(ht, hi->this->key, hi->this->klen, NULL); |
391 | 0 | } |
392 | | |
393 | | /* This is basically the following... |
394 | | * for every element in hash table { |
395 | | * comp elemeny.key, element.value |
396 | | * } |
397 | | * |
398 | | * Like with table_do, the comp callback is called for each and every |
399 | | * element of the hash table. |
400 | | */ |
401 | | int ogs_hash_do(ogs_hash_do_callback_fn_t *comp, |
402 | | void *rec, const ogs_hash_t *ht) |
403 | 0 | { |
404 | 0 | ogs_hash_index_t hix; |
405 | 0 | ogs_hash_index_t *hi; |
406 | 0 | int rv, dorv = 1; |
407 | |
|
408 | 0 | hix.ht = (ogs_hash_t *)ht; |
409 | 0 | hix.index = 0; |
410 | 0 | hix.this = NULL; |
411 | 0 | hix.next = NULL; |
412 | |
|
413 | 0 | if ((hi = ogs_hash_next(&hix))) { |
414 | | /* Scan the entire table */ |
415 | 0 | do { |
416 | 0 | rv = (*comp)(rec, hi->this->key, hi->this->klen, hi->this->val); |
417 | 0 | } while (rv && (hi = ogs_hash_next(hi))); |
418 | |
|
419 | 0 | if (rv == 0) { |
420 | 0 | dorv = 0; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | return dorv; |
424 | 0 | } |