Coverage Report

Created: 2026-11-06 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/core/ogs-hash.c
Line
Count
Source
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
/*
36
 * Robin Hood insertion, backward-shift deletion and reverse iteration
37
 * follow PostgreSQL src/include/lib/simplehash.h. PostgreSQL notice:
38
 *
39
 * PostgreSQL Database Management System
40
 * (also known as Postgres, formerly known as Postgres95)
41
 *
42
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
43
 *
44
 * Portions Copyright (c) 1994, The Regents of the University of California
45
 *
46
 * Permission to use, copy, modify, and distribute this software and its
47
 * documentation for any purpose, without fee, and without a written agreement
48
 * is hereby granted, provided that the above copyright notice and this
49
 * paragraph and the following two paragraphs appear in all copies.
50
 *
51
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
52
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
53
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
54
 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
55
 * POSSIBILITY OF SUCH DAMAGE.
56
 *
57
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
58
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
59
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
60
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
61
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
62
 */
63
64
/*
65
 * SipHash-2-4 adapted from https://github.com/veorq/SipHash, using its
66
 * MIT license option. Original implementation:
67
 * Copyright (c) 2012-2022 Jean-Philippe Aumasson
68
 * Copyright (c) 2012-2014 Daniel J. Bernstein
69
 * Upstream MIT notice:
70
 *
71
 * Copyright 2012-2024 JP Aumasson
72
 *
73
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
74
 *
75
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
76
 *
77
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
78
 */
79
80
#include "ogs-core.h"
81
82
#include <limits.h>
83
84
275k
#define KEY_INLINE_SIZE 24
85
106k
#define INITIAL_CAPACITY 16
86
87
typedef struct ogs_hash_slot_t ogs_hash_slot_t;
88
struct ogs_hash_slot_t {
89
    const void          *val;   /* NULL marks an empty slot */
90
    uint32_t            hash;
91
    int                 klen;
92
    union {
93
        char            bytes[KEY_INLINE_SIZE];
94
        char            *ptr;
95
    } key;
96
};
97
98
struct ogs_hash_index_t {
99
    ogs_hash_t          *ht;
100
    unsigned int        index;
101
    unsigned int        next;
102
    unsigned int        remaining;
103
};
104
105
struct ogs_hash_t {
106
    ogs_hash_slot_t     *slots;
107
    ogs_hash_index_t    iterator;
108
    unsigned int        count;
109
    unsigned int        capacity;
110
};
111
112
/* Initialized once, before publishing any table; lookups need no lock. */
113
static uint8_t hash_secret[16];
114
115
#ifdef _WIN32
116
static INIT_ONCE hash_once = INIT_ONCE_STATIC_INIT;
117
118
static BOOL CALLBACK init_hash_secret(
119
        PINIT_ONCE once, PVOID parameter, PVOID *context)
120
{
121
    ogs_random(hash_secret, sizeof(hash_secret));
122
    return TRUE;
123
}
124
#else
125
static pthread_once_t hash_once = PTHREAD_ONCE_INIT;
126
127
static void init_hash_secret(void)
128
13
{
129
13
    ogs_random(hash_secret, sizeof(hash_secret));
130
13
}
131
#endif
132
133
static uint64_t load64_le(const uint8_t *p)
134
236k
{
135
236k
    return (uint64_t)p[0] | ((uint64_t)p[1] << 8) |
136
236k
        ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) |
137
236k
        ((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) |
138
236k
        ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56);
139
236k
}
140
141
static uint64_t rotl64(uint64_t v, unsigned int bits)
142
3.74M
{
143
3.74M
    return (v << bits) | (v >> (64 - bits));
144
3.74M
}
145
146
624k
#define HASH_SIPROUND() do { \
147
624k
    v0 += v1; v1 = rotl64(v1, 13); v1 ^= v0; v0 = rotl64(v0, 32); \
148
624k
    v2 += v3; v3 = rotl64(v3, 16); v3 ^= v2; \
149
624k
    v0 += v3; v3 = rotl64(v3, 21); v3 ^= v0; \
150
624k
    v2 += v1; v1 = rotl64(v1, 17); v1 ^= v2; v2 = rotl64(v2, 32); \
151
624k
} while (0)
152
153
/* Byte loads make both unaligned keys and host endianness immaterial. */
154
static uint64_t siphash24(
155
        const void *key, size_t length, const uint8_t secret[16])
156
75.8k
{
157
75.8k
    const uint8_t *p = key;
158
75.8k
    uint64_t k0 = load64_le(secret);
159
75.8k
    uint64_t k1 = load64_le(secret + 8);
160
75.8k
    uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0;
161
75.8k
    uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1;
162
75.8k
    uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0;
163
75.8k
    uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
164
75.8k
    uint64_t last = (uint64_t)(length & 0xff) << 56;
165
75.8k
    size_t remaining = length;
166
75.8k
    unsigned int i;
167
168
160k
    while (remaining >= 8) {
169
84.4k
        uint64_t m = load64_le(p);
170
171
84.4k
        v3 ^= m;
172
84.4k
        HASH_SIPROUND();
173
84.4k
        HASH_SIPROUND();
174
84.4k
        v0 ^= m;
175
84.4k
        p += 8;
176
84.4k
        remaining -= 8;
177
84.4k
    }
178
365k
    for (i = 0; i < remaining; i++)
179
289k
        last |= (uint64_t)p[i] << (8 * i);
180
181
75.8k
    v3 ^= last;
182
75.8k
    HASH_SIPROUND();
183
75.8k
    HASH_SIPROUND();
184
75.8k
    v0 ^= last;
185
75.8k
    v2 ^= 0xff;
186
75.8k
    HASH_SIPROUND();
187
75.8k
    HASH_SIPROUND();
188
75.8k
    HASH_SIPROUND();
189
75.8k
    HASH_SIPROUND();
190
75.8k
    return v0 ^ v1 ^ v2 ^ v3;
191
75.8k
}
192
193
#undef HASH_SIPROUND
194
195
static uint32_t hash_key(const void *key, int *klen)
196
75.8k
{
197
75.8k
    if (*klen == OGS_HASH_KEY_STRING) {
198
0
        size_t length = strlen(key);
199
200
0
        ogs_assert(length <= INT_MAX);
201
0
        *klen = (int)length;
202
0
    }
203
75.8k
    return (uint32_t)siphash24(key, (size_t)*klen, hash_secret);
204
75.8k
}
205
206
static const void *slot_key(const ogs_hash_slot_t *slot)
207
155k
{
208
155k
    if (slot->klen < KEY_INLINE_SIZE)
209
151k
        return slot->key.bytes;
210
3.98k
    return slot->key.ptr;
211
155k
}
212
213
static void free_key(ogs_hash_slot_t *slot)
214
60.1k
{
215
60.1k
    if (slot->klen >= KEY_INLINE_SIZE)
216
420
        ogs_free(slot->key.ptr);
217
60.1k
}
218
219
static void prepare_slot(ogs_hash_slot_t *slot,
220
        const void *key, int klen, const void *val, uint32_t hash)
221
60.1k
{
222
60.1k
    char *copy;
223
224
60.1k
    memset(slot, 0, sizeof(*slot));
225
60.1k
    slot->hash = hash;
226
60.1k
    slot->klen = klen;
227
60.1k
    if (klen < KEY_INLINE_SIZE)
228
59.7k
        copy = slot->key.bytes;
229
420
    else {
230
420
        copy = ogs_malloc((size_t)klen + 1);
231
420
        ogs_assert(copy);
232
420
        slot->key.ptr = copy;
233
420
    }
234
60.1k
    memcpy(copy, key, klen);
235
60.1k
    copy[klen] = '\0';
236
60.1k
    slot->val = val;
237
60.1k
}
238
239
static unsigned int probe_distance(
240
        uint32_t hash, unsigned int index, unsigned int mask)
241
20.6k
{
242
20.6k
    return (index - (hash & mask)) & mask;
243
20.6k
}
244
245
/* A resident closer to home proves this key cannot occur further on. */
246
static ogs_hash_slot_t *find_slot(
247
        ogs_hash_t *ht, const void *key, int klen, uint32_t hash)
248
75.8k
{
249
75.8k
    unsigned int index, distance = 0, mask;
250
251
75.8k
    if (!ht->count)
252
53.3k
        return NULL;
253
254
22.5k
    mask = ht->capacity - 1;
255
22.5k
    index = hash & mask;
256
29.0k
    for (;;) {
257
29.0k
        ogs_hash_slot_t *slot = &ht->slots[index];
258
259
29.0k
        if (!slot->val ||
260
16.3k
                probe_distance(slot->hash, index, mask) < distance)
261
13.6k
            return NULL;
262
15.3k
        if (slot->hash == hash && slot->klen == klen &&
263
8.85k
                memcmp(slot_key(slot), key, klen) == 0)
264
8.85k
            return slot;
265
266
6.47k
        index = (index + 1) & mask;
267
6.47k
        distance++;
268
6.47k
    }
269
22.5k
}
270
271
/*
272
 * The caller has already excluded duplicates and left an empty slot.
273
 * Swapping whole slots transfers long-key pointers without copying or
274
 * freeing their allocations; inline keys travel with the slot itself.
275
 */
276
static void insert_slot(ogs_hash_slot_t *slots,
277
        unsigned int capacity, ogs_hash_slot_t entry)
278
61.8k
{
279
61.8k
    unsigned int mask = capacity - 1;
280
61.8k
    unsigned int index = entry.hash & mask;
281
61.8k
    unsigned int distance = 0;
282
283
66.1k
    for (;;) {
284
66.1k
        ogs_hash_slot_t *slot = &slots[index];
285
66.1k
        unsigned int resident_distance;
286
287
66.1k
        if (!slot->val) {
288
61.8k
            *slot = entry;
289
61.8k
            return;
290
61.8k
        }
291
4.32k
        resident_distance = probe_distance(slot->hash, index, mask);
292
4.32k
        if (resident_distance < distance) {
293
1.51k
            ogs_hash_slot_t displaced = *slot;
294
295
1.51k
            *slot = entry;
296
1.51k
            entry = displaced;
297
1.51k
            distance = resident_distance;
298
1.51k
        }
299
4.32k
        index = (index + 1) & mask;
300
4.32k
        distance++;
301
4.32k
    }
302
61.8k
}
303
304
static void grow_slots(ogs_hash_t *ht)
305
53.3k
{
306
53.3k
    ogs_hash_slot_t *slots;
307
53.3k
    unsigned int capacity, i;
308
309
53.3k
    ogs_assert(ht->capacity <= UINT_MAX / 2);
310
53.3k
    capacity = ht->capacity ? ht->capacity * 2 : INITIAL_CAPACITY;
311
53.3k
    ogs_assert(sizeof(*slots) <= SIZE_MAX / (size_t)capacity);
312
53.3k
    slots = ogs_calloc((size_t)capacity, sizeof(*slots));
313
53.3k
    ogs_assert(slots);
314
315
55.2k
    for (i = 0; i < ht->capacity; i++) {
316
1.90k
        if (ht->slots[i].val)
317
1.67k
            insert_slot(slots, capacity, ht->slots[i]);
318
1.90k
    }
319
53.3k
    if (ht->slots)
320
77
        ogs_free(ht->slots);
321
53.3k
    ht->slots = slots;
322
53.3k
    ht->capacity = capacity;
323
53.3k
}
324
325
static void add_entry(ogs_hash_t *ht,
326
        const void *key, int klen, const void *val, uint32_t hash)
327
60.1k
{
328
60.1k
    ogs_hash_slot_t entry;
329
330
    /*
331
     * key may point into the old array, or be a prefix of a stored key.
332
     * Finish its independent copy before growth or Robin Hood swaps.
333
     */
334
60.1k
    prepare_slot(&entry, key, klen, val, hash);
335
60.1k
    if (!ht->capacity ||
336
6.83k
            ht->count >= (uint64_t)ht->capacity * 9 / 10)
337
53.3k
        grow_slots(ht);
338
60.1k
    insert_slot(ht->slots, ht->capacity, entry);
339
60.1k
    ht->count++;
340
60.1k
}
341
342
static void remove_slot(ogs_hash_t *ht, ogs_hash_slot_t *slot)
343
0
{
344
0
    unsigned int mask = ht->capacity - 1;
345
0
    unsigned int index = (unsigned int)(slot - ht->slots);
346
347
0
    free_key(slot);
348
0
    for (;;) {
349
0
        unsigned int next = (index + 1) & mask;
350
351
        /*
352
         * This stop-at-home rule relies on Robin Hood insertion.
353
         * A plain linear-probing table would need a different test.
354
         */
355
0
        if (!ht->slots[next].val ||
356
0
                !probe_distance(ht->slots[next].hash, next, mask))
357
0
            break;
358
0
        ht->slots[index] = ht->slots[next];
359
0
        index = next;
360
0
    }
361
0
    memset(&ht->slots[index], 0, sizeof(ht->slots[index]));
362
0
    ht->count--;
363
0
}
364
365
ogs_hash_t *ogs_hash_make(void)
366
107k
{
367
107k
    ogs_hash_t *ht;
368
369
#ifdef _WIN32
370
    BOOL initialized = InitOnceExecuteOnce(
371
            &hash_once, init_hash_secret, NULL, NULL);
372
    ogs_assert(initialized);
373
#else
374
107k
    int rv = pthread_once(&hash_once, init_hash_secret);
375
107k
    ogs_assert(rv == 0);
376
107k
#endif
377
378
107k
    ht = ogs_calloc(1, sizeof(*ht));
379
107k
    if (!ht)
380
107k
        ogs_error("ogs_calloc() failed");
381
107k
    return ht;
382
107k
}
383
384
void ogs_hash_clear(ogs_hash_t *ht)
385
107k
{
386
107k
    unsigned int i;
387
388
107k
    ogs_assert(ht);
389
107k
    if (!ht->count)
390
53.6k
        return;
391
908k
    for (i = 0; i < ht->capacity; i++) {
392
854k
        if (ht->slots[i].val)
393
60.1k
            free_key(&ht->slots[i]);
394
854k
    }
395
53.3k
    memset(ht->slots, 0, (size_t)ht->capacity * sizeof(*ht->slots));
396
53.3k
    ht->count = 0;
397
53.3k
}
398
399
void ogs_hash_destroy(ogs_hash_t *ht)
400
107k
{
401
107k
    ogs_assert(ht);
402
107k
    ogs_hash_clear(ht);
403
107k
    if (ht->slots)
404
53.3k
        ogs_free(ht->slots);
405
107k
    ogs_free(ht);
406
107k
}
407
408
void *ogs_hash_get_debug(ogs_hash_t *ht,
409
        const void *key, int klen, const char *file_line)
410
64.5k
{
411
64.5k
    ogs_hash_slot_t *slot;
412
64.5k
    uint32_t hash;
413
414
64.5k
    ogs_assert(ht);
415
64.5k
    ogs_assert(key);
416
64.5k
    ogs_assert(klen == OGS_HASH_KEY_STRING || klen >= 0);
417
418
64.5k
    if (!ht->count)
419
53.3k
        return NULL;
420
11.2k
    hash = hash_key(key, &klen);
421
11.2k
    slot = find_slot(ht, key, klen, hash);
422
11.2k
    return slot ? (void *)slot->val : NULL;
423
64.5k
}
424
425
void ogs_hash_set_debug(ogs_hash_t *ht,
426
        const void *key, int klen, const void *val, const char *file_line)
427
64.5k
{
428
64.5k
    ogs_hash_slot_t *slot;
429
64.5k
    uint32_t hash;
430
431
64.5k
    ogs_assert(ht);
432
64.5k
    ogs_assert(key);
433
64.5k
    ogs_assert(klen == OGS_HASH_KEY_STRING || klen >= 0);
434
435
64.5k
    if (!ht->count && !val)
436
0
        return;
437
64.5k
    hash = hash_key(key, &klen);
438
64.5k
    slot = find_slot(ht, key, klen, hash);
439
64.5k
    if (slot) {
440
4.42k
        if (val)
441
4.42k
            slot->val = val;
442
0
        else
443
0
            remove_slot(ht, slot);
444
60.1k
    } else if (val)
445
60.1k
        add_entry(ht, key, klen, val, hash);
446
64.5k
}
447
448
void *ogs_hash_get_or_set_debug(ogs_hash_t *ht,
449
        const void *key, int klen, const void *val, const char *file_line)
450
0
{
451
0
    ogs_hash_slot_t *slot;
452
0
    uint32_t hash;
453
454
0
    ogs_assert(ht);
455
0
    ogs_assert(key);
456
0
    ogs_assert(klen == OGS_HASH_KEY_STRING || klen >= 0);
457
458
0
    if (!ht->count && !val)
459
0
        return NULL;
460
0
    hash = hash_key(key, &klen);
461
0
    slot = find_slot(ht, key, klen, hash);
462
0
    if (slot)
463
0
        return (void *)slot->val;
464
0
    if (val)
465
0
        add_entry(ht, key, klen, val, hash);
466
0
    return (void *)val;
467
0
}
468
469
bool ogs_hash_unset_if_owner_debug(ogs_hash_t *ht,
470
        const void *key, int klen, const void *owner, const char *file_line)
471
0
{
472
0
    ogs_hash_slot_t *slot;
473
0
    uint32_t hash;
474
475
0
    ogs_assert(ht);
476
0
    ogs_assert(key);
477
0
    ogs_assert(klen == OGS_HASH_KEY_STRING || klen >= 0);
478
0
    ogs_assert(owner);
479
480
0
    hash = hash_key(key, &klen);
481
0
    slot = find_slot(ht, key, klen, hash);
482
0
    if (slot && slot->val == owner) {
483
0
        remove_slot(ht, slot);
484
0
        return true;
485
0
    }
486
0
    ogs_warn("%s: hash unset skipped: entry not owned by expected context",
487
0
            file_line);
488
0
    return false;
489
0
}
490
491
unsigned int ogs_hash_count(ogs_hash_t *ht)
492
0
{
493
0
    ogs_assert(ht);
494
0
    return ht->count;
495
0
}
496
497
static void init_iterator(ogs_hash_t *ht, ogs_hash_index_t *hi)
498
179k
{
499
179k
    unsigned int boundary;
500
501
179k
    memset(hi, 0, sizeof(*hi));
502
179k
    hi->ht = ht;
503
179k
    if (!ht->count)
504
73.4k
        return;
505
506
    /* There is always an empty slot at the configured maximum load. */
507
107k
    for (boundary = 0; ht->slots[boundary].val; boundary++)
508
1.03k
        ;
509
106k
    hi->next = boundary;
510
106k
    hi->remaining = ht->capacity;
511
106k
}
512
513
ogs_hash_index_t *ogs_hash_next(ogs_hash_index_t *hi)
514
299k
{
515
299k
    ogs_assert(hi);
516
517
1.88M
    while (hi->remaining) {
518
1.70M
        unsigned int index = hi->next;
519
520
        /* Save the next position before the caller can delete current. */
521
1.70M
        hi->next = (index - 1) & (hi->ht->capacity - 1);
522
1.70M
        hi->remaining--;
523
1.70M
        if (hi->ht->slots[index].val) {
524
119k
            hi->index = index;
525
119k
            return hi;
526
119k
        }
527
1.70M
    }
528
179k
    return NULL;
529
299k
}
530
531
ogs_hash_index_t *ogs_hash_first(ogs_hash_t *ht)
532
179k
{
533
179k
    ogs_assert(ht);
534
179k
    init_iterator(ht, &ht->iterator);
535
179k
    return ogs_hash_next(&ht->iterator);
536
179k
}
537
538
void ogs_hash_this(ogs_hash_index_t *hi,
539
        const void **key, int *klen, void **val)
540
258k
{
541
258k
    ogs_hash_slot_t *slot;
542
543
258k
    ogs_assert(hi);
544
258k
    ogs_assert(hi->ht);
545
258k
    ogs_assert(hi->index < hi->ht->capacity);
546
258k
    slot = &hi->ht->slots[hi->index];
547
258k
    ogs_assert(slot->val);
548
549
258k
    if (key)
550
146k
        *key = slot_key(slot);
551
258k
    if (klen)
552
0
        *klen = slot->klen;
553
258k
    if (val)
554
111k
        *val = (void *)slot->val;
555
258k
}
556
557
const void *ogs_hash_this_key(ogs_hash_index_t *hi)
558
146k
{
559
146k
    const void *key;
560
561
146k
    ogs_hash_this(hi, &key, NULL, NULL);
562
146k
    return key;
563
146k
}
564
565
int ogs_hash_this_key_len(ogs_hash_index_t *hi)
566
0
{
567
0
    int klen;
568
569
0
    ogs_hash_this(hi, NULL, &klen, NULL);
570
0
    return klen;
571
0
}
572
573
void *ogs_hash_this_val(ogs_hash_index_t *hi)
574
111k
{
575
111k
    void *val;
576
577
111k
    ogs_hash_this(hi, NULL, NULL, &val);
578
111k
    return val;
579
111k
}
580
581
int ogs_hash_do(ogs_hash_do_callback_fn_t *comp,
582
        void *rec, const ogs_hash_t *ht)
583
0
{
584
0
    ogs_hash_index_t hi;
585
586
0
    ogs_assert(comp);
587
0
    ogs_assert(ht);
588
0
    init_iterator((ogs_hash_t *)ht, &hi);
589
0
    while (ogs_hash_next(&hi)) {
590
0
        ogs_hash_slot_t *slot = &hi.ht->slots[hi.index];
591
592
0
        if (!comp(rec, slot_key(slot), slot->klen, slot->val))
593
0
            return 0;
594
0
    }
595
0
    return 1;
596
0
}