Coverage Report

Created: 2025-08-29 07:10

/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
}