Commit 3effe275a6 for bind
commit 3effe275a6fac899cfe1f956525cf9c5e4a8ea94
Author: Alessio Podda <alessio@isc.org>
Date: Wed Aug 19 15:37:59 2026 +0200
Linear rdatavec subtract
Apply the same zip merge algorithm to vecheader_subtract().
diff --git a/lib/dns/rdatavec.c b/lib/dns/rdatavec.c
index d5bf4eea57..ac66b70e0e 100644
--- a/lib/dns/rdatavec.c
+++ b/lib/dns/rdatavec.c
@@ -400,37 +400,6 @@ dns_rdatavec_count(dns_vecheader_t *header) {
return rdatavec_count(header);
}
-/*
- * Make the dns_rdata_t 'rdata' refer to the vec item
- * beginning at '*current' (which is part of a vec of type
- * 'type' and class 'rdclass') and advance '*current' to
- * point to the next item in the vec.
- */
-static void
-rdata_from_vecitem(unsigned char **current, dns_rdataclass_t rdclass,
- dns_rdatatype_t type, dns_rdata_t *rdata) {
- unsigned char *tcurrent = *current;
- isc_region_t region;
- bool offline = false;
- uint16_t length = get_uint16(tcurrent);
-
- if (type == dns_rdatatype_rrsig) {
- if ((*tcurrent & DNS_RDATAVEC_OFFLINE) != 0) {
- offline = true;
- }
- length--;
- tcurrent++;
- }
- region.length = length;
- region.base = tcurrent;
- tcurrent += region.length;
- dns_rdata_fromregion(rdata, rdclass, type, ®ion);
- if (offline) {
- rdata->flags |= DNS_RDATA_OFFLINE;
- }
- *current = tcurrent;
-}
-
static void
rdata_to_vecitem(unsigned char **current, dns_rdatatype_t type,
dns_rdata_t *rdata) {
@@ -450,12 +419,6 @@ rdata_to_vecitem(unsigned char **current, dns_rdatatype_t type,
*current = p;
}
-typedef struct vecinfo {
- unsigned char *pos;
- dns_rdata_t rdata;
- bool dup;
-} vecinfo_t;
-
typedef struct vecmerge_iter {
rdatavec_iter_t left;
rdatavec_iter_t right;
@@ -504,6 +467,34 @@ vecmerge_next(vecmerge_iter_t *iter, dns_rdata_t *rdata) {
return true;
}
+static bool
+vecsubtract_next(vecmerge_iter_t *iter, dns_rdata_t *rdata) {
+ while (iter->left.iter_count > 0) {
+ dns_rdata_t left = DNS_RDATA_INIT, right = DNS_RDATA_INIT;
+ bool have_right = iter->right.iter_count > 0;
+ int cmp;
+
+ vecheader_current(&iter->left, &left);
+ if (have_right) {
+ vecheader_current(&iter->right, &right);
+ }
+
+ cmp = have_right ? dns_rdata_compare(&left, &right) : -1;
+ (void)vecheader_next(cmp <= 0 ? &iter->left : &iter->right);
+ if (cmp == 0) {
+ (void)vecheader_next(&iter->right);
+ }
+
+ if (cmp < 0) {
+ dns_rdata_reset(rdata);
+ *rdata = left;
+ return true;
+ }
+ }
+
+ return false;
+}
+
isc_result_t
dns_rdatavec_merge(dns_vecheader_t *oheader, dns_vecheader_t *nheader,
isc_mem_t *mctx, dns_rdataclass_t rdclass,
@@ -621,90 +612,42 @@ dns_rdatavec_subtract(dns_vecheader_t *oheader, dns_vecheader_t *sheader,
isc_mem_t *mctx, dns_rdataclass_t rdclass,
dns_rdatatype_t type, unsigned int flags,
dns_vecheader_t **theaderp) {
- isc_result_t result = ISC_R_SUCCESS;
- unsigned char *ocurrent = NULL, *scurrent = NULL;
- unsigned char *tstart = NULL, *tcurrent = NULL;
+ unsigned char *tcurrent = NULL;
unsigned int ocount, scount;
- uint32_t tlength;
- unsigned int tcount = 0, rcount = 0;
- vecinfo_t *oinfo = NULL, *sinfo = NULL;
+ unsigned int tcount = 0, rcount;
+ size_t rlength = 0, tlength;
+ vecmerge_iter_t iter;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
REQUIRE(theaderp != NULL && *theaderp == NULL);
REQUIRE(oheader != NULL && sheader != NULL);
- ocurrent = rdatavec_data(oheader);
ocount = rdatavec_count(oheader);
-
- scurrent = rdatavec_data(sheader);
scount = rdatavec_count(sheader);
INSIST(ocount > 0 && scount > 0);
- /* Get info about the rdatas being subtracted */
- sinfo = isc_mem_cget(mctx, scount, sizeof(struct vecinfo));
- for (size_t i = 0; i < scount; i++) {
- sinfo[i].pos = scurrent;
- dns_rdata_init(&sinfo[i].rdata);
- rdata_from_vecitem(&scurrent, rdclass, type, &sinfo[i].rdata);
- }
-
- /*
- * Figure out the target length. Start with the header,
- * plus 2 octets for the count.
- */
- tlength = header_size(oheader) + 2;
+ vecmerge_first(&iter, oheader, sheader, rdclass);
- /*
- * Add the length of the rdatas in the old vec that
- * aren't being subtracted.
- */
- oinfo = isc_mem_cget(mctx, ocount, sizeof(struct vecinfo));
- for (size_t i = 0; i < ocount; i++) {
- bool matched = false;
-
- oinfo[i].pos = ocurrent;
- dns_rdata_init(&oinfo[i].rdata);
- rdata_from_vecitem(&ocurrent, rdclass, type, &oinfo[i].rdata);
-
- for (size_t j = 0; j < scount; j++) {
- if (sinfo[j].dup) {
- continue;
- } else if (dns_rdata_compare(&oinfo[i].rdata,
- &sinfo[j].rdata) == 0)
- {
- matched = true;
- oinfo[i].dup = sinfo[j].dup = true;
- break;
- }
- }
+ while (vecsubtract_next(&iter, &rdata)) {
+ rlength += sizeof(uint16_t) + rdata.length +
+ (type == dns_rdatatype_rrsig);
+ tcount++;
+ }
+ rcount = ocount - tcount;
- if (matched) {
- /* This item will be subtracted. */
- rcount++;
- } else {
- /*
- * This rdata wasn't in the vec to be subtracted,
- * so copy it to the target. Add its length to
- * tlength and increment tcount.
- */
- tlength += (uint32_t)(ocurrent - oinfo[i].pos);
- if (tlength - header_size(oheader) - 2 >
- DNS_RDATA_MAXLENGTH)
- {
- CLEANUP(ISC_R_NOSPACE);
- }
- tcount++;
- }
+ if (rlength > DNS_RDATA_MAXLENGTH) {
+ return ISC_R_NOSPACE;
}
/*
- * If the EXACT flag wasn't set, check that all the records that
+ * If the EXACT flag was set, check that all the records that
* were to be subtracted actually did exist in the original vec.
* (The numeric check works here because rdatavecs do not contain
* duplicates.)
*/
if ((flags & DNS_RDATAVEC_EXACT) != 0 && rcount != scount) {
- CLEANUP(DNS_R_NOTEXACT);
+ return DNS_R_NOTEXACT;
}
/*
@@ -712,20 +655,22 @@ dns_rdatavec_subtract(dns_vecheader_t *oheader, dns_vecheader_t *sheader,
* create a new buffer, just return.
*/
if (tcount == 0) {
- CLEANUP(DNS_R_NXRRSET);
+ return DNS_R_NXRRSET;
}
/*
* If nothing is going to change, stop.
*/
if (rcount == 0) {
- CLEANUP(DNS_R_UNCHANGED);
+ return DNS_R_UNCHANGED;
}
+ tlength = header_size(oheader) + 2 + rlength;
+
/*
* Allocate the target buffer and copy the old vec's header.
*/
- tstart = isc_mem_get(mctx, tlength);
+ unsigned char *tstart = isc_mem_get(mctx, tlength);
dns_vecheader_t *as_header = (dns_vecheader_t *)tstart;
uint16_t attrs = RESIGN(oheader) ? DNS_VECHEADERATTR_RESIGN : 0;
*as_header = (dns_vecheader_t){
@@ -751,21 +696,17 @@ dns_rdatavec_subtract(dns_vecheader_t *oheader, dns_vecheader_t *sheader,
/*
* Copy the parts of the old vec that didn't have duplicates.
*/
- for (size_t i = 0; i < ocount; i++) {
- if (!oinfo[i].dup) {
- rdata_to_vecitem(&tcurrent, type, &oinfo[i].rdata);
- }
+ vecmerge_first(&iter, oheader, sheader, rdclass);
+
+ while (vecsubtract_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, sinfo, scount, sizeof(struct vecinfo));
-
- return result;
+ return ISC_R_SUCCESS;
}
void