Coverage Report

Created: 2026-02-26 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/tests/libtest/qp.c
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
#include <assert.h>
15
#include <stdint.h>
16
#include <stdio.h>
17
18
#include <isc/buffer.h>
19
#include <isc/loop.h>
20
#include <isc/magic.h>
21
#include <isc/refcount.h>
22
#include <isc/rwlock.h>
23
#include <isc/urcu.h>
24
#include <isc/util.h>
25
26
#include <dns/db.h>
27
#include <dns/fixedname.h>
28
#include <dns/name.h>
29
#include <dns/qp.h>
30
#include <dns/types.h>
31
32
#include "qp_p.h"
33
34
#include <tests/qp.h>
35
36
/***********************************************************************
37
 *
38
 *  key reverse conversions
39
 */
40
41
uint8_t
42
524k
qp_test_bittoascii(dns_qpshift_t bit) {
43
524k
  uint8_t byte = dns_qp_byte_for_bit[bit];
44
524k
  if (bit == SHIFT_NOBYTE) {
45
491k
    return '.';
46
491k
  } else if (qp_common_character(byte)) {
47
0
    return byte;
48
32.7k
  } else if (byte < '-') {
49
32.7k
    return '#';
50
32.7k
  } else if (byte < '_') {
51
0
    return '@';
52
0
  } else {
53
0
    return '~' - SHIFT_OFFSET + bit;
54
0
  }
55
524k
}
56
57
const char *
58
32.7k
qp_test_keytoascii(dns_qpkey_t key, size_t len) {
59
557k
  for (size_t offset = 1; offset < len; offset++) {
60
524k
    key[offset] = qp_test_bittoascii(key[offset]);
61
524k
  }
62
32.7k
  key[len] = '\0';
63
32.7k
  return (const char *)key;
64
32.7k
}
65
66
/***********************************************************************
67
 *
68
 *  trie properties
69
 */
70
71
static size_t
72
0
getheight(dns_qp_t *qp, dns_qpnode_t *n) {
73
0
  if (node_tag(n) == LEAF_TAG) {
74
0
    return 0;
75
0
  }
76
0
  size_t max_height = 0;
77
0
  dns_qpnode_t *twigs = branch_twigs(qp, n);
78
0
  dns_qpweight_t size = branch_twigs_size(n);
79
0
  for (dns_qpweight_t pos = 0; pos < size; pos++) {
80
0
    size_t height = getheight(qp, &twigs[pos]);
81
0
    max_height = ISC_MAX(max_height, height);
82
0
  }
83
0
  return max_height + 1;
84
0
}
85
86
size_t
87
0
qp_test_getheight(dns_qp_t *qp) {
88
0
  dns_qpnode_t *root = get_root(qp);
89
0
  return root == NULL ? 0 : getheight(qp, root);
90
0
}
91
92
static size_t
93
0
maxkeylen(dns_qp_t *qp, dns_qpnode_t *n) {
94
0
  if (node_tag(n) == LEAF_TAG) {
95
0
    dns_qpkey_t key;
96
0
    return leaf_qpkey(qp, n, key);
97
0
  }
98
0
  size_t max_len = 0;
99
0
  dns_qpnode_t *twigs = branch_twigs(qp, n);
100
0
  dns_qpweight_t size = branch_twigs_size(n);
101
0
  for (dns_qpweight_t pos = 0; pos < size; pos++) {
102
0
    size_t len = maxkeylen(qp, &twigs[pos]);
103
0
    max_len = ISC_MAX(max_len, len);
104
0
  }
105
0
  return max_len;
106
0
}
107
108
size_t
109
0
qp_test_maxkeylen(dns_qp_t *qp) {
110
0
  dns_qpnode_t *root = get_root(qp);
111
0
  return root == NULL ? 0 : maxkeylen(qp, root);
112
0
}
113
114
/***********************************************************************
115
 *
116
 *  dump to stdout
117
 */
118
119
static void
120
0
dumpread(dns_qpreadable_t qpr, const char *type, const char *tail) {
121
0
  dns_qpreader_t *qp = dns_qpreader(qpr);
122
0
  printf("%s %p root %u %u:%u base %p methods %p%s", type, qp,
123
0
         qp->root_ref, ref_chunk(qp->root_ref), ref_cell(qp->root_ref),
124
0
         qp->base, qp->methods, tail);
125
0
}
126
127
static void
128
0
dumpqp(dns_qp_t *qp, const char *type) {
129
0
  dumpread(qp, type, " mctx ");
130
0
  printf("%p\n", qp->mctx);
131
0
  printf("%s %p usage %p chunk_max %u bump %u fender %u\n", type, qp,
132
0
         qp->usage, qp->chunk_max, qp->bump, qp->fender);
133
0
  printf("%s %p leaf %u live %u used %u free %u hold %u\n", type, qp,
134
0
         qp->leaf_count, qp->used_count - qp->free_count, qp->used_count,
135
0
         qp->free_count, qp->hold_count);
136
0
  printf("%s %p compact_all=%d transaction_mode=%d write_protect=%d\n",
137
0
         type, qp, qp->compact_all, qp->transaction_mode,
138
0
         qp->write_protect);
139
0
}
140
141
void
142
0
qp_test_dumpread(dns_qpreadable_t qp) {
143
0
  dumpread(qp, "qpread", "\n");
144
0
  fflush(stdout);
145
0
}
146
147
void
148
0
qp_test_dumpsnap(dns_qpsnap_t *qp) {
149
0
  dumpread(qp, "qpsnap", " whence ");
150
0
  printf("%p\n", qp->whence);
151
0
  fflush(stdout);
152
0
}
153
154
void
155
0
qp_test_dumpqp(dns_qp_t *qp) {
156
0
  dumpqp(qp, "qp");
157
0
  fflush(stdout);
158
0
}
159
160
void
161
0
qp_test_dumpmulti(dns_qpmulti_t *multi) {
162
0
  dns_qpreader_t qpr;
163
0
  dns_qpnode_t *reader = rcu_dereference(multi->reader);
164
0
  dns_qpmulti_t *whence = unpack_reader(&qpr, reader);
165
0
  dumpqp(&multi->writer, "qpmulti->writer");
166
0
  printf("qpmulti->reader %p root_ref %u %u:%u base %p\n", reader,
167
0
         qpr.root_ref, ref_chunk(qpr.root_ref), ref_cell(qpr.root_ref),
168
0
         qpr.base);
169
0
  printf("qpmulti->reader %p whence %p\n", reader, whence);
170
0
  unsigned int snapshots = 0;
171
0
  ISC_LIST_FOREACH(multi->snapshots, snap, link) {
172
0
    snapshots++;
173
0
  }
174
0
  printf("qpmulti %p snapshots %u\n", multi, snapshots);
175
0
  fflush(stdout);
176
0
}
177
178
void
179
0
qp_test_dumpchunks(dns_qp_t *qp) {
180
0
  dns_qpcell_t used_count = 0;
181
0
  dns_qpcell_t free_count = 0;
182
0
  dumpqp(qp, "qp");
183
0
  for (dns_qpchunk_t c = 0; c < qp->chunk_max; c++) {
184
0
    printf("qp %p chunk %u base %p "
185
0
           "used %u free %u immutable %u discounted %u\n",
186
0
           qp, c, qp->base->ptr[c], qp->usage[c].used,
187
0
           qp->usage[c].free, qp->usage[c].immutable,
188
0
           qp->usage[c].discounted);
189
0
    used_count += qp->usage[c].used;
190
0
    free_count += qp->usage[c].free;
191
0
  }
192
0
  printf("qp %p total used %" PRIu32 " free %" PRIu32 "\n", qp,
193
0
         used_count, free_count);
194
0
  fflush(stdout);
195
0
}
196
197
void
198
0
qp_test_dumptrie(dns_qpreadable_t qpr) {
199
0
  dns_qpreader_t *qp = dns_qpreader(qpr);
200
0
  struct {
201
0
    dns_qpref_t ref;
202
0
    dns_qpshift_t max, pos;
203
0
  } stack[512];
204
0
  size_t sp = 0;
205
0
  dns_qpcell_t leaf_count = 0;
206
207
  /*
208
   * fake up a sentinel stack entry corresponding to the root
209
   * node; the ref is deliberately out of bounds, and pos == max
210
   * so we will immediately stop scanning it
211
   */
212
0
  stack[sp].ref = INVALID_REF;
213
0
  stack[sp].max = 0;
214
0
  stack[sp].pos = 0;
215
216
0
  dns_qpnode_t *n = get_root(qp);
217
0
  if (n == NULL) {
218
0
    printf("%p EMPTY\n", n);
219
0
    fflush(stdout);
220
0
    return;
221
0
  } else {
222
0
    printf("%p ROOT qp %p base %p\n", n, qp, qp->base);
223
0
  }
224
225
0
  for (;;) {
226
0
    if (is_branch(n)) {
227
0
      dns_qpref_t ref = branch_twigs_ref(n);
228
0
      dns_qpweight_t max = branch_twigs_size(n);
229
0
      dns_qpnode_t *twigs = ref_ptr(qp, ref);
230
231
      /* brief list of twigs */
232
0
      dns_qpkey_t bits;
233
0
      size_t len = 0;
234
0
      for (dns_qpshift_t bit = SHIFT_NOBYTE;
235
0
           bit < SHIFT_OFFSET; bit++)
236
0
      {
237
0
        if (branch_has_twig(n, bit)) {
238
0
          bits[len++] = bit;
239
0
        }
240
0
      }
241
0
      assert(len == max);
242
0
      qp_test_keytoascii(bits, len);
243
0
      printf("%*s%p BRANCH %p %u %u:%u %zu %s\n", (int)sp * 2,
244
0
             "", n, twigs, ref, ref_chunk(ref), ref_cell(ref),
245
0
             branch_key_offset(n), bits);
246
247
0
      ++sp;
248
0
      stack[sp].ref = ref;
249
0
      stack[sp].max = max;
250
0
      stack[sp].pos = 0;
251
0
    } else {
252
0
      dns_qpkey_t key;
253
0
      qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
254
0
      printf("%*s%p LEAF %p %d %s\n", (int)sp * 2, "", n,
255
0
             leaf_pval(n), leaf_ival(n), key);
256
0
      leaf_count++;
257
0
    }
258
259
0
    while (stack[sp].pos == stack[sp].max) {
260
0
      if (sp == 0) {
261
0
        printf("LEAVES %d\n", leaf_count);
262
0
        fflush(stdout);
263
0
        return;
264
0
      }
265
0
      --sp;
266
0
    }
267
268
0
    stack[sp].pos++;
269
0
    fprintf(stderr, "pos %d/%d, ref+%d\n", stack[sp].pos,
270
0
      stack[sp].max, stack[sp].pos - 1);
271
0
    n = ref_ptr(qp, stack[sp].ref) + stack[sp].pos - 1;
272
0
  }
273
0
}
274
275
static void
276
0
dumpdot_name(dns_qpnode_t *n) {
277
0
  if (n == NULL) {
278
0
    printf("empty");
279
0
  } else if (is_branch(n)) {
280
0
    dns_qpref_t ref = branch_twigs_ref(n);
281
0
    printf("c%dn%d", ref_chunk(ref), ref_cell(ref));
282
0
  } else {
283
0
    printf("v%p", leaf_pval(n));
284
0
  }
285
0
}
286
287
static void
288
0
dumpdot_twig(dns_qp_t *qp, dns_qpnode_t *n) {
289
0
  if (n == NULL) {
290
0
    printf("empty [shape=oval, label=\"\\N EMPTY\"];\n");
291
0
  } else if (is_branch(n)) {
292
0
    dumpdot_name(n);
293
0
    printf(" [shape=record, label=\"{ \\N\\noff %zu | ",
294
0
           branch_key_offset(n));
295
0
    char sep = '{';
296
0
    for (dns_qpshift_t bit = SHIFT_NOBYTE; bit < SHIFT_OFFSET;
297
0
         bit++)
298
0
    {
299
0
      if (branch_has_twig(n, bit)) {
300
0
        printf("%c <t%d> %c ", sep,
301
0
               branch_twig_pos(n, bit),
302
0
               qp_test_bittoascii(bit));
303
0
        sep = '|';
304
0
      }
305
0
    }
306
0
    printf("}}\"];\n");
307
308
0
    dns_qpnode_t *twigs = branch_twigs(qp, n);
309
0
    dns_qpweight_t size = branch_twigs_size(n);
310
311
0
    for (dns_qpweight_t pos = 0; pos < size; pos++) {
312
0
      dumpdot_name(n);
313
0
      printf(":t%d:e -> ", pos);
314
0
      dumpdot_name(&twigs[pos]);
315
0
      printf(":w;\n");
316
0
    }
317
318
0
    for (dns_qpweight_t pos = 0; pos < size; pos++) {
319
0
      dumpdot_twig(qp, &twigs[pos]);
320
0
    }
321
322
0
  } else {
323
0
    dns_qpkey_t key;
324
0
    const char *str;
325
0
    str = qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
326
0
    printf("v%p [shape=oval, label=\"\\N ival %d\\n%s\"];\n",
327
0
           leaf_pval(n), leaf_ival(n), str);
328
0
  }
329
0
}
330
331
void
332
0
qp_test_dumpdot(dns_qp_t *qp) {
333
0
  REQUIRE(QP_VALID(qp));
334
0
  dns_qpnode_t *n = get_root(qp);
335
0
  printf("strict digraph {\nrankdir = \"LR\"; ranksep = 1.0;\n");
336
0
  printf("ROOT [shape=point]; ROOT -> ");
337
0
  dumpdot_name(n);
338
0
  printf(":w;\n");
339
0
  dumpdot_twig(qp, n);
340
0
  printf("}\n");
341
0
}
342
343
void
344
0
qp_test_printkey(const dns_qpkey_t key, size_t keylen) {
345
0
  dns_fixedname_t fn;
346
0
  dns_name_t *n = dns_fixedname_initname(&fn);
347
0
  dns_namespace_t s;
348
0
  char txt[DNS_NAME_FORMATSIZE];
349
350
0
  dns_qpkey_toname(key, keylen, n, &s);
351
0
  dns_name_format(n, txt, sizeof(txt));
352
0
  printf("%s%s%s\n", txt,
353
0
         s == DNS_DBNAMESPACE_NSEC3
354
0
           ? "NSEC3:"
355
0
           : (s == DNS_DBNAMESPACE_NSEC ? "NSEC" : ""),
356
0
         dns_name_isabsolute(n) ? "." : "");
357
0
}
358
359
/**********************************************************************/