Coverage Report

Created: 2026-06-09 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/qp_p.h
Line
Count
Source
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
/*
15
 * For an overview, see doc/design/qp-trie.md
16
 *
17
 * This private header defines the internal data structures,
18
 */
19
20
#pragma once
21
22
#include <isc/bit.h>
23
#include <isc/refcount.h>
24
25
#include <dns/qp.h>
26
27
/***********************************************************************
28
 *
29
 *  interior node basics
30
 */
31
32
/*
33
 * A qp-trie node is almost always one of two types: branch or leaf.
34
 * (A third type is used only to anchor the root of a trie; see below.)
35
 *
36
 * A node contains a 64-bit word and a 32-bit word. In order to avoid
37
 * unwanted padding, they are declared as three 32-bit words; this keeps
38
 * the size down to 12 bytes. They are in native endian order, so getting
39
 * the 64-bit part should compile down to an unaligned load.
40
 *
41
 * The node type is identified by the least significant bits of the 64-bit
42
 * word.
43
 *
44
 * In a leaf node:
45
 * - The 64-bit word is used to store a pointer value. (Pointers must be
46
 *   word-aligned so the least significant bits are zero; those bits can
47
 *   then act as a node tag to indicate that this is a leaf. This
48
 *   requirement is enforced by the make_leaf() constructor.)
49
 * - The 32-bit word is used to store an integer value.  Both the
50
 *   pointer and integer values can be retrieved when looking up a key.
51
 *
52
 * In a branch node:
53
 * - The 64-bit word is subdivided into three portions: the least
54
 *   significant bits are the node type (for a branch, 0x1); the
55
 *   most significant 15 bits are an offset value into the key, and
56
 *   the 47 bits in the middle are a bitmap; see the documentation
57
 *   for the SHIFT_* enum below.
58
 * - The 32-bit word is a reference (dns_qpref_t) to the packed sparse
59
 *   vector of "twigs", i.e. child nodes. A branch node has at least
60
 *   two and at most 47 twigs. (The qp-trie update functions ensure that
61
 *   branches actually branch, i.e. a branch cannot have only one child.)
62
 *
63
 * A third node type, reader nodes, anchors the root of a trie.
64
 * A pair of reader nodes together contain a packed `dns_qpreader_t`.
65
 * See the section on "packed reader nodes" for details.
66
 */
67
struct dns_qpnode {
68
#if WORDS_BIGENDIAN
69
  uint32_t bighi, biglo, small;
70
#else
71
  uint32_t biglo, bighi, small;
72
#endif
73
};
74
75
/*
76
 * The possible values of the node type tag. Type tags must fit in two bits
77
 * for compatibility with 4-byte pointer alignment on 32-bit systems.
78
 */
79
enum {
80
  LEAF_TAG = 0, /* leaf node */
81
  BRANCH_TAG = 1, /* branch node */
82
  READER_TAG = 2, /* reader node */
83
  TAG_MASK = 3, /* mask covering tag bits */
84
};
85
86
/*
87
 * This code does not work on CPUs with large pointers, e.g. CHERI capability
88
 * architectures. When porting to that kind of machine, a `dns_qpnode` should
89
 * be just a `uintptr_t`; a leaf node will contain a single pointer, and a
90
 * branch node will fit in the same space with room to spare.
91
 */
92
STATIC_ASSERT(sizeof(void *) <= sizeof(uint64_t),
93
        "pointers must fit in 64 bits");
94
95
/*
96
 * The 64-bit word in a branch node is comprised of a node type tag, a
97
 * bitmap, and an offset into the key. It is called an "index word" because
98
 * it describes how to access the twigs vector (think "database index").
99
 * The following enum sets up the bit positions of these parts.
100
 *
101
 * The bitmap is just above the type tag. The `dns_qp_bits_for_byte[]` table
102
 * is used to fill in a key so that bit tests can work directly against the
103
 * index word without superfluous masking or shifting; we don't need to
104
 * mask out the bitmap before testing a bit, but we do need to mask the
105
 * bitmap before calling popcount.
106
 *
107
 * The byte offset into the key is at the top of the word, so that it
108
 * can be extracted with just a shift, with no masking needed.
109
 *
110
 * The names are SHIFT_thing because they are dns_qpshift_t values. (See
111
 * below for the various `qp_*` type declarations.)
112
 *
113
 * These values are relatively fixed in practice: SHIFT_NOBYTE needs
114
 * to leave space for the type tag, and the implementation of
115
 * `dns_qpkey_fromname()` depends on the bitmap being large enough.
116
 * The symbolic names avoid mystery numbers in the code.
117
 */
118
enum {
119
  SHIFT_NOBYTE = 2,  /* label separator has no byte value */
120
  SHIFT_BITMAP,    /* many bits here */
121
  SHIFT_OFFSET = 49, /* offset of byte in key */
122
};
123
124
/***********************************************************************
125
 *
126
 *  garbage collector tuning parameters
127
 */
128
129
/*
130
 * A "cell" is a location that can contain a `dns_qpnode_t`, and a "chunk"
131
 * is a moderately large array of cells. A big trie can occupy
132
 * multiple chunks. (Unlike other nodes, a trie's root node lives in
133
 * its `struct dns_qp` instead of being allocated in a cell.)
134
 *
135
 * The qp-trie allocator hands out space for twigs vectors. Allocations are
136
 * made sequentially from one of the chunks; this kind of "sequential
137
 * allocator" is also known as a "bump allocator", so in `struct dns_qp`
138
 * (see below) the allocation chunk is called `bump`.
139
 */
140
141
/*
142
 * Number of cells in a chunk is a power of 2, which must have space for
143
 * a full twigs vector (48 wide). When testing, use a much smaller chunk
144
 * size to make the allocator work harder.
145
 */
146
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
147
#define QP_CHUNK_LOG_MIN 7
148
1.68G
#define QP_CHUNK_LOG_MAX 7
149
#else
150
#define QP_CHUNK_LOG_MIN 3
151
#define QP_CHUNK_LOG_MAX 12
152
#endif
153
154
STATIC_ASSERT(2 <= QP_CHUNK_LOG_MIN && QP_CHUNK_LOG_MIN <= QP_CHUNK_LOG_MAX,
155
        "qp-trie min chunk size is unreasonable");
156
STATIC_ASSERT(6 <= QP_CHUNK_LOG_MAX && QP_CHUNK_LOG_MAX <= 20,
157
        "qp-trie max chunk size is unreasonable");
158
159
1.68G
#define QP_CHUNK_SIZE  (1U << QP_CHUNK_LOG_MAX)
160
#define QP_CHUNK_BYTES (QP_CHUNK_SIZE * sizeof(dns_qpnode_t))
161
162
STATIC_ASSERT(QP_SAFETY_MARGIN >= QP_CHUNK_BYTES,
163
        "qp-trie safety margin too small");
164
165
/*
166
 * We need a bitfield this big to count how much of a chunk is in use:
167
 * it needs to count from 0 up to and including `1 << QP_CHUNK_LOG_MAX`.
168
 */
169
#define QP_USAGE_BITS (QP_CHUNK_LOG_MAX + 1)
170
171
/*
172
 * A chunk needs to be compacted if it is less full than this threshold.
173
 * (12% overhead seems reasonable)
174
 */
175
5.67M
#define QP_MAX_FREE (QP_CHUNK_SIZE / 8)
176
5.67M
#define QP_MIN_USED (QP_CHUNK_SIZE - QP_MAX_FREE)
177
178
/*
179
 * Compact automatically when we pass this threshold: when there is a lot
180
 * of free space in absolute terms, and when we have freed more than half
181
 * of the space we allocated.
182
 *
183
 * The current compaction algorithm scans the whole trie, so it is important
184
 * to scale the threshold based on the size of the trie to avoid quadratic
185
 * behaviour. XXXFANF find an algorithm that scans less of the trie!
186
 *
187
 * During a modification transaction, when we copy-on-write some twigs we
188
 * count the old copy as "free", because they will be when the transaction
189
 * commits. But they cannot be recovered immediately so they are also
190
 * counted as on hold, and discounted when we decide whether to compact.
191
 */
192
#define QP_GC_HEURISTIC(qp, free) \
193
9.13M
  ((free) > QP_CHUNK_SIZE * 4 && (free) > (qp)->used_count / 2)
194
195
54.1k
#define QP_NEEDGC(qp) QP_GC_HEURISTIC(qp, (qp)->free_count)
196
9.07M
#define QP_AUTOGC(qp) QP_GC_HEURISTIC(qp, (qp)->free_count - (qp)->hold_count)
197
198
/*
199
 * The chunk base and usage arrays are resized geometically and start off
200
 * with two entries.
201
 */
202
20.9k
#define GROWTH_FACTOR(size) ((size) + (size) / 2 + 2)
203
204
/*
205
 * Constructors and accessors for dns_qpref_t values, defined here to show
206
 * how the dns_qpref_t, dns_qpchunk_t, dns_qpcell_t types relate to each other
207
 */
208
209
static inline dns_qpref_t
210
14.9M
make_ref(dns_qpchunk_t chunk, dns_qpcell_t cell) {
211
14.9M
  return QP_CHUNK_SIZE * chunk + cell;
212
14.9M
}
Unexecuted instantiation: dns_qp.c:make_ref
qp.c:make_ref
Line
Count
Source
210
14.9M
make_ref(dns_qpchunk_t chunk, dns_qpcell_t cell) {
211
14.9M
  return QP_CHUNK_SIZE * chunk + cell;
212
14.9M
}
Unexecuted instantiation: lib.c:make_ref
213
214
static inline dns_qpchunk_t
215
834M
ref_chunk(dns_qpref_t ref) {
216
834M
  return ref / QP_CHUNK_SIZE;
217
834M
}
Unexecuted instantiation: dns_qp.c:ref_chunk
qp.c:ref_chunk
Line
Count
Source
215
834M
ref_chunk(dns_qpref_t ref) {
216
834M
  return ref / QP_CHUNK_SIZE;
217
834M
}
Unexecuted instantiation: lib.c:ref_chunk
218
219
static inline dns_qpcell_t
220
817M
ref_cell(dns_qpref_t ref) {
221
817M
  return ref % QP_CHUNK_SIZE;
222
817M
}
Unexecuted instantiation: dns_qp.c:ref_cell
qp.c:ref_cell
Line
Count
Source
220
817M
ref_cell(dns_qpref_t ref) {
221
817M
  return ref % QP_CHUNK_SIZE;
222
817M
}
Unexecuted instantiation: lib.c:ref_cell
223
224
/*
225
 * We should not use the `root_ref` in an empty trie, so we set it
226
 * to a value that should trigger an obvious bug. See qp_init()
227
 * and get_root() below.
228
 */
229
20.1M
#define INVALID_REF ((dns_qpref_t)~0UL)
230
231
/***********************************************************************
232
 *
233
 *  chunk arrays
234
 */
235
236
/*
237
 * A `dns_qp_t` contains two arrays holding information about each chunk.
238
 *
239
 * The `base` array holds pointers to the base of each chunk.
240
 * The `usage` array hold the allocator's state for each chunk.
241
 *
242
 * The `base` array is used by the hot qp-trie traversal paths. It can
243
 * be shared by multiple versions of a trie, which are tracked with a
244
 * refcount. Old versions of the trie can retain old versions of the
245
 * `base` array.
246
 *
247
 * In multithreaded code, the `usage` array is only used when the
248
 * `dns_qpmulti_t` mutex is held, and there is only one version of
249
 * it in active use (maybe with a snapshot for rollback support).
250
 *
251
 * The two arrays are separate because they have rather different
252
 * access patterns, different lifetimes, and different element sizes.
253
 */
254
255
/*
256
 * For most purposes we don't need to know exactly which cells are
257
 * in use in a chunk, we only need to know how many of them there are.
258
 *
259
 * After we have finished allocating from a chunk, the `used` counter
260
 * is the size we need to know for shrinking the chunk and for
261
 * scanning it to detach leaf values before the chunk is free()d. The
262
 * `free` counter tells us when the chunk needs compacting and when it
263
 * has become empty.
264
 *
265
 * The `exists` flag allows the chunk scanning loops to look at the
266
 * usage array only.
267
 *
268
 * In multithreaded code, we mark chunks as `immutable` when a modify
269
 * transaction is opened. (We don't mark them immutable on commit,
270
 * because the old bump chunk must remain mutable between write
271
 * transactions, but it must become immutable when an update
272
 * transaction is opened.)
273
 *
274
 * There are a few flags used to mark which chunks are still needed by
275
 * snapshots after the chunks have passed their normal reclamation
276
 * phase.
277
 */
278
typedef struct qp_usage {
279
  /*% the allocation point, increases monotonically */
280
  dns_qpcell_t used : QP_USAGE_BITS;
281
  /*% the actual size of the allocation */
282
  dns_qpcell_t capacity : QP_USAGE_BITS;
283
  /*% count of nodes no longer needed, also monotonic */
284
  dns_qpcell_t free : QP_USAGE_BITS;
285
  /*% qp->base->ptr[chunk] != NULL */
286
  bool exists : 1;
287
  /*% is this chunk shared? [MT] */
288
  bool immutable : 1;
289
  /*% already subtracted from multi->*_count [MT] */
290
  bool discounted : 1;
291
  /*% is a snapshot using this chunk? [MT] */
292
  bool snapshot : 1;
293
  /*% tried to free it but a snapshot needs it [MT] */
294
  bool snapfree : 1;
295
  /*% for mark/sweep snapshot flag updates [MT] */
296
  bool snapmark : 1;
297
} qp_usage_t;
298
299
/*
300
 * The chunks are owned by the current version of the `base` array.
301
 * When the array is resized, the old version might still be in use by
302
 * concurrent readers, in which case it is free()d later when its
303
 * refcount drops to zero.
304
 *
305
 * A `dns_qpbase_t` counts references from `dns_qp_t` objects and
306
 * from packed readers, but not from `dns_qpread_t` nor from
307
 * `dns_qpsnap_t` objects. Refcount adjustments for `dns_qpread_t`
308
 * would wreck multicore scalability; instead we rely on RCU.
309
 *
310
 * The `usage` array determines when a chunk is no longer needed: old
311
 * chunk pointers in old `base` arrays are ignored. (They can become
312
 * dangling pointers to free memory, but they will never be
313
 * dereferenced.)
314
 *
315
 * We ensure that individual chunk base pointers remain immutable
316
 * after assignment, and they are not cleared until the chunk is
317
 * free()d, after all readers have departed. Slots can be reused, and
318
 * we allow transactions to fill or re-fill empty slots adjacent to
319
 * busy slots that are in use by readers.
320
 */
321
struct dns_qpbase {
322
  unsigned int magic;
323
  isc_refcount_t refcount;
324
  dns_qpnode_t *ptr[];
325
};
326
327
/*
328
 * Chunks that may be in use by readers are reclaimed asynchronously.
329
 * When a transaction commits, immutable chunks that are now empty are
330
 * listed in a `qp_rcuctx_t` structure and passed to `call_rcu()`.
331
 */
332
typedef struct qp_rcuctx {
333
  unsigned int magic;
334
  struct rcu_head rcu_head;
335
  isc_mem_t *mctx;
336
  dns_qpmulti_t *multi;
337
  ISC_LINK(struct qp_rcuctx) link;
338
  dns_qpchunk_t count;
339
  dns_qpchunk_t chunk[];
340
} qp_rcuctx_t;
341
342
/*
343
 * Returns true when the base array can be free()d.
344
 */
345
static inline bool
346
38.8k
qpbase_unref(dns_qpreadable_t qpr) {
347
38.8k
  dns_qpreader_t *qp = dns_qpreader(qpr);
348
38.8k
  return qp->base != NULL &&
349
38.8k
         isc_refcount_decrement(&qp->base->refcount) == 1;
350
38.8k
}
Unexecuted instantiation: dns_qp.c:qpbase_unref
qp.c:qpbase_unref
Line
Count
Source
346
38.8k
qpbase_unref(dns_qpreadable_t qpr) {
347
38.8k
  dns_qpreader_t *qp = dns_qpreader(qpr);
348
38.8k
  return qp->base != NULL &&
349
38.8k
         isc_refcount_decrement(&qp->base->refcount) == 1;
350
38.8k
}
Unexecuted instantiation: lib.c:qpbase_unref
351
352
/*
353
 * Now we know about `dns_qpreader_t` and `dns_qpbase_t`,
354
 * here's how we convert a twig reference into a pointer.
355
 */
356
static inline dns_qpnode_t *
357
714M
ref_ptr(dns_qpreadable_t qpr, dns_qpref_t ref) {
358
714M
  dns_qpreader_t *qp = dns_qpreader(qpr);
359
714M
  return qp->base->ptr[ref_chunk(ref)] + ref_cell(ref);
360
714M
}
Unexecuted instantiation: dns_qp.c:ref_ptr
qp.c:ref_ptr
Line
Count
Source
357
714M
ref_ptr(dns_qpreadable_t qpr, dns_qpref_t ref) {
358
714M
  dns_qpreader_t *qp = dns_qpreader(qpr);
359
714M
  return qp->base->ptr[ref_chunk(ref)] + ref_cell(ref);
360
714M
}
Unexecuted instantiation: lib.c:ref_ptr
361
362
/***********************************************************************
363
 *
364
 *  main qp-trie structures
365
 */
366
367
54.9k
#define QP_MAGIC       ISC_MAGIC('t', 'r', 'i', 'e')
368
658
#define QPITER_MAGIC   ISC_MAGIC('q', 'p', 'i', 't')
369
581
#define QPCHAIN_MAGIC  ISC_MAGIC('q', 'p', 'c', 'h')
370
18.0k
#define QPMULTI_MAGIC  ISC_MAGIC('q', 'p', 'm', 'v')
371
121k
#define QPREADER_MAGIC ISC_MAGIC('q', 'p', 'r', 'x')
372
20.9k
#define QPBASE_MAGIC   ISC_MAGIC('q', 'p', 'b', 'p')
373
18.4k
#define QPRCU_MAGIC    ISC_MAGIC('q', 'p', 'c', 'b')
374
375
#define QP_VALID(qp)    ISC_MAGIC_VALID(qp, QP_MAGIC)
376
#define QPITER_VALID(qp)  ISC_MAGIC_VALID(qp, QPITER_MAGIC)
377
#define QPCHAIN_VALID(qp) ISC_MAGIC_VALID(qp, QPCHAIN_MAGIC)
378
#define QPMULTI_VALID(qp) ISC_MAGIC_VALID(qp, QPMULTI_MAGIC)
379
#define QPBASE_VALID(qp)  ISC_MAGIC_VALID(qp, QPBASE_MAGIC)
380
#define QPRCU_VALID(qp)   ISC_MAGIC_VALID(qp, QPRCU_MAGIC)
381
382
/*
383
 * Polymorphic initialization of the `dns_qpreader_t` prefix.
384
 *
385
 * The location of the root node is actually a dns_qpref_t, but is
386
 * declared in DNS_QPREADER_FIELDS as uint32_t to avoid leaking too
387
 * many internal details into the public API.
388
 *
389
 * The `uctx` and `methods` support callbacks into the user's code.
390
 * They are constant after initialization.
391
 */
392
#define QP_INIT(qp, m, x)                 \
393
18.2k
  (*(qp) = (typeof(*(qp))){         \
394
18.2k
     .magic = QP_MAGIC,       \
395
18.2k
     .root_ref = INVALID_REF, \
396
18.2k
     .uctx = x,               \
397
18.2k
     .methods = m,            \
398
18.2k
   })
399
400
/*
401
 * Snapshots have some extra cleanup machinery.
402
 *
403
 * Originally, a snapshot was basically just a `dns_qpread_t`
404
 * allocated on the heap, with the extra behaviour that memory
405
 * reclamation is suppressed for a particular trie while it has any
406
 * snapshots. However that design gets into trouble for a zone with
407
 * frequent updates and many zone transfers.
408
 *
409
 * Instead, each snapshot records which chunks it needs. When a
410
 * snapshot is created, it makes a copy of the `base` array, except
411
 * for chunks that are empty and waiting to be reclaimed. When a
412
 * snapshot is destroyed, we can traverse the list of snapshots to
413
 * accurately mark which chunks are still needed.
414
 *
415
 * A snapshot's `whence` pointer helps ensure that a `dns_qpsnap_t`is
416
 * not muddled up with the wrong `dns_qpmulti_t`.
417
 *
418
 * A trie's `base` array might have grown after the snapshot was
419
 * created, so it records its own `chunk_max`.
420
 */
421
struct dns_qpsnap {
422
  DNS_QPREADER_FIELDS;
423
  dns_qpmulti_t *whence;
424
  uint32_t chunk_max;
425
  ISC_LINK(struct dns_qpsnap) link;
426
};
427
428
/*
429
 * Read-write access to a qp-trie requires extra fields to support the
430
 * allocator and garbage collector.
431
 *
432
 * Bare instances of a `struct dns_qp` are used for stand-alone
433
 * single-threaded tries. For multithreaded access, a `dns_qpmulti_t`
434
 * wraps a `dns_qp_t` with a mutex and other fields that are only needed
435
 * at the start or end of a transaction.
436
 *
437
 * Allocations are made sequentially in the `bump` chunk. A sequence
438
 * of lightweight write transactions can use the same `bump` chunk, so
439
 * its prefix before `fender` is immutable, and the rest is mutable.
440
 *
441
 * To decide when to compact and reclaim space, QP_MAX_GARBAGE() examines
442
 * the values of `used_count`, `free_count`, and `hold_count`. The
443
 * `hold_count` tracks nodes that need to be retained while readers are
444
 * using them; they are free but cannot be reclaimed until the transaction
445
 * has committed, so the `hold_count` is discounted from QP_MAX_GARBAGE()
446
 * during a transaction.
447
 *
448
 * There are some flags that alter the behaviour of write transactions.
449
 *
450
 *  - The `transaction_mode` indicates whether the current transaction is a
451
 *    light write or a heavy update, or (between transactions) the previous
452
 *    transaction's mode, because the setup for the next transaction
453
 *    depends on how the previous one committed. The mode is set at the
454
 *    start of each transaction. It is QP_NONE in a single-threaded qp-trie
455
 *    to detect if part of a `dns_qpmulti_t` is passed to dns_qp_destroy().
456
 *
457
 *  - The `compact_all` flag is used when every node in the trie should be
458
 *    copied. (Usually compation aims to avoid moving nodes out of
459
 *    unfragmented chunks.) It is used when compaction is explicitly
460
 *    requested via `dns_qp_compact()`, and as an emergency mechanism if
461
 *    normal compaction failed to clear the QP_MAX_GARBAGE() condition.
462
 *    (This emergency is a bug even tho we have a rescue mechanism.)
463
 *
464
 *  - When a qp-trie is destroyed while it has pending cleanup work, its
465
 *    `destroy` flag is set so that it is destroyed by the reclaim worker.
466
 *    (Because items cannot be removed from the middle of the cleanup list.)
467
 *
468
 *  - When built with fuzzing support, we can use mprotect() and munmap()
469
 *    to ensure that incorrect memory accesses cause fatal errors. The
470
 *    `write_protect` flag must be set straight after the `dns_qpmulti_t`
471
 *    is created, then left unchanged.
472
 *
473
 * Some of the dns_qp_t fields are only needed for multithreaded transactions
474
 * (marked [MT] below) but the same code paths are also used for single-
475
 * threaded writes.
476
 */
477
struct dns_qp {
478
  DNS_QPREADER_FIELDS;
479
  /*% memory context (const) */
480
  isc_mem_t *mctx;
481
  /*% array of per-chunk allocation counters */
482
  qp_usage_t *usage;
483
  /*% number of slots in `chunk` and `usage` arrays */
484
  dns_qpchunk_t chunk_max;
485
  /*% which chunk is used for allocations */
486
  dns_qpchunk_t bump;
487
  /*% nodes in the `bump` chunk below `fender` are read only [MT] */
488
  dns_qpcell_t fender;
489
  /*% number of leaf nodes */
490
  dns_qpcell_t leaf_count;
491
  /*% total of all usage[] counters */
492
  dns_qpcell_t used_count, free_count;
493
  /*% free cells that cannot be recovered right now */
494
  dns_qpcell_t hold_count;
495
  /*% capacity of last allocated chunk, for exponential chunk growth */
496
  dns_qpcell_t chunk_capacity;
497
  /*% what kind of transaction was most recently started [MT] */
498
  enum { QP_NONE, QP_WRITE, QP_UPDATE } transaction_mode : 2;
499
  /*% compact the entire trie [MT] */
500
  bool compact_all : 1;
501
  /*% optionally when compiled with fuzzing support [MT] */
502
  bool write_protect : 1;
503
};
504
505
/*
506
 * Concurrent access to a qp-trie.
507
 *
508
 * The `reader` pointer provides wait-free access to the current version
509
 * of the trie. See the "packed reader nodes" section below for a
510
 * description of what it points to.
511
 *
512
 * The main object under the protection of the mutex is the `writer`
513
 * containing all the allocator state. There can be a backup copy when
514
 * we want to be able to rollback an update transaction.
515
 *
516
 * There is a `reader_ref` which corresponds to the `reader` pointer
517
 * (`ref_ptr(multi->reader_ref) == multi->reader`). The `reader_ref` is
518
 * necessary when freeing the space used by the reader, because there
519
 * isn't a good way to recover a dns_qpref_t from a dns_qpnode_t pointer.
520
 *
521
 * There is a per-trie list of snapshots that is used for reclaiming
522
 * memory when a snapshot is destroyed.
523
 *
524
 * Finally, we maintain a global list of `dns_qpmulti_t` objects that
525
 * need asynchronous safe memory recovery.
526
 */
527
struct dns_qpmulti {
528
  uint32_t magic;
529
  /*% RCU-protected pointer to current packed reader */
530
  dns_qpnode_t *reader;
531
  /*% the mutex protects the rest of this structure */
532
  isc_mutex_t mutex;
533
  /*% ref_ptr(writer, reader_ref) == reader */
534
  dns_qpref_t reader_ref;
535
  /*% the main working structure */
536
  dns_qp_t writer;
537
  /*% saved allocator state to support rollback */
538
  dns_qp_t *rollback;
539
  /*% all snapshots of this trie */
540
  ISC_LIST(dns_qpsnap_t) snapshots;
541
  /*% refcount for memory reclamation */
542
  isc_refcount_t references;
543
};
544
545
/***********************************************************************
546
 *
547
 *  interior node constructors and accessors
548
 */
549
550
/*
551
 * See the comments under "interior node basics" above, which explain
552
 * the layout of nodes as implemented by the following functions.
553
 *
554
 * These functions are (mostly) constructors and getters. Imagine how
555
 * much less code there would be if C had sum types with control over
556
 * the layout...
557
 */
558
559
/*
560
 * Get the 64-bit word of a node.
561
 */
562
static inline uint64_t
563
1.20G
node64(dns_qpnode_t *n) {
564
1.20G
  uint64_t lo = n->biglo;
565
1.20G
  uint64_t hi = n->bighi;
566
1.20G
  return lo | (hi << 32);
567
1.20G
}
Unexecuted instantiation: dns_qp.c:node64
qp.c:node64
Line
Count
Source
563
1.20G
node64(dns_qpnode_t *n) {
564
1.20G
  uint64_t lo = n->biglo;
565
1.20G
  uint64_t hi = n->bighi;
566
1.20G
  return lo | (hi << 32);
567
1.20G
}
Unexecuted instantiation: lib.c:node64
568
569
/*
570
 * Get the 32-bit word of a node.
571
 */
572
static inline uint32_t
573
737M
node32(dns_qpnode_t *n) {
574
737M
  return n->small;
575
737M
}
Unexecuted instantiation: dns_qp.c:node32
qp.c:node32
Line
Count
Source
573
737M
node32(dns_qpnode_t *n) {
574
737M
  return n->small;
575
737M
}
Unexecuted instantiation: lib.c:node32
576
577
/*
578
 * Create a node from its parts
579
 */
580
static inline dns_qpnode_t
581
27.1M
make_node(uint64_t big, uint32_t small) {
582
27.1M
  return (dns_qpnode_t){
583
27.1M
    .biglo = (uint32_t)(big),
584
27.1M
    .bighi = (uint32_t)(big >> 32),
585
27.1M
    .small = small,
586
27.1M
  };
587
27.1M
}
Unexecuted instantiation: dns_qp.c:make_node
qp.c:make_node
Line
Count
Source
581
27.1M
make_node(uint64_t big, uint32_t small) {
582
27.1M
  return (dns_qpnode_t){
583
27.1M
    .biglo = (uint32_t)(big),
584
27.1M
    .bighi = (uint32_t)(big >> 32),
585
27.1M
    .small = small,
586
27.1M
  };
587
27.1M
}
Unexecuted instantiation: lib.c:make_node
588
589
/*
590
 * Extract a pointer from a node's 64 bit word. The double cast is to avoid
591
 * a warning about mismatched pointer/integer sizes on 32 bit systems.
592
 */
593
static inline void *
594
141M
node_pointer(dns_qpnode_t *n) {
595
141M
  return (void *)(uintptr_t)(node64(n) & ~TAG_MASK);
596
141M
}
Unexecuted instantiation: dns_qp.c:node_pointer
qp.c:node_pointer
Line
Count
Source
594
141M
node_pointer(dns_qpnode_t *n) {
595
141M
  return (void *)(uintptr_t)(node64(n) & ~TAG_MASK);
596
141M
}
Unexecuted instantiation: lib.c:node_pointer
597
598
/*
599
 * Examine a node's tag bits
600
 */
601
static inline uint32_t
602
169M
node_tag(dns_qpnode_t *n) {
603
169M
  return n->biglo & TAG_MASK;
604
169M
}
Unexecuted instantiation: dns_qp.c:node_tag
qp.c:node_tag
Line
Count
Source
602
169M
node_tag(dns_qpnode_t *n) {
603
169M
  return n->biglo & TAG_MASK;
604
169M
}
Unexecuted instantiation: lib.c:node_tag
605
606
/*
607
 * simplified for the hot path
608
 */
609
static inline bool
610
372M
is_branch(dns_qpnode_t *n) {
611
372M
  return n->biglo & BRANCH_TAG;
612
372M
}
Unexecuted instantiation: dns_qp.c:is_branch
qp.c:is_branch
Line
Count
Source
610
372M
is_branch(dns_qpnode_t *n) {
611
372M
  return n->biglo & BRANCH_TAG;
612
372M
}
Unexecuted instantiation: lib.c:is_branch
613
614
/* leaf nodes *********************************************************/
615
616
/*
617
 * Get a leaf's pointer value.
618
 */
619
static inline void *
620
60.3M
leaf_pval(dns_qpnode_t *n) {
621
60.3M
  return node_pointer(n);
622
60.3M
}
Unexecuted instantiation: dns_qp.c:leaf_pval
qp.c:leaf_pval
Line
Count
Source
620
60.3M
leaf_pval(dns_qpnode_t *n) {
621
60.3M
  return node_pointer(n);
622
60.3M
}
Unexecuted instantiation: lib.c:leaf_pval
623
624
/*
625
 * Get a leaf's integer value
626
 */
627
static inline uint32_t
628
57.3M
leaf_ival(dns_qpnode_t *n) {
629
57.3M
  return node32(n);
630
57.3M
}
Unexecuted instantiation: dns_qp.c:leaf_ival
qp.c:leaf_ival
Line
Count
Source
628
57.3M
leaf_ival(dns_qpnode_t *n) {
629
57.3M
  return node32(n);
630
57.3M
}
Unexecuted instantiation: lib.c:leaf_ival
631
632
/*
633
 * Create a leaf node from its parts
634
 */
635
static inline dns_qpnode_t
636
13.0M
make_leaf(const void *pval, uint32_t ival) {
637
13.0M
  dns_qpnode_t leaf = make_node((uintptr_t)pval, ival);
638
13.0M
  REQUIRE(node_tag(&leaf) == LEAF_TAG);
639
13.0M
  return leaf;
640
13.0M
}
Unexecuted instantiation: dns_qp.c:make_leaf
qp.c:make_leaf
Line
Count
Source
636
13.0M
make_leaf(const void *pval, uint32_t ival) {
637
13.0M
  dns_qpnode_t leaf = make_node((uintptr_t)pval, ival);
638
13.0M
  REQUIRE(node_tag(&leaf) == LEAF_TAG);
639
13.0M
  return leaf;
640
13.0M
}
Unexecuted instantiation: lib.c:make_leaf
641
642
/* branch nodes *******************************************************/
643
644
/*
645
 * The following function names use plural `twigs` when they work on a
646
 * branch's twigs vector as a whole, and singular `twig` when they work on
647
 * a particular twig.
648
 */
649
650
/*
651
 * Get a branch node's index word
652
 */
653
static inline uint64_t
654
1.07G
branch_index(dns_qpnode_t *n) {
655
1.07G
  return node64(n);
656
1.07G
}
Unexecuted instantiation: dns_qp.c:branch_index
qp.c:branch_index
Line
Count
Source
654
1.07G
branch_index(dns_qpnode_t *n) {
655
1.07G
  return node64(n);
656
1.07G
}
Unexecuted instantiation: lib.c:branch_index
657
658
/*
659
 * Get a reference to a branch node's child twigs.
660
 */
661
static inline dns_qpref_t
662
681M
branch_twigs_ref(dns_qpnode_t *n) {
663
681M
  return node32(n);
664
681M
}
Unexecuted instantiation: dns_qp.c:branch_twigs_ref
qp.c:branch_twigs_ref
Line
Count
Source
662
681M
branch_twigs_ref(dns_qpnode_t *n) {
663
681M
  return node32(n);
664
681M
}
Unexecuted instantiation: lib.c:branch_twigs_ref
665
666
/*
667
 * Bit positions in the bitmap come directly from the key. DNS names are
668
 * converted to keys using the tables declared at the end of this file.
669
 */
670
static inline dns_qpshift_t
671
1.21G
qpkey_bit(const dns_qpkey_t key, size_t len, size_t offset) {
672
1.21G
  if (offset < len) {
673
1.21G
    return key[offset];
674
1.21G
  } else {
675
7.88M
    return SHIFT_NOBYTE;
676
7.88M
  }
677
1.21G
}
Unexecuted instantiation: dns_qp.c:qpkey_bit
qp.c:qpkey_bit
Line
Count
Source
671
1.21G
qpkey_bit(const dns_qpkey_t key, size_t len, size_t offset) {
672
1.21G
  if (offset < len) {
673
1.21G
    return key[offset];
674
1.21G
  } else {
675
7.88M
    return SHIFT_NOBYTE;
676
7.88M
  }
677
1.21G
}
Unexecuted instantiation: lib.c:qpkey_bit
678
679
/*
680
 * Extract a branch node's offset field, used to index the key.
681
 */
682
static inline size_t
683
456M
branch_key_offset(dns_qpnode_t *n) {
684
456M
  return (size_t)(branch_index(n) >> SHIFT_OFFSET);
685
456M
}
Unexecuted instantiation: dns_qp.c:branch_key_offset
qp.c:branch_key_offset
Line
Count
Source
683
456M
branch_key_offset(dns_qpnode_t *n) {
684
456M
  return (size_t)(branch_index(n) >> SHIFT_OFFSET);
685
456M
}
Unexecuted instantiation: lib.c:branch_key_offset
686
687
/*
688
 * Which bit identifies the twig of this node for this key?
689
 */
690
static inline dns_qpshift_t
691
293M
branch_keybit(dns_qpnode_t *n, const dns_qpkey_t key, size_t len) {
692
293M
  return qpkey_bit(key, len, branch_key_offset(n));
693
293M
}
Unexecuted instantiation: dns_qp.c:branch_keybit
qp.c:branch_keybit
Line
Count
Source
691
293M
branch_keybit(dns_qpnode_t *n, const dns_qpkey_t key, size_t len) {
692
293M
  return qpkey_bit(key, len, branch_key_offset(n));
693
293M
}
Unexecuted instantiation: lib.c:branch_keybit
694
695
/*
696
 * Get a pointer to a the first twig of a branch (this also functions
697
 * as a pointer to the entire twig vector).
698
 */
699
static inline dns_qpnode_t *
700
286
branch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
701
286
  return ref_ptr(qpr, branch_twigs_ref(n));
702
286
}
Unexecuted instantiation: dns_qp.c:branch_twigs
qp.c:branch_twigs
Line
Count
Source
700
286
branch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
701
286
  return ref_ptr(qpr, branch_twigs_ref(n));
702
286
}
Unexecuted instantiation: lib.c:branch_twigs
703
704
/*
705
 * Warm up the cache while calculating which twig we want.
706
 */
707
static inline void
708
302M
prefetch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
709
302M
  __builtin_prefetch(ref_ptr(qpr, branch_twigs_ref(n)));
710
302M
}
Unexecuted instantiation: dns_qp.c:prefetch_twigs
qp.c:prefetch_twigs
Line
Count
Source
708
302M
prefetch_twigs(dns_qpreadable_t qpr, dns_qpnode_t *n) {
709
302M
  __builtin_prefetch(ref_ptr(qpr, branch_twigs_ref(n)));
710
302M
}
Unexecuted instantiation: lib.c:prefetch_twigs
711
712
/* root node **********************************************************/
713
714
/*
715
 * Get a pointer to the root node, checking if the trie is empty.
716
 */
717
static inline dns_qpnode_t *
718
19.1M
get_root(dns_qpreadable_t qpr) {
719
19.1M
  dns_qpreader_t *qp = dns_qpreader(qpr);
720
19.1M
  if (qp->root_ref == INVALID_REF) {
721
2.56M
    return NULL;
722
16.5M
  } else {
723
16.5M
    return ref_ptr(qp, qp->root_ref);
724
16.5M
  }
725
19.1M
}
Unexecuted instantiation: dns_qp.c:get_root
qp.c:get_root
Line
Count
Source
718
19.1M
get_root(dns_qpreadable_t qpr) {
719
19.1M
  dns_qpreader_t *qp = dns_qpreader(qpr);
720
19.1M
  if (qp->root_ref == INVALID_REF) {
721
2.56M
    return NULL;
722
16.5M
  } else {
723
16.5M
    return ref_ptr(qp, qp->root_ref);
724
16.5M
  }
725
19.1M
}
Unexecuted instantiation: lib.c:get_root
726
727
/*
728
 * When we need to move the root node, we avoid repeating allocation
729
 * logistics by making a temporary fake branch node that has
730
 *  `branch_twigs_size() == 1 && branch_twigs_ref() == root_ref`
731
 * just enough to treat the root node as a vector of one twig.
732
 */
733
#define MOVABLE_ROOT(qp)                                   \
734
10.3k
  (&(dns_qpnode_t){                                  \
735
10.3k
    .biglo = BRANCH_TAG | (1 << SHIFT_NOBYTE), \
736
10.3k
    .small = qp->root_ref,                     \
737
10.3k
  })
738
739
/***********************************************************************
740
 *
741
 *  bitmap popcount shenanigans
742
 */
743
744
/*
745
 * How many twigs appear in the vector before the one corresponding to the
746
 * given bit? Calculated using popcount of part of the branch's bitmap.
747
 *
748
 * To calculate a mask that covers the lesser bits in the bitmap,
749
 * we subtract 1 to set all lesser bits, and subtract the tag mask
750
 * because the type tag is not part of the bitmap.
751
 */
752
static inline dns_qpweight_t
753
301M
branch_count_bitmap_before(dns_qpnode_t *n, dns_qpshift_t bit) {
754
301M
  uint64_t mask = (1ULL << bit) - 1 - TAG_MASK;
755
301M
  uint64_t bitmap = branch_index(n) & mask;
756
301M
  return (dns_qpweight_t)stdc_count_ones(bitmap);
757
301M
}
Unexecuted instantiation: dns_qp.c:branch_count_bitmap_before
qp.c:branch_count_bitmap_before
Line
Count
Source
753
301M
branch_count_bitmap_before(dns_qpnode_t *n, dns_qpshift_t bit) {
754
301M
  uint64_t mask = (1ULL << bit) - 1 - TAG_MASK;
755
301M
  uint64_t bitmap = branch_index(n) & mask;
756
301M
  return (dns_qpweight_t)stdc_count_ones(bitmap);
757
301M
}
Unexecuted instantiation: lib.c:branch_count_bitmap_before
758
759
/*
760
 * How many twigs does this branch have?
761
 *
762
 * The offset is directly after the bitmap so the offset's lesser bits
763
 * covers the whole bitmap, and the bitmap's weight is the number of twigs.
764
 */
765
static inline dns_qpweight_t
766
16.7M
branch_twigs_size(dns_qpnode_t *n) {
767
16.7M
  return branch_count_bitmap_before(n, SHIFT_OFFSET);
768
16.7M
}
Unexecuted instantiation: dns_qp.c:branch_twigs_size
qp.c:branch_twigs_size
Line
Count
Source
766
16.7M
branch_twigs_size(dns_qpnode_t *n) {
767
16.7M
  return branch_count_bitmap_before(n, SHIFT_OFFSET);
768
16.7M
}
Unexecuted instantiation: lib.c:branch_twigs_size
769
770
/*
771
 * Position of a twig within the packed sparse vector.
772
 */
773
static inline dns_qpweight_t
774
284M
branch_twig_pos(dns_qpnode_t *n, dns_qpshift_t bit) {
775
284M
  return branch_count_bitmap_before(n, bit);
776
284M
}
Unexecuted instantiation: dns_qp.c:branch_twig_pos
qp.c:branch_twig_pos
Line
Count
Source
774
284M
branch_twig_pos(dns_qpnode_t *n, dns_qpshift_t bit) {
775
284M
  return branch_count_bitmap_before(n, bit);
776
284M
}
Unexecuted instantiation: lib.c:branch_twig_pos
777
778
/*
779
 * Get a pointer to the twig for a given bit number.
780
 */
781
static inline dns_qpnode_t *
782
201M
branch_twig_ptr(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpshift_t bit) {
783
201M
  return ref_ptr(qpr, branch_twigs_ref(n) + branch_twig_pos(n, bit));
784
201M
}
Unexecuted instantiation: dns_qp.c:branch_twig_ptr
qp.c:branch_twig_ptr
Line
Count
Source
782
201M
branch_twig_ptr(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpshift_t bit) {
783
201M
  return ref_ptr(qpr, branch_twigs_ref(n) + branch_twig_pos(n, bit));
784
201M
}
Unexecuted instantiation: lib.c:branch_twig_ptr
785
786
/*
787
 * Is the twig identified by this bit present?
788
 */
789
static inline bool
790
302M
branch_has_twig(dns_qpnode_t *n, dns_qpshift_t bit) {
791
302M
  return branch_index(n) & (1ULL << bit);
792
302M
}
Unexecuted instantiation: dns_qp.c:branch_has_twig
qp.c:branch_has_twig
Line
Count
Source
790
302M
branch_has_twig(dns_qpnode_t *n, dns_qpshift_t bit) {
791
302M
  return branch_index(n) & (1ULL << bit);
792
302M
}
Unexecuted instantiation: lib.c:branch_has_twig
793
794
/* twig logistics *****************************************************/
795
796
static inline void
797
20.1M
move_twigs(dns_qpnode_t *to, dns_qpnode_t *from, dns_qpweight_t size) {
798
20.1M
  memmove(to, from, size * sizeof(dns_qpnode_t));
799
20.1M
}
Unexecuted instantiation: dns_qp.c:move_twigs
qp.c:move_twigs
Line
Count
Source
797
20.1M
move_twigs(dns_qpnode_t *to, dns_qpnode_t *from, dns_qpweight_t size) {
798
20.1M
  memmove(to, from, size * sizeof(dns_qpnode_t));
799
20.1M
}
Unexecuted instantiation: lib.c:move_twigs
800
801
static inline void
802
11.9M
zero_twigs(dns_qpnode_t *twigs, dns_qpweight_t size) {
803
11.9M
  memset(twigs, 0, size * sizeof(dns_qpnode_t));
804
11.9M
}
Unexecuted instantiation: dns_qp.c:zero_twigs
qp.c:zero_twigs
Line
Count
Source
802
11.9M
zero_twigs(dns_qpnode_t *twigs, dns_qpweight_t size) {
803
11.9M
  memset(twigs, 0, size * sizeof(dns_qpnode_t));
804
11.9M
}
Unexecuted instantiation: lib.c:zero_twigs
805
806
/***********************************************************************
807
 *
808
 *  packed reader nodes
809
 */
810
811
/*
812
 * The purpose of these packed reader nodes is to simplify safe memory
813
 * reclamation for a multithreaded qp-trie.
814
 *
815
 * After the `reader` pointer in a qpmulti is replaced, we need to wait
816
 * for a grace period before we can reclaim the memory that is no longer
817
 * needed by the trie. So we need some kind of structure to hold
818
 * pointers to the (logically) detached memory until it is safe to free.
819
 * This memory includes the chunks and the `base` arrays.
820
 *
821
 * Packed reader nodes save us from having to track `dns_qpread_t`
822
 * objects as distinct allocations: the packed reader nodes get
823
 * reclaimed when the chunk containing their cells is reclaimed.
824
 * When a real `dns_qpread_t` object is needed, it is allocated on the
825
 * stack (it must not live longer than a isc_loop callback) and the
826
 * packed reader is unpacked into it.
827
 *
828
 * Chunks are owned by the current `base` array, so unused chunks are
829
 * held there until they are free()d. Old `base` arrays are attached
830
 * to packed reader nodes with a refcount. When a chunk is reclaimed,
831
 * it is scanned so that `chunk_free()` can call `detach_leaf()` on
832
 * any remaining references to leaf objects. Similarly, it calls
833
 * `qpbase_unref()` to reclaim old `base` arrays.
834
 */
835
836
/*
837
 * Two nodes is just enough space for the information needed by
838
 * readers and for deferred memory reclamation.
839
 */
840
54.1k
#define READER_SIZE 2
841
842
/*
843
 * Create a packed reader; space for the reader should have been
844
 * allocated using `alloc_twigs(&multi->writer, READER_SIZE)`.
845
 */
846
static inline void
847
36.1k
make_reader(dns_qpnode_t *reader, dns_qpmulti_t *multi) {
848
36.1k
  dns_qp_t *qp = &multi->writer;
849
36.1k
  reader[0] = make_node(READER_TAG | (uintptr_t)multi, QPREADER_MAGIC);
850
36.1k
  reader[1] = make_node(READER_TAG | (uintptr_t)qp->base, qp->root_ref);
851
36.1k
}
Unexecuted instantiation: dns_qp.c:make_reader
qp.c:make_reader
Line
Count
Source
847
36.1k
make_reader(dns_qpnode_t *reader, dns_qpmulti_t *multi) {
848
36.1k
  dns_qp_t *qp = &multi->writer;
849
36.1k
  reader[0] = make_node(READER_TAG | (uintptr_t)multi, QPREADER_MAGIC);
850
36.1k
  reader[1] = make_node(READER_TAG | (uintptr_t)qp->base, qp->root_ref);
851
36.1k
}
Unexecuted instantiation: lib.c:make_reader
852
853
static inline bool
854
71.9M
reader_valid(dns_qpnode_t *reader) {
855
71.9M
  return reader != NULL && //
856
71.9M
         node_tag(&reader[0]) == READER_TAG &&
857
90.8k
         node_tag(&reader[1]) == READER_TAG &&
858
71.9M
         node32(&reader[0]) == QPREADER_MAGIC;
859
71.9M
}
Unexecuted instantiation: dns_qp.c:reader_valid
qp.c:reader_valid
Line
Count
Source
854
71.9M
reader_valid(dns_qpnode_t *reader) {
855
71.9M
  return reader != NULL && //
856
71.9M
         node_tag(&reader[0]) == READER_TAG &&
857
90.8k
         node_tag(&reader[1]) == READER_TAG &&
858
71.9M
         node32(&reader[0]) == QPREADER_MAGIC;
859
71.9M
}
Unexecuted instantiation: lib.c:reader_valid
860
861
/*
862
 * Verify and unpack a reader. We return the `multi` pointer to use in
863
 * consistency checks.
864
 */
865
static inline dns_qpmulti_t *
866
36.6k
unpack_reader(dns_qpreader_t *qp, dns_qpnode_t *reader) {
867
36.6k
  INSIST(reader_valid(reader));
868
36.6k
  dns_qpmulti_t *multi = node_pointer(&reader[0]);
869
36.6k
  dns_qpbase_t *base = node_pointer(&reader[1]);
870
36.6k
  INSIST(QPMULTI_VALID(multi));
871
36.6k
  INSIST(QPBASE_VALID(base));
872
36.6k
  *qp = (dns_qpreader_t){
873
36.6k
    .magic = QP_MAGIC,
874
36.6k
    .uctx = multi->writer.uctx,
875
36.6k
    .methods = multi->writer.methods,
876
36.6k
    .root_ref = node32(&reader[1]),
877
36.6k
    .base = base,
878
36.6k
  };
879
36.6k
  return multi;
880
36.6k
}
Unexecuted instantiation: dns_qp.c:unpack_reader
qp.c:unpack_reader
Line
Count
Source
866
36.6k
unpack_reader(dns_qpreader_t *qp, dns_qpnode_t *reader) {
867
36.6k
  INSIST(reader_valid(reader));
868
36.6k
  dns_qpmulti_t *multi = node_pointer(&reader[0]);
869
36.6k
  dns_qpbase_t *base = node_pointer(&reader[1]);
870
36.6k
  INSIST(QPMULTI_VALID(multi));
871
36.6k
  INSIST(QPBASE_VALID(base));
872
36.6k
  *qp = (dns_qpreader_t){
873
36.6k
    .magic = QP_MAGIC,
874
36.6k
    .uctx = multi->writer.uctx,
875
36.6k
    .methods = multi->writer.methods,
876
36.6k
    .root_ref = node32(&reader[1]),
877
36.6k
    .base = base,
878
36.6k
  };
879
36.6k
  return multi;
880
36.6k
}
Unexecuted instantiation: lib.c:unpack_reader
881
882
/***********************************************************************
883
 *
884
 *  method invocation helpers
885
 */
886
887
static inline void
888
12.9M
attach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
889
12.9M
  dns_qpreader_t *qp = dns_qpreader(qpr);
890
12.9M
  qp->methods->attach(qp->uctx, leaf_pval(n), leaf_ival(n));
891
12.9M
}
Unexecuted instantiation: dns_qp.c:attach_leaf
qp.c:attach_leaf
Line
Count
Source
888
12.9M
attach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
889
12.9M
  dns_qpreader_t *qp = dns_qpreader(qpr);
890
12.9M
  qp->methods->attach(qp->uctx, leaf_pval(n), leaf_ival(n));
891
12.9M
}
Unexecuted instantiation: lib.c:attach_leaf
892
893
static inline void
894
12.9M
detach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
895
12.9M
  dns_qpreader_t *qp = dns_qpreader(qpr);
896
12.9M
  qp->methods->detach(qp->uctx, leaf_pval(n), leaf_ival(n));
897
12.9M
}
Unexecuted instantiation: dns_qp.c:detach_leaf
qp.c:detach_leaf
Line
Count
Source
894
12.9M
detach_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n) {
895
12.9M
  dns_qpreader_t *qp = dns_qpreader(qpr);
896
12.9M
  qp->methods->detach(qp->uctx, leaf_pval(n), leaf_ival(n));
897
12.9M
}
Unexecuted instantiation: lib.c:detach_leaf
898
899
static inline size_t
900
31.8M
leaf_qpkey(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpkey_t key) {
901
31.8M
  dns_qpreader_t *qp = dns_qpreader(qpr);
902
31.8M
  size_t len = qp->methods->makekey(key, qp->uctx, leaf_pval(n),
903
31.8M
            leaf_ival(n));
904
31.8M
  INSIST(len < sizeof(dns_qpkey_t));
905
31.8M
  return len;
906
31.8M
}
Unexecuted instantiation: dns_qp.c:leaf_qpkey
qp.c:leaf_qpkey
Line
Count
Source
900
31.8M
leaf_qpkey(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpkey_t key) {
901
31.8M
  dns_qpreader_t *qp = dns_qpreader(qpr);
902
31.8M
  size_t len = qp->methods->makekey(key, qp->uctx, leaf_pval(n),
903
31.8M
            leaf_ival(n));
904
31.8M
  INSIST(len < sizeof(dns_qpkey_t));
905
31.8M
  return len;
906
31.8M
}
Unexecuted instantiation: lib.c:leaf_qpkey
907
908
static inline char *
909
0
triename(dns_qpreadable_t qpr, char *buf, size_t size) {
910
0
  dns_qpreader_t *qp = dns_qpreader(qpr);
911
0
  qp->methods->triename(qp->uctx, buf, size);
912
0
  return buf;
913
0
}
Unexecuted instantiation: dns_qp.c:triename
Unexecuted instantiation: qp.c:triename
Unexecuted instantiation: lib.c:triename
914
915
#define TRIENAME(qp) \
916
0
  triename(qp, (char[DNS_QP_TRIENAME_MAX]){}, DNS_QP_TRIENAME_MAX)
917
918
/***********************************************************************
919
 *
920
 *  converting DNS names to trie keys
921
 */
922
923
/*
924
 * This is a deliberate simplification of the hostname characters,
925
 * because it doesn't matter much if we treat a few extra characters
926
 * favourably: there is plenty of space in the index word for a
927
 * slightly larger bitmap.
928
 */
929
static inline bool
930
51.9k
qp_common_character(uint8_t byte) {
931
51.9k
  return ('-' <= byte && byte <= '9') || ('_' <= byte && byte <= 'z');
932
51.9k
}
Unexecuted instantiation: dns_qp.c:qp_common_character
qp.c:qp_common_character
Line
Count
Source
930
51.9k
qp_common_character(uint8_t byte) {
931
51.9k
  return ('-' <= byte && byte <= '9') || ('_' <= byte && byte <= 'z');
932
51.9k
}
Unexecuted instantiation: lib.c:qp_common_character
933
934
/*
935
 * Lookup table mapping bytes in DNS names to bit positions, used
936
 * by dns_qpkey_fromname() to convert DNS names to qp-trie keys.
937
 */
938
extern uint16_t dns_qp_bits_for_byte[];
939
940
/*
941
 * And the reverse, mapping bit positions to characters, so the tests
942
 * can print diagnostics involving qp-trie keys.
943
 */
944
extern uint8_t dns_qp_byte_for_bit[];
945
946
/**********************************************************************/
947
948
void
949
dns__qp_initialize(void);
950
void
951
dns__qp_shutdown(void);