Coverage Report

Created: 2025-07-18 07:02

/src/bind9/lib/isc/ht.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <inttypes.h>
15
#include <string.h>
16
17
#include <isc/ascii.h>
18
#include <isc/hash.h>
19
#include <isc/ht.h>
20
#include <isc/magic.h>
21
#include <isc/mem.h>
22
#include <isc/result.h>
23
#include <isc/types.h>
24
#include <isc/util.h>
25
26
typedef struct isc_ht_node isc_ht_node_t;
27
28
0
#define ISC_HT_MAGIC   ISC_MAGIC('H', 'T', 'a', 'b')
29
#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
30
31
0
#define HT_NO_BITS    0
32
#define HT_MIN_BITS   1
33
0
#define HT_MAX_BITS   32
34
0
#define HT_OVERCOMMIT 3
35
36
0
#define HT_NEXTTABLE(idx)      ((idx == 0) ? 1 : 0)
37
0
#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
38
39
0
#define GOLDEN_RATIO_32 0x61C88647
40
41
0
#define HASHSIZE(bits) (UINT64_C(1) << (bits))
42
43
struct isc_ht_node {
44
  void *value;
45
  isc_ht_node_t *next;
46
  uint32_t hashval;
47
  size_t keysize;
48
  unsigned char key[];
49
};
50
51
struct isc_ht {
52
  unsigned int magic;
53
  isc_mem_t *mctx;
54
  size_t count;
55
  bool case_sensitive;
56
  size_t size[2];
57
  uint8_t hashbits[2];
58
  isc_ht_node_t **table[2];
59
  uint8_t hindex;
60
  uint32_t hiter; /* rehashing iterator */
61
};
62
63
struct isc_ht_iter {
64
  isc_ht_t *ht;
65
  size_t i;
66
  uint8_t hindex;
67
  isc_ht_node_t *cur;
68
};
69
70
static isc_ht_node_t *
71
isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
72
       const uint32_t keysize, const uint32_t hashval, const uint8_t idx);
73
static void
74
isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
75
      const uint32_t hashval, const uint8_t idx, void *value);
76
static isc_result_t
77
isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
78
         const uint32_t hashval, const uint8_t idx);
79
80
static uint32_t
81
rehash_bits(isc_ht_t *ht, size_t newcount);
82
83
static void
84
hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits);
85
static void
86
hashtable_free(isc_ht_t *ht, const uint8_t idx);
87
static void
88
hashtable_rehash(isc_ht_t *ht, uint32_t newbits);
89
static void
90
hashtable_rehash_one(isc_ht_t *ht);
91
static void
92
maybe_rehash(isc_ht_t *ht, size_t newcount);
93
94
static isc_result_t
95
isc__ht_iter_next(isc_ht_iter_t *it);
96
97
static bool
98
isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval,
99
0
       const uint8_t *key, uint32_t keysize, bool case_sensitive) {
100
0
  return node->hashval == hashval && node->keysize == keysize &&
101
0
         (case_sensitive
102
0
      ? (memcmp(node->key, key, keysize) == 0)
103
0
      : (isc_ascii_lowerequal(node->key, key, keysize)));
104
0
}
105
106
static uint32_t
107
0
hash_32(uint32_t val, unsigned int bits) {
108
0
  REQUIRE(bits <= HT_MAX_BITS);
109
  /* High bits are more random. */
110
0
  return val * GOLDEN_RATIO_32 >> (32 - bits);
111
0
}
112
113
static bool
114
0
rehashing_in_progress(const isc_ht_t *ht) {
115
0
  return ht->table[HT_NEXTTABLE(ht->hindex)] != NULL;
116
0
}
117
118
static bool
119
0
hashtable_is_overcommited(isc_ht_t *ht) {
120
0
  return ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT);
121
0
}
122
123
static uint32_t
124
0
rehash_bits(isc_ht_t *ht, size_t newcount) {
125
0
  uint32_t newbits = ht->hashbits[ht->hindex];
126
127
0
  while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
128
0
    newbits += 1;
129
0
  }
130
131
0
  return newbits;
132
0
}
133
134
/*
135
 * Rebuild the hashtable to reduce the load factor
136
 */
137
static void
138
0
hashtable_rehash(isc_ht_t *ht, uint32_t newbits) {
139
0
  uint8_t oldindex = ht->hindex;
140
0
  uint32_t oldbits = ht->hashbits[oldindex];
141
0
  uint8_t newindex = HT_NEXTTABLE(oldindex);
142
143
0
  REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
144
0
  REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
145
0
  REQUIRE(ht->table[oldindex] != NULL);
146
147
0
  REQUIRE(newbits <= HT_MAX_BITS);
148
0
  REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
149
0
  REQUIRE(ht->table[newindex] == NULL);
150
151
0
  REQUIRE(newbits > oldbits);
152
153
0
  hashtable_new(ht, newindex, newbits);
154
155
0
  ht->hindex = newindex;
156
157
0
  hashtable_rehash_one(ht);
158
0
}
159
160
static void
161
0
hashtable_rehash_one(isc_ht_t *ht) {
162
0
  isc_ht_node_t **newtable = ht->table[ht->hindex];
163
0
  uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
164
0
  isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
165
0
  isc_ht_node_t *node = NULL;
166
0
  isc_ht_node_t *nextnode;
167
168
  /* Find first non-empty node */
169
0
  while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
170
0
    ht->hiter++;
171
0
  }
172
173
  /* Rehashing complete */
174
0
  if (ht->hiter == oldsize) {
175
0
    hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
176
0
    ht->hiter = 0;
177
0
    return;
178
0
  }
179
180
  /* Move the first non-empty node from old hashtable to new hashtable */
181
0
  for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
182
0
    uint32_t hash = hash_32(node->hashval,
183
0
          ht->hashbits[ht->hindex]);
184
0
    nextnode = node->next;
185
0
    node->next = newtable[hash];
186
0
    newtable[hash] = node;
187
0
  }
188
189
0
  oldtable[ht->hiter] = NULL;
190
191
0
  ht->hiter++;
192
0
}
193
194
static void
195
0
maybe_rehash(isc_ht_t *ht, size_t newcount) {
196
0
  uint32_t newbits = rehash_bits(ht, newcount);
197
198
0
  if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
199
0
    hashtable_rehash(ht, newbits);
200
0
  }
201
0
}
202
203
static void
204
0
hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) {
205
0
  REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
206
0
  REQUIRE(ht->table[idx] == NULL);
207
0
  REQUIRE(bits >= HT_MIN_BITS);
208
0
  REQUIRE(bits <= HT_MAX_BITS);
209
210
0
  ht->hashbits[idx] = bits;
211
0
  ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
212
213
0
  ht->table[idx] = isc_mem_cget(ht->mctx, ht->size[idx],
214
0
              sizeof(isc_ht_node_t *));
215
0
}
216
217
static void
218
0
hashtable_free(isc_ht_t *ht, const uint8_t idx) {
219
0
  for (size_t i = 0; i < ht->size[idx]; i++) {
220
0
    isc_ht_node_t *node = ht->table[idx][i];
221
0
    while (node != NULL) {
222
0
      isc_ht_node_t *next = node->next;
223
0
      ht->count--;
224
0
      isc_mem_put(ht->mctx, node,
225
0
            sizeof(*node) + node->keysize);
226
0
      node = next;
227
0
    }
228
0
  }
229
230
0
  isc_mem_cput(ht->mctx, ht->table[idx], ht->size[idx],
231
0
         sizeof(isc_ht_node_t *));
232
233
0
  ht->hashbits[idx] = HT_NO_BITS;
234
0
  ht->table[idx] = NULL;
235
0
}
236
237
void
238
isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
239
0
      unsigned int options) {
240
0
  isc_ht_t *ht = NULL;
241
0
  bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
242
243
0
  REQUIRE(htp != NULL && *htp == NULL);
244
0
  REQUIRE(mctx != NULL);
245
0
  REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
246
247
0
  ht = isc_mem_get(mctx, sizeof(*ht));
248
0
  *ht = (isc_ht_t){
249
0
    .case_sensitive = case_sensitive,
250
0
  };
251
252
0
  isc_mem_attach(mctx, &ht->mctx);
253
254
0
  hashtable_new(ht, 0, bits);
255
256
0
  ht->magic = ISC_HT_MAGIC;
257
258
0
  *htp = ht;
259
0
}
260
261
void
262
0
isc_ht_destroy(isc_ht_t **htp) {
263
0
  isc_ht_t *ht;
264
265
0
  REQUIRE(htp != NULL);
266
0
  REQUIRE(ISC_HT_VALID(*htp));
267
268
0
  ht = *htp;
269
0
  *htp = NULL;
270
0
  ht->magic = 0;
271
272
0
  for (size_t i = 0; i <= 1; i++) {
273
0
    if (ht->table[i] != NULL) {
274
0
      hashtable_free(ht, i);
275
0
    }
276
0
  }
277
278
0
  INSIST(ht->count == 0);
279
280
0
  isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
281
0
}
282
283
static void
284
isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
285
0
      const uint32_t hashval, const uint8_t idx, void *value) {
286
0
  isc_ht_node_t *node;
287
0
  uint32_t hash;
288
289
0
  hash = hash_32(hashval, ht->hashbits[idx]);
290
291
0
  node = isc_mem_get(ht->mctx, STRUCT_FLEX_SIZE(node, key, keysize));
292
0
  *node = (isc_ht_node_t){
293
0
    .keysize = keysize,
294
0
    .hashval = hashval,
295
0
    .next = ht->table[idx][hash],
296
0
    .value = value,
297
0
  };
298
299
0
  memmove(node->key, key, keysize);
300
301
0
  ht->count++;
302
0
  ht->table[idx][hash] = node;
303
0
}
304
305
isc_result_t
306
isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
307
0
     void *value) {
308
0
  uint32_t hashval;
309
310
0
  REQUIRE(ISC_HT_VALID(ht));
311
0
  REQUIRE(key != NULL && keysize > 0);
312
313
0
  if (rehashing_in_progress(ht)) {
314
    /* Rehash in progress */
315
0
    hashtable_rehash_one(ht);
316
0
  } else if (hashtable_is_overcommited(ht)) {
317
    /* Rehash requested */
318
0
    maybe_rehash(ht, ht->count);
319
0
  }
320
321
0
  hashval = isc_hash32(key, keysize, ht->case_sensitive);
322
323
0
  if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
324
0
    return ISC_R_EXISTS;
325
0
  }
326
327
0
  isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
328
329
0
  return ISC_R_SUCCESS;
330
0
}
331
332
static isc_ht_node_t *
333
isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
334
       const uint32_t keysize, const uint32_t hashval,
335
0
       const uint8_t idx) {
336
0
  uint32_t hash;
337
0
  uint8_t findex = idx;
338
339
0
nexttable:
340
0
  hash = hash_32(hashval, ht->hashbits[findex]);
341
0
  for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL;
342
0
       node = node->next)
343
0
  {
344
0
    if (isc__ht_node_match(node, hashval, key, keysize,
345
0
               ht->case_sensitive))
346
0
    {
347
0
      return node;
348
0
    }
349
0
  }
350
0
  if (TRY_NEXTTABLE(findex, ht)) {
351
    /*
352
     * Rehashing in progress, check the other table
353
     */
354
0
    findex = HT_NEXTTABLE(findex);
355
0
    goto nexttable;
356
0
  }
357
358
0
  return NULL;
359
0
}
360
361
isc_result_t
362
isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
363
0
      const uint32_t keysize, void **valuep) {
364
0
  uint32_t hashval;
365
0
  isc_ht_node_t *node;
366
367
0
  REQUIRE(ISC_HT_VALID(ht));
368
0
  REQUIRE(key != NULL && keysize > 0);
369
0
  REQUIRE(valuep == NULL || *valuep == NULL);
370
371
0
  hashval = isc_hash32(key, keysize, ht->case_sensitive);
372
373
0
  node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
374
0
  if (node == NULL) {
375
0
    return ISC_R_NOTFOUND;
376
0
  }
377
378
0
  SET_IF_NOT_NULL(valuep, node->value);
379
0
  return ISC_R_SUCCESS;
380
0
}
381
382
static isc_result_t
383
isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
384
0
         const uint32_t hashval, const uint8_t idx) {
385
0
  isc_ht_node_t *prev = NULL;
386
0
  uint32_t hash;
387
388
0
  hash = hash_32(hashval, ht->hashbits[idx]);
389
390
0
  for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL;
391
0
       prev = node, node = node->next)
392
0
  {
393
0
    if (isc__ht_node_match(node, hashval, key, keysize,
394
0
               ht->case_sensitive))
395
0
    {
396
0
      if (prev == NULL) {
397
0
        ht->table[idx][hash] = node->next;
398
0
      } else {
399
0
        prev->next = node->next;
400
0
      }
401
0
      isc_mem_put(ht->mctx, node,
402
0
            STRUCT_FLEX_SIZE(node, key, node->keysize));
403
0
      ht->count--;
404
405
0
      return ISC_R_SUCCESS;
406
0
    }
407
0
  }
408
409
0
  return ISC_R_NOTFOUND;
410
0
}
411
412
isc_result_t
413
0
isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) {
414
0
  uint32_t hashval;
415
0
  uint8_t hindex;
416
0
  isc_result_t result;
417
418
0
  REQUIRE(ISC_HT_VALID(ht));
419
0
  REQUIRE(key != NULL && keysize > 0);
420
421
0
  if (rehashing_in_progress(ht)) {
422
    /* Rehash in progress */
423
0
    hashtable_rehash_one(ht);
424
0
  }
425
426
0
  hindex = ht->hindex;
427
0
  hashval = isc_hash32(key, keysize, ht->case_sensitive);
428
0
nexttable:
429
0
  result = isc__ht_delete(ht, key, keysize, hashval, hindex);
430
431
0
  if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
432
    /*
433
     * Rehashing in progress, check the other table
434
     */
435
0
    hindex = HT_NEXTTABLE(hindex);
436
0
    goto nexttable;
437
0
  }
438
439
0
  return result;
440
0
}
441
442
void
443
0
isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
444
0
  isc_ht_iter_t *it;
445
446
0
  REQUIRE(ISC_HT_VALID(ht));
447
0
  REQUIRE(itp != NULL && *itp == NULL);
448
449
0
  it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
450
0
  *it = (isc_ht_iter_t){
451
0
    .ht = ht,
452
0
    .hindex = ht->hindex,
453
0
  };
454
455
0
  *itp = it;
456
0
}
457
458
void
459
0
isc_ht_iter_destroy(isc_ht_iter_t **itp) {
460
0
  isc_ht_iter_t *it;
461
0
  isc_ht_t *ht;
462
463
0
  REQUIRE(itp != NULL && *itp != NULL);
464
465
0
  it = *itp;
466
0
  *itp = NULL;
467
0
  ht = it->ht;
468
0
  isc_mem_put(ht->mctx, it, sizeof(*it));
469
0
}
470
471
isc_result_t
472
0
isc_ht_iter_first(isc_ht_iter_t *it) {
473
0
  isc_ht_t *ht;
474
475
0
  REQUIRE(it != NULL);
476
477
0
  ht = it->ht;
478
479
0
  it->hindex = ht->hindex;
480
0
  it->i = 0;
481
482
0
  return isc__ht_iter_next(it);
483
0
}
484
485
static isc_result_t
486
0
isc__ht_iter_next(isc_ht_iter_t *it) {
487
0
  isc_ht_t *ht = it->ht;
488
489
0
  while (it->i < ht->size[it->hindex] &&
490
0
         ht->table[it->hindex][it->i] == NULL)
491
0
  {
492
0
    it->i++;
493
0
  }
494
495
0
  if (it->i < ht->size[it->hindex]) {
496
0
    it->cur = ht->table[it->hindex][it->i];
497
498
0
    return ISC_R_SUCCESS;
499
0
  }
500
501
0
  if (TRY_NEXTTABLE(it->hindex, ht)) {
502
0
    it->hindex = HT_NEXTTABLE(it->hindex);
503
0
    it->i = 0;
504
0
    return isc__ht_iter_next(it);
505
0
  }
506
507
0
  return ISC_R_NOMORE;
508
0
}
509
510
isc_result_t
511
0
isc_ht_iter_next(isc_ht_iter_t *it) {
512
0
  REQUIRE(it != NULL);
513
0
  REQUIRE(it->cur != NULL);
514
515
0
  it->cur = it->cur->next;
516
517
0
  if (it->cur != NULL) {
518
0
    return ISC_R_SUCCESS;
519
0
  }
520
521
0
  it->i++;
522
523
0
  return isc__ht_iter_next(it);
524
0
}
525
526
isc_result_t
527
0
isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
528
0
  isc_result_t result = ISC_R_SUCCESS;
529
0
  isc_ht_node_t *dnode = NULL;
530
0
  uint8_t dindex;
531
0
  isc_ht_t *ht;
532
0
  isc_result_t dresult;
533
534
0
  REQUIRE(it != NULL);
535
0
  REQUIRE(it->cur != NULL);
536
537
0
  ht = it->ht;
538
0
  dnode = it->cur;
539
0
  dindex = it->hindex;
540
541
0
  result = isc_ht_iter_next(it);
542
543
0
  dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
544
0
         dindex);
545
0
  INSIST(dresult == ISC_R_SUCCESS);
546
547
0
  return result;
548
0
}
549
550
void
551
0
isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
552
0
  REQUIRE(it != NULL);
553
0
  REQUIRE(it->cur != NULL);
554
0
  REQUIRE(valuep != NULL && *valuep == NULL);
555
556
0
  *valuep = it->cur->value;
557
0
}
558
559
void
560
isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
561
0
           size_t *keysize) {
562
0
  REQUIRE(it != NULL);
563
0
  REQUIRE(it->cur != NULL);
564
0
  REQUIRE(key != NULL && *key == NULL);
565
566
0
  *key = it->cur->key;
567
0
  *keysize = it->cur->keysize;
568
0
}
569
570
size_t
571
0
isc_ht_count(const isc_ht_t *ht) {
572
0
  REQUIRE(ISC_HT_VALID(ht));
573
574
0
  return ht->count;
575
0
}