Commit 6dbe2dadf8 for bind
commit 6dbe2dadf80b0e33ace150e287a65d82736a6069
Author: Alessio Podda <alessio@isc.org>
Date: Tue Aug 18 16:35:03 2026 +0200
Add merge iterator
Abstract the logic of vecheader_merge into an iterator API.
diff --git a/lib/dns/rdatavec.c b/lib/dns/rdatavec.c
index 4c795b49f7..d5bf4eea57 100644
--- a/lib/dns/rdatavec.c
+++ b/lib/dns/rdatavec.c
@@ -454,28 +454,72 @@ typedef struct vecinfo {
unsigned char *pos;
dns_rdata_t rdata;
bool dup;
- uint32_t length;
} vecinfo_t;
+typedef struct vecmerge_iter {
+ rdatavec_iter_t left;
+ rdatavec_iter_t right;
+} vecmerge_iter_t;
+
+static void
+vecmerge_first(vecmerge_iter_t *iter, dns_vecheader_t *left,
+ dns_vecheader_t *right, dns_rdataclass_t rdclass) {
+ INSIST(vecheader_first(&iter->left, left, rdclass) == ISC_R_SUCCESS);
+ INSIST(vecheader_first(&iter->right, right, rdclass) == ISC_R_SUCCESS);
+}
+
+static bool
+vecmerge_next(vecmerge_iter_t *iter, dns_rdata_t *rdata) {
+ bool have_left = iter->left.iter_count > 0;
+ bool have_right = iter->right.iter_count > 0;
+
+ if (!have_left && !have_right) {
+ return false;
+ }
+
+ dns_rdata_reset(rdata);
+
+ if (!have_right) {
+ vecheader_current(&iter->left, rdata);
+ (void)vecheader_next(&iter->left);
+ } else if (!have_left) {
+ vecheader_current(&iter->right, rdata);
+ (void)vecheader_next(&iter->right);
+ } else {
+ dns_rdata_t left = DNS_RDATA_INIT, right = DNS_RDATA_INIT;
+ int cmp;
+
+ vecheader_current(&iter->left, &left);
+ vecheader_current(&iter->right, &right);
+
+ cmp = dns_rdata_compare(&left, &right);
+ *rdata = cmp <= 0 ? left : right;
+
+ (void)vecheader_next(cmp <= 0 ? &iter->left : &iter->right);
+ if (cmp == 0) {
+ (void)vecheader_next(&iter->right);
+ }
+ }
+
+ return true;
+}
+
isc_result_t
dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
isc_mem_t *mctx, dns_rdataclass_t rdclass,
dns_rdatatype_t type, unsigned int flags,
uint32_t maxrrperset, dns_vecheader_t **theaderp) {
- isc_result_t result = ISC_R_SUCCESS;
- unsigned char *ocurrent = NULL, *ncurrent = NULL, *tcurrent = NULL;
+ unsigned char *tcurrent = NULL;
unsigned int ocount, ncount, tcount = 0;
+ unsigned int ndup;
size_t rlength = 0, tlength = 0;
- vecinfo_t *oinfo = NULL, *ninfo = NULL;
- size_t o = 0, n = 0;
+ vecmerge_iter_t iter;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
REQUIRE(theaderp != NULL && *theaderp == NULL);
REQUIRE(oheader != NULL && nheader != NULL);
- ocurrent = rdatavec_data(oheader);
ocount = rdatavec_count(oheader);
-
- ncurrent = rdatavec_data(nheader);
ncount = rdatavec_count(nheader);
INSIST(ocount > 0 && ncount > 0);
@@ -484,99 +528,46 @@ dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
return DNS_R_TOOMANYRECORDS;
}
- /*
- * Allocate both info arrays up front so the cleanup path is
- * always safe to call regardless of where we exit.
- */
- oinfo = isc_mem_cget(mctx, ocount, sizeof(struct vecinfo));
- ninfo = isc_mem_cget(mctx, ncount, sizeof(struct vecinfo));
-
- /*
- * Gather the rdatas in the old vec.
- */
- for (size_t i = 0; i < ocount; i++) {
- oinfo[i].pos = ocurrent;
- dns_rdata_init(&oinfo[i].rdata);
- rdata_from_vecitem(&ocurrent, rdclass, type, &oinfo[i].rdata);
- oinfo[i].length = (uint32_t)(ocurrent - oinfo[i].pos);
- }
-
- /*
- * Gather the rdatas in the new vec.
- */
- for (size_t i = 0; i < ncount; i++) {
- ninfo[i].pos = ncurrent;
- dns_rdata_init(&ninfo[i].rdata);
- rdata_from_vecitem(&ncurrent, rdclass, type, &ninfo[i].rdata);
- ninfo[i].length = (uint32_t)(ncurrent - ninfo[i].pos);
- }
-
- /*
- * The old and new vecs are both sorted in DNSSEC order, so
- * duplicates can be found with a linear walk.
- */
- for (size_t oi = 0, ni = 0; oi < ocount && ni < ncount;) {
- int cmp = dns_rdata_compare(&oinfo[oi].rdata,
- &ninfo[ni].rdata);
- bool is_dup = cmp == 0;
-
- oinfo[oi].dup = ninfo[ni].dup = is_dup;
- oi += cmp <= 0;
- ni += cmp >= 0;
- }
+ vecmerge_first(&iter, oheader, nheader, rdclass);
- /*
- * Compute the target length and count. All old rdatas are copied;
- * only new rdatas that are not duplicated in the old vec are copied.
- */
- for (size_t i = 0; i < ocount; i++) {
- rlength += oinfo[i].length;
- }
-
- for (size_t i = 0; i < ncount; i++) {
- bool not_dup = !ninfo[i].dup;
- rlength += not_dup * ninfo[i].length;
- tcount += not_dup;
+ while (vecmerge_next(&iter, &rdata)) {
+ rlength += sizeof(uint16_t) + rdata.length +
+ (type == dns_rdatatype_rrsig);
+ tcount++;
}
+ ndup = ocount + ncount - tcount;
if (rlength > DNS_RDATA_MAXLENGTH) {
- CLEANUP(ISC_R_NOSPACE);
+ return ISC_R_NOSPACE;
}
- tlength = header_size(oheader) + 2 + rlength;
-
/*
* If the EXACT flag is set, there can't be any rdata in
- * the new vec that was also in the old. If tcount is less
- * than ncount, then we found such a duplicate.
+ * the new vec that was also in the old.
*/
- if (((flags & DNS_RDATAVEC_EXACT) != 0) && (tcount < ncount)) {
- CLEANUP(DNS_R_NOTEXACT);
+ if (((flags & DNS_RDATAVEC_EXACT) != 0) && (ndup != 0)) {
+ return DNS_R_NOTEXACT;
}
/*
* If nothing's being copied in from the new vec, and the
* FORCE flag isn't set, we're done.
*/
- if (tcount == 0 && (flags & DNS_RDATAVEC_FORCE) == 0) {
- CLEANUP(DNS_R_UNCHANGED);
+ if (ndup == ncount && (flags & DNS_RDATAVEC_FORCE) == 0) {
+ return DNS_R_UNCHANGED;
}
- /* Add to tcount the total number of items from the old vec. */
- tcount += ocount;
-
- /* Resposition ncurrent at the first item. */
- ncurrent = rdatavec_data(nheader);
-
/* Single types can't have more than one RR. */
if (tcount > 1 && dns_rdatatype_issingleton(type)) {
- CLEANUP(DNS_R_SINGLETON);
+ return DNS_R_SINGLETON;
}
if (tcount > 0xffff) {
- CLEANUP(ISC_R_NOSPACE);
+ return ISC_R_NOSPACE;
}
+ tlength = header_size(oheader) + 2 + rlength;
+
/*
* Allocate the target buffer and initialize the header.
* Preserve the case of the old header, but the rest from the
@@ -612,42 +603,17 @@ dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
* Now walk the sets together, adding each item in DNSSEC order,
* and skipping over any more dups in the new vec.
*/
- while (o < ocount || n < ncount) {
- bool fromold;
-
- /* Skip to the next non-duplicate in the new vec. */
- for (; n < ncount && ninfo[n].dup; n++)
- ;
+ vecmerge_first(&iter, oheader, nheader, rdclass);
- if (o == ocount) {
- fromold = false;
- } else if (n == ncount) {
- fromold = true;
- } else {
- fromold = dns_rdata_compare(&oinfo[o].rdata,
- &ninfo[n].rdata) < 0;
- }
-
- if (fromold) {
- rdata_to_vecitem(&tcurrent, type, &oinfo[o].rdata);
- if (++o < ocount) {
- /* Skip to the next rdata in the old vec */
- continue;
- }
- } else {
- rdata_to_vecitem(&tcurrent, type, &ninfo[n++].rdata);
- }
+ while (vecmerge_next(&iter, &rdata)) {
+ rdata_to_vecitem(&tcurrent, type, &rdata);
}
INSIST(tcurrent == tstart + tlength);
*theaderp = (dns_vecheader_t *)tstart;
-cleanup:
- isc_mem_cput(mctx, oinfo, ocount, sizeof(struct vecinfo));
- isc_mem_cput(mctx, ninfo, ncount, sizeof(struct vecinfo));
-
- return result;
+ return ISC_R_SUCCESS;
}
isc_result_t