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);
}

Reply via email to