Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: qp.h,v 1.4 2026/01/29 18:37:50 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #pragma once
     17 
     18 /*
     19  * A qp-trie is a kind of key -> value map, supporting lookups that are
     20  * aware of the lexicographic order of keys.
     21  *
     22  * Keys are `dns_qpkey_t`, which is a string-like thing, usually created
     23  * from a DNS name. You can use both relative and absolute DNS names as
     24  * keys, even in the same trie, except for one caveat: if a trie contains
     25  * names relative to the zone apex, the natural way to represent the apex
     26  * itself (spelled `@` in zone files) is a zero-length name; but a
     27  * zero-length name has the same qpkey representation as the root zone
     28  * (apart from its length), so they collide.
     29  *
     30  * Leaf values are a pair of a `void *` pointer and a `uint32_t`
     31  * (because that is what fits inside an internal qp-trie leaf node).
     32  *
     33  * The trie does not store keys; instead keys are derived from leaf values
     34  * by calling a method provided by the user.
     35  *
     36  * There are a few flavours of qp-trie.
     37  *
     38  * The basic `dns_qp_t` supports single-threaded read/write access.
     39  *
     40  * A `dns_qpmulti_t` is a wrapper that supports multithreaded access.
     41  * There can be many concurrent readers and a single writer. Writes are
     42  * transactional, and support multi-version concurrency.
     43  *
     44  * The concurrency strategy uses copy-on-write. When making changes during
     45  * a transaction, the caller must not modify leaf values in place, but
     46  * instead delete the old leaf from the trie and insert a replacement. Leaf
     47  * values have reference counts, which will indicate when the old leaf
     48  * value can be freed after it is no longer needed by readers using an old
     49  * version of the trie.
     50  *
     51  * For fast concurrent reads, call `dns_qpmulti_query()` to fill in a
     52  * `dns_qpread_t`, which must be allocated on the stack. This gives
     53  * the reader access to a single version of the trie. The reader's
     54  * thread must be registered with `liburcu`, which is normally taken
     55  * care of by `libisc`. Readers are not blocked by any write activity,
     56  * and vice versa.
     57  *
     58  * For reads that need a stable view of the trie for multiple cycles
     59  * of an isc_loop, or which can be used from any thread, call
     60  * `dns_qpmulti_snapshot()` to get a `dns_qpsnap_t`. A snapshot is for
     61  * relatively heavy long-running read-only operations such as zone
     62  * transfers.
     63  *
     64  * You can start one read-write transaction at a time using
     65  * `dns_qpmulti_write()` or `dns_qpmulti_update()`. Either way, you
     66  * get a `dns_qp_t` that can be modified like a single-threaded trie,
     67  * without affecting other read-only query or snapshot users of the
     68  * `dns_qpmulti_t`.
     69  *
     70  * "Update" transactions are heavyweight. They allocate working memory to
     71  * hold modifications to the trie, and compact the trie before committing.
     72  * For extra space savings, a partially-used allocation chunk is shrunk to
     73  * the smallest size possible. Unlike "write" transactions, an "update"
     74  * transaction can be rolled back instead of committed. (Update
     75  * transactions are intended for things like authoritative zones, where it
     76  * is important to keep the per-trie memory overhead low because there can
     77  * be a very large number of them.)
     78  *
     79  * "Write" transactions are more lightweight: they skip the allocation and
     80  * compaction at the start and end of the transaction. (Write transactions
     81  * are intended for frequent small changes, as in the DNS cache.)
     82  */
     83 
     84 /***********************************************************************
     85  *
     86  *  types
     87  */
     88 
     89 #include <isc/attributes.h>
     90 
     91 #include <dns/name.h>
     92 #include <dns/types.h>
     93 
     94 /*%
     95  * How many bytes a qp-trie might allocate as part of an insert. Needed for
     96  * overmem checks.
     97  */
     98 #define QP_SAFETY_MARGIN ((1ul << 12ul) * 12)
     99 
    100 /*%
    101  * A `dns_qp_t` supports single-threaded read/write access.
    102  */
    103 typedef struct dns_qp dns_qp_t;
    104 
    105 /*%
    106  * A `dns_qpmulti_t` supports multi-version wait-free concurrent reads
    107  * and one transactional modification at a time.
    108  */
    109 typedef struct dns_qpmulti dns_qpmulti_t;
    110 
    111 /*%
    112  * Read-only parts of a qp-trie.
    113  *
    114  * A `dns_qpreader_t` is the common prefix of the `dns_qpreadable`
    115  * types, containing just the fields neded for the hot path. The
    116  * internals of a `dns_qpreader_t` are private; they are only exposed
    117  * so that callers can allocate a `dns_qpread_t` on the stack.
    118  *
    119  * Ranty aside: annoyingly, C doesn't allow us to use a predeclared
    120  * structure type as an anonymous struct member, so we have to use a
    121  * macro. (GCC and Clang have the feature we want under -fms-extensions,
    122  * but a non-standard extension won't make these declarations neater if
    123  * we must also have a standard alternative.)
    124  */
    125 #define DNS_QPREADER_FIELDS                   \
    126 	uint32_t		    magic;    \
    127 	dns_qpref_t		    root_ref; \
    128 	dns_qpbase_t		   *base;     \
    129 	void			   *uctx;     \
    130 	const struct dns_qpmethods *methods
    131 
    132 typedef struct dns_qpbase dns_qpbase_t; /* private, declared in qp_p.h */
    133 
    134 /*%
    135  * A unique twig reference; this can be converted to chunk and cell
    136  * values to find a specific location.
    137  */
    138 typedef uint32_t dns_qpref_t;
    139 
    140 typedef struct dns_qpreader {
    141 	DNS_QPREADER_FIELDS;
    142 } dns_qpreader_t;
    143 
    144 /*%
    145  * A `dns_qpread_t` is a read-only handle on a `dns_qpmulti_t`.
    146  * The caller provides space for it on the stack; it can be
    147  * used by only one thread. As well as the `DNS_QPREADER_FIELDS`,
    148  * it contains a thread ID to check for incorrect usage.
    149  *
    150  * The internals of a `dns_qpread_t` are private; they are only
    151  * exposed so that callers can allocate an instance on the stack.
    152  */
    153 typedef struct dns_qpread {
    154 	DNS_QPREADER_FIELDS;
    155 	uint32_t tid;
    156 } dns_qpread_t;
    157 
    158 /*%
    159  * A `dns_qpsnap_t` is a read-only snapshot of a `dns_qpmulti_t`.
    160  * It requires allocation and taking the `dns_qpmulti_t` mutex to
    161  * create; it can be used from any thread.
    162  */
    163 typedef struct dns_qpsnap dns_qpsnap_t;
    164 
    165 /*%
    166  * The read-only qp-trie functions can work on either of the read-only
    167  * qp-trie types dns_qpsnap_t or dns_qpread_t, or the general-purpose
    168  * read-write `dns_qp_t`. They rely on the fact that all the
    169  * `dns_qpreadable_t` structures start with a `dns_qpreader_t`
    170  */
    171 typedef union dns_qpreadable {
    172 	dns_qpreader_t *qp;
    173 	dns_qpread_t   *qpr;
    174 	dns_qpsnap_t   *qps;
    175 	dns_qp_t       *qpt;
    176 } dns_qpreadable_t __attribute__((__transparent_union__));
    177 
    178 #define dns_qpreader(qpr) ((qpr).qp)
    179 
    180 /*%
    181  * The maximum size of a key is also the maximum depth of a trie.
    182  *
    183  * A domain name can be up to 255 bytes. When converted to a key, each
    184  * character in the name corresponds to one byte in the key if it is a
    185  * common hostname character; otherwise unusual characters are escaped,
    186  * using two bytes in the key. So we allow keys to be up to 512 bytes.
    187  * (The actual max is (255 - 5) * 2 + 6 == 506)
    188  */
    189 #define DNS_QP_MAXKEY 512
    190 
    191 /*
    192  * C is not strict enough with its integer types for the following typedefs
    193  * to improve type safety, but it helps to have annotations saying what
    194  * particular kind of number we are dealing with.
    195  */
    196 
    197 /*%
    198  * The bit number, or position of a bit inside a word. (Valid values 0..63)
    199  * A dns_qpkey_t (below) is an array of these; each element within dns_qpkey
    200  * must satisfy:
    201  *
    202  *	SHIFT_NOBYTE <= key[off] && key[off] < SHIFT_OFFSET
    203  */
    204 typedef uint8_t dns_qpshift_t;
    205 
    206 /*%
    207  * The number of bits set in a word (i.e, Hamming weight or popcount).
    208  * This is used to determine the position of a node in the packed sparse
    209  * vector of twigs. Valid values are 0..47 (because our bitmap does not
    210  * fill the entire word).
    211  */
    212 typedef uint8_t dns_qpweight_t;
    213 
    214 /*
    215  * Chunk and cell numbers, used to identify a specific location in
    216  * one of the chunks stored in the QP base pointer array. Each cell
    217  * within a chunk can contain a node.
    218  */
    219 typedef uint32_t dns_qpchunk_t;
    220 typedef uint32_t dns_qpcell_t;
    221 
    222 /*%
    223  * A trie lookup key is a small array, allocated on the stack during trie
    224  * searches. Keys are usually created on demand from DNS names using
    225  * `dns_qpkey_fromname()`, but in principle you can define your own
    226  * functions to convert other types to trie lookup keys.
    227  */
    228 typedef dns_qpshift_t dns_qpkey_t[DNS_QP_MAXKEY];
    229 
    230 /*%
    231  * A QP iterator traverses a trie starting with the root and passing
    232  * though each leaf node in lexicographic order; it is used by
    233  * `dns_qpiter_init()` and `dns_qpiter_next()`. It is also used
    234  * internally by `dns_qp_findname_iterator()` to locate the predecessor
    235  * of a searched-for name.
    236  */
    237 typedef struct dns_qpiter {
    238 	unsigned int	magic;
    239 	dns_qpreader_t *qp;
    240 	uint16_t	sp;
    241 	dns_qpnode_t   *stack[DNS_QP_MAXKEY];
    242 } dns_qpiter_t;
    243 
    244 /*%
    245  * A QP chain holds references to each populated node between the root and
    246  * a given leaf. It is used internally by `dns_qp_lookup()` to return a
    247  * partial match if the specific name requested is not found; optionally it
    248  * can be passed back to the caller so that individual nodes can be
    249  * accessed.
    250  */
    251 typedef struct dns_qpchain {
    252 	unsigned int	magic;
    253 	dns_qpreader_t *qp;
    254 	uint8_t		len;
    255 	struct {
    256 		dns_qpnode_t *node;
    257 		size_t	      offset;
    258 	} chain[DNS_NAME_MAXLABELS];
    259 } dns_qpchain_t;
    260 
    261 /*%
    262  * These leaf methods allow the qp-trie code to call back to the code
    263  * responsible for the leaf values that are stored in the trie. The
    264  * methods are provided for a whole trie when the trie is created.
    265  *
    266  * When you create a qp-trie, you provide a context pointer that is
    267  * passed to the methods. The context pointer can tell the methods
    268  * something about the trie as a whole, in addition to a particular
    269  * leaf's values.
    270  *
    271  * The `attach` and `detach` methods adjust reference counts on value
    272  * objects. They support copy-on-write and safe memory reclamation
    273  * needed for multi-version concurrency. The methods are only called
    274  * when the `dns_qpmulti_t` mutex is held.
    275  *
    276  * Note: When a value object reference count is greater than one, the
    277  * object is in use by concurrent readers so it must not be modified. A
    278  * refcount equal to one does not indicate whether or not the object is
    279  * mutable: its refcount can be 1 while it is only in use by readers (and
    280  * must be left unchanged), or newly created by a writer (and therefore
    281  * mutable).
    282  *
    283  * The `makekey` method fills in a `dns_qpkey_t` corresponding to a
    284  * value object stored in the qp-trie. It returns the length of the
    285  * key, which must be less than `sizeof(dns_qpkey_t)`. This method
    286  * will typically call dns_qpkey_fromname() with a name stored in the
    287  * value object.
    288  *
    289  * For logging and tracing, the `triename` method copies a human-
    290  * readable identifier into `buf` which has max length `size`.
    291  */
    292 typedef struct dns_qpmethods {
    293 	void (*attach)(void *uctx, void *pval, uint32_t ival);
    294 	void (*detach)(void *uctx, void *pval, uint32_t ival);
    295 	size_t (*makekey)(dns_qpkey_t key, void *uctx, void *pval,
    296 			  uint32_t ival);
    297 	void (*triename)(void *uctx, char *buf, size_t size);
    298 } dns_qpmethods_t;
    299 
    300 /*%
    301  * Buffers for use by the `triename()` method need to be large enough
    302  * to hold a zone name and a few descriptive words.
    303  */
    304 #define DNS_QP_TRIENAME_MAX 300
    305 
    306 /*%
    307  * A container for the counters returned by `dns_qp_memusage()`
    308  */
    309 typedef struct dns_qp_memusage {
    310 	void  *uctx;	    /*%< qp-trie method context */
    311 	size_t leaves;	    /*%< values in the trie */
    312 	size_t live;	    /*%< nodes in use */
    313 	size_t used;	    /*%< allocated nodes */
    314 	size_t hold;	    /*%< nodes retained for readers */
    315 	size_t free;	    /*%< nodes to be reclaimed */
    316 	size_t node_size;   /*%< in bytes */
    317 	size_t chunk_count; /*%< allocated chunks */
    318 	size_t bytes;	    /*%< total memory in chunks and metadata */
    319 	bool   fragmented;  /*%< trie needs compaction */
    320 } dns_qp_memusage_t;
    321 
    322 /*%
    323  * Choice of mode for `dns_qp_compact()`
    324  */
    325 typedef enum dns_qpgc {
    326 	DNS_QPGC_MAYBE,
    327 	DNS_QPGC_NOW,
    328 	DNS_QPGC_ALL,
    329 } dns_qpgc_t;
    330 
    331 /***********************************************************************
    332  *
    333  *  functions - create, destory, enquire
    334  */
    335 
    336 void
    337 dns_qp_create(isc_mem_t *mctx, const dns_qpmethods_t *methods, void *uctx,
    338 	      dns_qp_t **qptp);
    339 /*%<
    340  * Create a single-threaded qp-trie.
    341  *
    342  * Requires:
    343  * \li  `mctx` is a pointer to a valid memory context.
    344  * \li  all the methods are non-NULL
    345  * \li  `qptp != NULL && *qptp == NULL`
    346  *
    347  * Ensures:
    348  * \li  `*qptp` is a pointer to a valid single-threaded qp-trie
    349  */
    350 
    351 void
    352 dns_qp_destroy(dns_qp_t **qptp);
    353 /*%<
    354  * Destroy a single-threaded qp-trie.
    355  *
    356  * Requires:
    357  * \li  `qptp != NULL`
    358  * \li  `*qptp` is a pointer to a valid single-threaded qp-trie
    359  *
    360  * Ensures:
    361  * \li  all memory allocated by the qp-trie has been released
    362  * \li  `*qptp` is NULL
    363  */
    364 
    365 void
    366 dns_qpmulti_create(isc_mem_t *mctx, const dns_qpmethods_t *methods, void *uctx,
    367 		   dns_qpmulti_t **qpmp);
    368 /*%<
    369  * Create a multi-threaded qp-trie.
    370  *
    371  * Requires:
    372  * \li  `mctx` is a pointer to a valid memory context.
    373  * \li  all the methods are non-NULL
    374  * \li  `qpmp != NULL && *qpmp == NULL`
    375  *
    376  * Ensures:
    377  * \li  `*qpmp` is a pointer to a valid multi-threaded qp-trie
    378  */
    379 
    380 void
    381 dns_qpmulti_destroy(dns_qpmulti_t **qpmp);
    382 /*%<
    383  * Destroy a multi-threaded qp-trie.
    384  *
    385  * Requires:
    386  * \li  `qptp != NULL`
    387  * \li  `*qptp` is a pointer to a valid multi-threaded qp-trie
    388  * \li  there are no write or update transactions in progress
    389  * \li  no snapshots exist
    390  *
    391  * Ensures:
    392  * \li  all memory allocated by the qp-trie has been released
    393  * \li  `*qpmp` is NULL
    394  */
    395 
    396 void
    397 dns_qp_compact(dns_qp_t *qp, dns_qpgc_t mode);
    398 /*%<
    399  * Defragment the qp-trie and release unused memory.
    400  *
    401  * When modifications make a trie too fragmented, it is automatically
    402  * compacted. However, automatic compaction is limited when a
    403  * multithreaded trie has lots of immutable memory from past
    404  * transactions, and lightweight write transactions do not compact on
    405  * commit like heavyweight update transactions.
    406  *
    407  * This function can be used with a single-threaded qp-trie and during a
    408  * transaction on a multi-threaded trie.
    409  *
    410  * \li	If `mode == DNS_QPGC_MAYBE`, the trie is cleaned if it is fragmented
    411  *
    412  * \li	If `mode == DNS_QPGC_NOW`, the trie is cleaned while avoiding
    413  *	unnecessary work
    414  *
    415  * \li	If `mode == DNS_QPGC_ALL`, the entire trie is compacted
    416  *
    417  * Requires:
    418  * \li  `qp` is a pointer to a valid qp-trie
    419  */
    420 
    421 void
    422 dns_qp_gctime(uint64_t *compact_us, uint64_t *recover_us,
    423 	      uint64_t *rollback_us);
    424 /*%<
    425  * Get the total times spent on garbage collection in microseconds.
    426  *
    427  * These counters are global, covering every qp-trie in the program.
    428  *
    429  * XXXFANF This is a placeholder until we can record times in histograms.
    430  */
    431 
    432 dns_qp_memusage_t
    433 dns_qp_memusage(dns_qp_t *qp);
    434 /*%<
    435  * Get the memory counters from a qp-trie
    436  *
    437  * Requires:
    438  * \li  `qp` is a pointer to a valid qp-trie
    439  *
    440  * Returns:
    441  * \li  a `dns_qp_memusage_t` structure described above
    442  */
    443 
    444 dns_qp_memusage_t
    445 dns_qpmulti_memusage(dns_qpmulti_t *multi);
    446 /*%<
    447  * Get the memory counters from multi-threaded qp-trie outside the
    448  * context of a transaction.
    449  *
    450  * Requires:
    451  * \li  `multi` is a pointer to a valid dns_qpmulti_t
    452  *
    453  * Returns:
    454  * \li  a `dns_qp_memusage_t` structure described above
    455  */
    456 
    457 /***********************************************************************
    458  *
    459  *  functions - search, modify
    460  */
    461 
    462 /*
    463  * XXXFANF todo, based on what we discover BIND needs
    464  *
    465  * more fancy searches: lexicographic predecessor (for NSEC),
    466  * successor (for modification-safe iteration), etc.
    467  *
    468  * do we need specific lookup functions to find out if the
    469  * returned value is readonly or mutable?
    470  *
    471  * richer modification such as dns_qp_replace{key,name}
    472  */
    473 
    474 size_t
    475 dns_qpkey_fromname(dns_qpkey_t key, const dns_name_t *name);
    476 /*%<
    477  * Convert a DNS name into a trie lookup key.
    478  *
    479  * Requires:
    480  * \li  `name` is a pointer to a valid `dns_name_t`
    481  *
    482  * Ensures:
    483  * \li	returned length is less than `sizeof(dns_qpkey_t)`
    484  *
    485  * Returns:
    486  * \li  the length of the key
    487  */
    488 
    489 void
    490 dns_qpkey_toname(const dns_qpkey_t key, size_t keylen, dns_name_t *name);
    491 /*%<
    492  * Convert a trie lookup key back into a DNS name.
    493  *
    494  * Requires:
    495  * \li  `name` is a pointer to a valid `dns_name_t`
    496  * \li  `name->buffer` is not NULL
    497  * \li  `name->offsets` is not NULL
    498  */
    499 
    500 isc_result_t
    501 dns_qp_getkey(dns_qpreadable_t qpr, const dns_qpkey_t search_key,
    502 	      size_t search_keylen, void **pval_r, uint32_t *ival_r);
    503 /*%<
    504  * Find a leaf in a qp-trie that matches the given search key
    505  *
    506  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    507  * are not null, unless the return value is ISC_R_NOTFOUND.
    508  *
    509  * Requires:
    510  * \li  `qpr` is a pointer to a readable qp-trie
    511  * \li	`search_keylen < sizeof(dns_qpkey_t)`
    512  *
    513  * Returns:
    514  * \li  ISC_R_NOTFOUND if the trie has no leaf with a matching key
    515  * \li  ISC_R_SUCCESS if the leaf was found
    516  */
    517 
    518 isc_result_t
    519 dns_qp_getname(dns_qpreadable_t qpr, const dns_name_t *name, void **pval_r,
    520 	       uint32_t *ival_r);
    521 /*%<
    522  * Find a leaf in a qp-trie that matches the given DNS name
    523  *
    524  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    525  * are not null, unless the return value is ISC_R_NOTFOUND.
    526  *
    527  * Requires:
    528  * \li  `qpr` is a pointer to a readable qp-trie
    529  * \li  `name` is a pointer to a valid `dns_name_t`
    530  *
    531  * Returns:
    532  * \li  ISC_R_NOTFOUND if the trie has no leaf with a matching key
    533  * \li  ISC_R_SUCCESS if the leaf was found
    534  */
    535 
    536 isc_result_t
    537 dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
    538 	      dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain,
    539 	      void **pval_r, uint32_t *ival_r);
    540 /*%<
    541  * Look up a leaf in a qp-trie that is equal to, or an ancestor domain of,
    542  * 'name'.
    543  *
    544  * If 'foundname' is not NULL, it will be updated to contain the name
    545  * that was found (if any). The return code, ISC_R_SUCCESS or
    546  * DNS_R_PARTIALMATCH, indicates whether the name found is the name
    547  * that was requested, or an ancestor. If the result is ISC_R_NOTFOUND,
    548  * 'foundname' will not be updated. (NOTE: the name will be constructed
    549  * from the QP key of the found node, and this can be time-consuming.
    550  * In performance-critical code, it is faster to store a copy of the
    551  * name in the node data and use that instead of passing 'foundname'.)
    552  *
    553  * If 'chain' is not NULL, it is updated to contain a QP chain with
    554  * references to the populated nodes in the tree between the root and
    555  * the name that was found. If the return code is DNS_R_PARTIALMATCH
    556  * then the chain terminates at the closest ancestor found; if it is
    557  * ISC_R_SUCCESS then it terminates at the name that was requested.
    558  * If the result is ISC_R_NOTFOUND, 'chain' will not be updated.
    559  *
    560  * If 'iter' is not NULL, it will be updated to point to a QP iterator
    561  * which is pointed at the searched-for name if it exists in the trie,
    562  * or the closest predecessor if it doesn't.
    563  *
    564  * The leaf data for the node that was found will be assigned to
    565  * whichever of `*pval_r` and `*ival_r` are not NULL, unless the
    566  * return value is ISC_R_NOTFOUND.
    567  *
    568  * Requires:
    569  * \li  `qpr` is a pointer to a readable qp-trie
    570  * \li  `name` is a pointer to a valid `dns_name_t`
    571  * \li  `foundname` is a pointer to a valid `dns_name_t` with
    572  *       buffer and offset space available, or is NULL
    573  *
    574  * Returns:
    575  * \li  ISC_R_SUCCESS if an exact match was found
    576  * \li  ISC_R_PARTIALMATCH if an ancestor domain was found
    577  * \li  ISC_R_NOTFOUND if no match was found
    578  */
    579 
    580 isc_result_t
    581 dns_qp_insert(dns_qp_t *qp, void *pval, uint32_t ival);
    582 /*%<
    583  * Insert a leaf into a qp-trie
    584  *
    585  * Requires:
    586  * \li  `qp` is a pointer to a valid qp-trie
    587  * \li  `pval != NULL`
    588  * \li  `alignof(pval) >= 4`
    589  *
    590  * Returns:
    591  * \li  ISC_R_EXISTS if the trie already has a leaf with the same key
    592  * \li  ISC_R_SUCCESS if the leaf was added to the trie
    593  */
    594 
    595 isc_result_t
    596 dns_qp_deletekey(dns_qp_t *qp, const dns_qpkey_t key, size_t keylen,
    597 		 void **pval_r, uint32_t *ival_r);
    598 /*%<
    599  * Delete a leaf from a qp-trie that matches the given key
    600  *
    601  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    602  * are not null, unless the return value is ISC_R_NOTFOUND.
    603  *
    604  * Requires:
    605  * \li  `qp` is a pointer to a valid qp-trie
    606  * \li	`keylen < sizeof(dns_qpkey_t)`
    607  *
    608  * Returns:
    609  * \li  ISC_R_NOTFOUND if the trie has no leaf with a matching key
    610  * \li  ISC_R_SUCCESS if the leaf was deleted from the trie
    611  */
    612 
    613 isc_result_t
    614 dns_qp_deletename(dns_qp_t *qp, const dns_name_t *name, void **pval_r,
    615 		  uint32_t *ival_r);
    616 /*%<
    617  * Delete a leaf from a qp-trie that matches the given DNS name
    618  *
    619  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    620  * are not null, unless the return value is ISC_R_NOTFOUND.
    621  *
    622  * Requires:
    623  * \li  `qp` is a pointer to a valid qp-trie
    624  * \li  `name` is a pointer to a valid qp-trie
    625  *
    626  * Returns:
    627  * \li  ISC_R_NOTFOUND if the trie has no leaf with a matching name
    628  * \li  ISC_R_SUCCESS if the leaf was deleted from the trie
    629  */
    630 
    631 void
    632 dns_qpiter_init(dns_qpreadable_t qpr, dns_qpiter_t *qpi);
    633 /*%<
    634  * Initialize an iterator
    635  *
    636  * SAFETY NOTE: If `qpr` is a `dns_qp_t`, it is not safe to modify the
    637  * trie during iteration. If `qpr` is a `dns_qpread_t` or `dns_qpsnap_t`
    638  * then (like any other read-only access) modifications will not affect
    639  * iteration.
    640  *
    641  * Requires:
    642  * \li  `qp` is a pointer to a valid qp-trie
    643  * \li  `qpi` is a pointer to a qp iterator
    644  */
    645 
    646 isc_result_t
    647 dns_qpiter_next(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
    648 		uint32_t *ival_r);
    649 isc_result_t
    650 dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
    651 		uint32_t *ival_r);
    652 /*%<
    653  * Iterate forward/backward through a QP trie in lexicographic order.
    654  *
    655  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    656  * are not null, unless the return value is ISC_R_NOMORE. Similarly,
    657  * if `name` is not null, it is updated to contain the node name.
    658  *
    659  * NOTE: see the safety note under `dns_qpiter_init()`.
    660  *
    661  * For example,
    662  *
    663  *	dns_qpiter_t qpi;
    664  *	void *pval;
    665  *	uint32_t ival;
    666  *	dns_qpiter_init(qp, &qpi);
    667  *	while (dns_qpiter_next(&qpi, &pval, &ival) == ISC_R_SUCCESS) {
    668  *		// do something with pval and ival
    669  *	}
    670  *
    671  * Requires:
    672  * \li  `qpi` is a pointer to a valid qp iterator
    673  *
    674  * Returns:
    675  * \li  ISC_R_SUCCESS if a leaf was found and pval_r and ival_r were set
    676  * \li  ISC_R_NOMORE otherwise
    677  */
    678 
    679 isc_result_t
    680 dns_qpiter_current(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r,
    681 		   uint32_t *ival_r);
    682 /*%<
    683  * Sets the values of `name`, `pval_r` and `ival_r` to those at the
    684  * node currently pointed to by `qpi`, but without moving the iterator
    685  * in either direction. If the iterator is not currently pointed at a
    686  * leaf node, ISC_R_FAILURE is returned.
    687  * Requires:
    688  *
    689  * \li  `qpi` is a pointer to a valid qp iterator
    690  *
    691  * Returns:
    692  * \li  ISC_R_SUCCESS if a leaf was found and pval_r and ival_r were set
    693  * \li  ISC_R_FAILURE if the iterator is not initialized or not pointing
    694  *      at a leaf node
    695  */
    696 
    697 void
    698 dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain);
    699 /*%<
    700  * Initialize a QP chain.
    701  *
    702  * Requires:
    703  * \li  `qpr` is a pointer to a valid qp-trie
    704  * \li  `chain` is not NULL
    705  */
    706 
    707 unsigned int
    708 dns_qpchain_length(dns_qpchain_t *chain);
    709 /*%<
    710  * Returns the length of a QP chain.
    711  *
    712  * Requires:
    713  * \li  `chain` is a pointer to an initialized QP chain object
    714  */
    715 
    716 void
    717 dns_qpchain_node(dns_qpchain_t *chain, unsigned int level, dns_name_t *name,
    718 		 void **pval_r, uint32_t *ival_r);
    719 /*%<
    720  * Sets 'name' to the name of the leaf referenced at `chain->stack[level]`.
    721  *
    722  * The leaf values are assigned to whichever of `*pval_r` and `*ival_r`
    723  * are not null.
    724  *
    725  * Requires:
    726  * \li  `chain` is a pointer to an initialized QP chain object
    727  * \li  `level` is less than `chain->len`
    728  */
    729 
    730 /***********************************************************************
    731  *
    732  *  functions - transactions
    733  */
    734 
    735 void
    736 dns_qpmulti_query(dns_qpmulti_t *multi, dns_qpread_t *qpr);
    737 /*%<
    738  * Start a lightweight (brief) read-only transaction
    739  *
    740  * The `dns_qpmulti_query()` function must be called from an isc_loop
    741  * thread and its 'qpr' argument must be allocated on the stack.
    742  *
    743  * Requires:
    744  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    745  * \li  `qpr != NULL`
    746  *
    747  * Returns:
    748  * \li  `qpr` is a valid read-only qp-trie handle
    749  */
    750 
    751 void
    752 dns_qpread_destroy(dns_qpmulti_t *multi, dns_qpread_t *qpr);
    753 /*%<
    754  * End a lightweight query or read transaction.
    755  *
    756  * Requires:
    757  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    758  * \li  `qpr` is a read-only qp-trie handle obtained from `multi`
    759  *
    760  * Returns:
    761  * \li  `qpr` is invalidated
    762  */
    763 
    764 void
    765 dns_qpmulti_snapshot(dns_qpmulti_t *multi, dns_qpsnap_t **qpsp);
    766 /*%<
    767  * Start a heavyweight (long) read-only transaction
    768  *
    769  * This function briefly takes and releases the modification mutex
    770  * while allocating a copy of the trie's metadata. While the snapshot
    771  * exists it does not interfere with other read-only or read-write
    772  * transactions on the trie.
    773  *
    774  * Requires:
    775  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    776  * \li  `qpsp != NULL`
    777  * \li  `*qpsp == NULL`
    778  *
    779  * Returns:
    780  * \li  `*qpsp` is a pointer to a snapshot obtained from `multi`
    781  */
    782 
    783 void
    784 dns_qpsnap_destroy(dns_qpmulti_t *multi, dns_qpsnap_t **qpsp);
    785 /*%<
    786  * End a heavyweight read transaction
    787  *
    788  * Requires:
    789  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    790  * \li  `qpsp != NULL`
    791  * \li  `*qpsp` is a pointer to a snapshot obtained from `multi`
    792  *
    793  * Returns:
    794  * \li  `*qpsp == NULL`
    795  */
    796 
    797 void
    798 dns_qpmulti_update(dns_qpmulti_t *multi, dns_qp_t **qptp);
    799 /*%<
    800  * Start a heavyweight write transaction
    801  *
    802  * This style of transaction allocates a copy of the trie's metadata to
    803  * support rollback, and it aims to minimize the memory usage of the
    804  * trie between transactions. The trie is compacted when the transaction
    805  * commits, and any partly-used chunk is shrunk to fit.
    806  *
    807  * During the transaction, the modification mutex is held.
    808  *
    809  * Requires:
    810  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    811  * \li  `qptp != NULL`
    812  * \li  `*qptp == NULL`
    813  *
    814  * Returns:
    815  * \li  `*qptp` is a pointer to the modifiable qp-trie inside `multi`
    816  */
    817 
    818 void
    819 dns_qpmulti_write(dns_qpmulti_t *multi, dns_qp_t **qptp);
    820 /*%<
    821  * Start a lightweight write transaction
    822  *
    823  * This style of transaction does not need extra allocations in addition
    824  * to the ones required by insert and delete operations. It is intended
    825  * for a large trie that gets frequent small writes, such as a DNS
    826  * cache.
    827  *
    828  * A sequence of lightweight write transactions can accumulate
    829  * garbage that the automatic compact/recycle cannot reclaim.
    830  * To reclaim this space, you can use the `dns_qp_memusage
    831  * fragmented` flag to trigger a call to dns_qp_compact(), or you
    832  * can use occasional update transactions to compact the trie.
    833  *
    834  * During the transaction, the modification mutex is held.
    835  *
    836  * Requires:
    837  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    838  * \li  `qptp != NULL`
    839  * \li  `*qptp == NULL`
    840  *
    841  * Returns:
    842  * \li  `*qptp` is a pointer to the modifiable qp-trie inside `multi`
    843  */
    844 
    845 void
    846 dns_qpmulti_commit(dns_qpmulti_t *multi, dns_qp_t **qptp);
    847 /*%<
    848  * Complete a modification transaction
    849  *
    850  * Apart from memory management logistics, the commit itself only
    851  * requires flipping the read pointer inside `multi` from the old
    852  * version of the trie to the new version. Readers are not blocked.
    853  *
    854  * This function releases the modification mutex after the post-commit
    855  * memory reclamation is completed.
    856  *
    857  * Requires:
    858  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    859  * \li  `qptp != NULL`
    860  * \li  `*qptp` is a pointer to the modifiable qp-trie inside `multi`
    861  *
    862  * Returns:
    863  * \li  `*qptp == NULL`
    864  */
    865 
    866 void
    867 dns_qpmulti_rollback(dns_qpmulti_t *multi, dns_qp_t **qptp);
    868 /*%<
    869  * Abandon an update transaction
    870  *
    871  * This function reclaims the memory allocated during the transaction
    872  * and releases the modification mutex.
    873  *
    874  * Requires:
    875  * \li  `multi` is a pointer to a valid multi-threaded qp-trie
    876  * \li  `qptp != NULL`
    877  * \li  `*qptp` is a pointer to the modifiable qp-trie inside `multi`
    878  * \li  `*qptp` was obtained from `dns_qpmulti_update()`
    879  *
    880  * Returns:
    881  * \li  `*qptp == NULL`
    882  */
    883 
    884 /**********************************************************************/
    885