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