https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236
--- Comment #2 from Mukund Sivaraman <muks at banu dot com> ---
This is the C function (so you can compare notes from the next comment):
isc_result_t
dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
isc_region_t *region, unsigned int reservelen)
{
/*
* Use &removed as a sentinal pointer for duplicate
* rdata as rdata.data == NULL is valid.
*/
static unsigned char removed;
struct xrdata *x;
unsigned char *rawbuf;
#if DNS_RDATASET_FIXED
unsigned char *offsetbase;
#endif
unsigned int buflen;
isc_result_t result;
unsigned int nitems;
unsigned int nalloc;
unsigned int i;
#if DNS_RDATASET_FIXED
unsigned int *offsettable;
#endif
unsigned int length;
buflen = reservelen + 2;
nalloc = dns_rdataset_count(rdataset);
nitems = nalloc;
if (nitems == 0 && rdataset->type != 0)
return (ISC_R_FAILURE);
if (nalloc > 0xffff)
return (ISC_R_NOSPACE);
if (nalloc != 0) {
x = isc_mem_get(mctx, nalloc * sizeof(struct xrdata));
if (x == NULL)
return (ISC_R_NOMEMORY);
} else
x = NULL;
/*
* Save all of the rdata members into an array.
*/
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
goto free_rdatas;
for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) {
INSIST(result == ISC_R_SUCCESS);
dns_rdata_init(&x[i].rdata);
dns_rdataset_current(rdataset, &x[i].rdata);
INSIST(x[i].rdata.data != &removed);
#if DNS_RDATASET_FIXED
x[i].order = i;
#endif
result = dns_rdataset_next(rdataset);
}
if (result != ISC_R_NOMORE)
goto free_rdatas;
if (i != nalloc) {
/*
* Somehow we iterated over fewer rdatas than
* dns_rdataset_count() said there were!
*/
result = ISC_R_FAILURE;
goto free_rdatas;
}
/*
* Put into DNSSEC order.
*/
qsort(x, nalloc, sizeof(struct xrdata), compare_rdata);
/*
* Remove duplicates and compute the total storage required.
*
* If an rdata is not a duplicate, accumulate the storage size
* required for the rdata. We do not store the class, type, etc,
* just the rdata, so our overhead is 2 bytes for the number of
* records, and 8 for each rdata, (length(2), offset(4) and order(2))
* and then the rdata itself.
*/
for (i = 1; i < nalloc; i++) {
if (compare_rdata(&x[i-1].rdata, &x[i].rdata) == 0) {
x[i-1].rdata.data = &removed;
#if DNS_RDATASET_FIXED
/*
* Preserve the least order so A, B, A -> A, B
* after duplicate removal.
*/
if (x[i-1].order < x[i].order)
x[i].order = x[i-1].order;
#endif
nitems--;
} else {
#if DNS_RDATASET_FIXED
buflen += (8 + x[i-1].rdata.length);
#else
buflen += (2 + x[i-1].rdata.length);
#endif
/*
* Provide space to store the per RR meta data.
*/
if (rdataset->type == dns_rdatatype_rrsig)
buflen++;
}
}
/*
* Don't forget the last item!
*/
if (nalloc != 0) {
#if DNS_RDATASET_FIXED
buflen += (8 + x[i-1].rdata.length);
#else
buflen += (2 + x[i-1].rdata.length);
#endif
}
/*
* Provide space to store the per RR meta data.
*/
if (rdataset->type == dns_rdatatype_rrsig)
buflen++;
/*
* Ensure that singleton types are actually singletons.
*/
if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) {
/*
* We have a singleton type, but there's more than one
* RR in the rdataset.
*/
result = DNS_R_SINGLETON;
goto free_rdatas;
}
/*
* Allocate the memory, set up a buffer, start copying in
* data.
*/
rawbuf = isc_mem_get(mctx, buflen);
if (rawbuf == NULL) {
result = ISC_R_NOMEMORY;
goto free_rdatas;
}
#if DNS_RDATASET_FIXED
/* Allocate temporary offset table. */
offsettable = isc_mem_get(mctx, nalloc * sizeof(unsigned int));
if (offsettable == NULL) {
isc_mem_put(mctx, rawbuf, buflen);
result = ISC_R_NOMEMORY;
goto free_rdatas;
}
memset(offsettable, 0, nalloc * sizeof(unsigned int));
#endif
region->base = rawbuf;
region->length = buflen;
rawbuf += reservelen;
#if DNS_RDATASET_FIXED
offsetbase = rawbuf;
#endif
*rawbuf++ = (nitems & 0xff00) >> 8;
*rawbuf++ = (nitems & 0x00ff);
#if DNS_RDATASET_FIXED
/* Skip load order table. Filled in later. */
rawbuf += nitems * 4;
#endif
for (i = 0; i < nalloc; i++) {
if (x[i].rdata.data == &removed)
continue;
#if DNS_RDATASET_FIXED
offsettable[x[i].order] = rawbuf - offsetbase;
#endif
length = x[i].rdata.length;
if (rdataset->type == dns_rdatatype_rrsig)
length++;
INSIST(length <= 0xffff);
*rawbuf++ = (length & 0xff00) >> 8;
*rawbuf++ = (length & 0x00ff);
#if DNS_RDATASET_FIXED
rawbuf += 2; /* filled in later */
#endif
/*
* Store the per RR meta data.
*/
if (rdataset->type == dns_rdatatype_rrsig) {
*rawbuf++ |= (x[i].rdata.flags & DNS_RDATA_OFFLINE) ?
DNS_RDATASLAB_OFFLINE : 0;
}
memmove(rawbuf, x[i].rdata.data, x[i].rdata.length);
rawbuf += x[i].rdata.length;
}
#if DNS_RDATASET_FIXED
fillin_offsets(offsetbase, offsettable, nalloc);
isc_mem_put(mctx, offsettable, nalloc * sizeof(unsigned int));
#endif
result = ISC_R_SUCCESS;
free_rdatas:
if (x != NULL)
isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata));
return (result);
}