/* $NetBSD: buffer.c,v 1.6 2021/02/19 16:42:19 christos Exp $ */ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include #include #include #include #include void isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) { /* * Make 'b' refer to the 'length'-byte region starting at 'base'. * XXXDCL see the comment in buffer.h about base being const. */ REQUIRE(b != NULL); ISC__BUFFER_INIT(b, base, length); } void isc__buffer_initnull(isc_buffer_t *b) { /* * Initialize a new buffer which has no backing store. This can * later be grown as needed and swapped in place. */ ISC__BUFFER_INIT(b, NULL, 0); } void isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) { /* * Re-initialize the buffer enough to reconfigure the base of the * buffer. We will swap in the new buffer, after copying any * data we contain into the new buffer and adjusting all of our * internal pointers. * * The buffer must not be smaller than the length of the original * buffer. */ REQUIRE(b->length <= length); REQUIRE(base != NULL); REQUIRE(!b->autore); if (b->length > 0U) { (void)memmove(base, b->base, b->length); } b->base = base; b->length = length; } void isc__buffer_invalidate(isc_buffer_t *b) { /* * Make 'b' an invalid buffer. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(!ISC_LINK_LINKED(b, link)); REQUIRE(b->mctx == NULL); ISC__BUFFER_INVALIDATE(b); } void isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) { REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->mctx != NULL); b->autore = enable; } void isc__buffer_region(isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_REGION(b, r); } void isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the used region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_USEDREGION(b, r); } void isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the available region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_AVAILABLEREGION(b, r); } void isc__buffer_add(isc_buffer_t *b, unsigned int n) { /* * Increase the 'used' region of 'b' by 'n' bytes. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used + n <= b->length); ISC__BUFFER_ADD(b, n); } void isc__buffer_subtract(isc_buffer_t *b, unsigned int n) { /* * Decrease the 'used' region of 'b' by 'n' bytes. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used >= n); ISC__BUFFER_SUBTRACT(b, n); } void isc__buffer_clear(isc_buffer_t *b) { /* * Make the used region empty. */ REQUIRE(ISC_BUFFER_VALID(b)); ISC__BUFFER_CLEAR(b); } void isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the consumed region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_CONSUMEDREGION(b, r); } void isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the remaining region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_REMAININGREGION(b, r); } void isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) { /* * Make 'r' refer to the active region of 'b'. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); ISC__BUFFER_ACTIVEREGION(b, r); } void isc__buffer_setactive(isc_buffer_t *b, unsigned int n) { /* * Sets the end of the active region 'n' bytes after current. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->current + n <= b->used); ISC__BUFFER_SETACTIVE(b, n); } void isc__buffer_first(isc_buffer_t *b) { /* * Make the consumed region empty. */ REQUIRE(ISC_BUFFER_VALID(b)); ISC__BUFFER_FIRST(b); } void isc__buffer_forward(isc_buffer_t *b, unsigned int n) { /* * Increase the 'consumed' region of 'b' by 'n' bytes. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->current + n <= b->used); ISC__BUFFER_FORWARD(b, n); } void isc__buffer_back(isc_buffer_t *b, unsigned int n) { /* * Decrease the 'consumed' region of 'b' by 'n' bytes. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(n <= b->current); ISC__BUFFER_BACK(b, n); } void isc_buffer_compact(isc_buffer_t *b) { unsigned int length; void *src; /* * Compact the used region by moving the remaining region so it occurs * at the start of the buffer. The used region is shrunk by the size * of the consumed region, and the consumed region is then made empty. */ REQUIRE(ISC_BUFFER_VALID(b)); src = isc_buffer_current(b); length = isc_buffer_remaininglength(b); if (length > 0U) { (void)memmove(b->base, src, (size_t)length); } if (b->active > b->current) { b->active -= b->current; } else { b->active = 0; } b->current = 0; b->used = length; } uint8_t isc_buffer_getuint8(isc_buffer_t *b) { unsigned char *cp; uint8_t result; /* * Read an unsigned 8-bit integer from 'b' and return it. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used - b->current >= 1); cp = isc_buffer_current(b); b->current += 1; result = ((uint8_t)(cp[0])); return (result); } void isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, 1); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= 1); ISC__BUFFER_PUTUINT8(b, val); } uint16_t isc_buffer_getuint16(isc_buffer_t *b) { unsigned char *cp; uint16_t result; /* * Read an unsigned 16-bit integer in network byte order from 'b', * convert it to host byte order, and return it. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used - b->current >= 2); cp = isc_buffer_current(b); b->current += 2; result = ((unsigned int)(cp[0])) << 8; result |= ((unsigned int)(cp[1])); return (result); } void isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, 2); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= 2); ISC__BUFFER_PUTUINT16(b, val); } void isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, 3); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= 3); ISC__BUFFER_PUTUINT24(b, val); } uint32_t isc_buffer_getuint32(isc_buffer_t *b) { unsigned char *cp; uint32_t result; /* * Read an unsigned 32-bit integer in network byte order from 'b', * convert it to host byte order, and return it. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used - b->current >= 4); cp = isc_buffer_current(b); b->current += 4; result = ((unsigned int)(cp[0])) << 24; result |= ((unsigned int)(cp[1])) << 16; result |= ((unsigned int)(cp[2])) << 8; result |= ((unsigned int)(cp[3])); return (result); } void isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, 4); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= 4); ISC__BUFFER_PUTUINT32(b, val); } uint64_t isc_buffer_getuint48(isc_buffer_t *b) { unsigned char *cp; uint64_t result; /* * Read an unsigned 48-bit integer in network byte order from 'b', * convert it to host byte order, and return it. */ REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(b->used - b->current >= 6); cp = isc_buffer_current(b); b->current += 6; result = ((int64_t)(cp[0])) << 40; result |= ((int64_t)(cp[1])) << 32; result |= ((int64_t)(cp[2])) << 24; result |= ((int64_t)(cp[3])) << 16; result |= ((int64_t)(cp[4])) << 8; result |= ((int64_t)(cp[5])); return (result); } void isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) { isc_result_t result; uint16_t valhi; uint32_t vallo; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, 6); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= 6); valhi = (uint16_t)(val >> 32); vallo = (uint32_t)(val & 0xFFFFFFFF); ISC__BUFFER_PUTUINT16(b, valhi); ISC__BUFFER_PUTUINT32(b, vallo); } void isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base, unsigned int length) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, length); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= length); ISC__BUFFER_PUTMEM(b, base, length); } void isc__buffer_putstr(isc_buffer_t *b, const char *source) { unsigned int l; unsigned char *cp; isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(source != NULL); /* * Do not use ISC__BUFFER_PUTSTR(), so strlen is only done once. */ l = strlen(source); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, l); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= l); cp = isc_buffer_used(b); memmove(cp, source, l); b->used += l; } void isc_buffer_putdecint(isc_buffer_t *b, int64_t v) { unsigned int l = 0; unsigned char *cp; char buf[21]; isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); /* xxxwpk do it more low-level way ? */ l = snprintf(buf, 21, "%" PRId64, v); RUNTIME_CHECK(l <= 21); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, l); REQUIRE(result == ISC_R_SUCCESS); } REQUIRE(isc_buffer_availablelength(b) >= l); cp = isc_buffer_used(b); memmove(cp, buf, l); b->used += l; } isc_result_t isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) { isc_buffer_t *dst = NULL; isc_region_t region; isc_result_t result; REQUIRE(dstp != NULL && *dstp == NULL); REQUIRE(ISC_BUFFER_VALID(src)); isc_buffer_usedregion(src, ®ion); isc_buffer_allocate(mctx, &dst, region.length); result = isc_buffer_copyregion(dst, ®ion); RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */ *dstp = dst; return (ISC_R_SUCCESS); } isc_result_t isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) { isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); REQUIRE(r != NULL); if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, r->length); if (result != ISC_R_SUCCESS) { return (result); } } if (r->length > isc_buffer_availablelength(b)) { return (ISC_R_NOSPACE); } if (r->length > 0U) { memmove(isc_buffer_used(b), r->base, r->length); b->used += r->length; } return (ISC_R_SUCCESS); } void isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer, unsigned int length) { REQUIRE(dynbuffer != NULL && *dynbuffer == NULL); isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t)); unsigned char *bdata = isc_mem_get(mctx, length); isc_buffer_init(dbuf, bdata, length); ENSURE(ISC_BUFFER_VALID(dbuf)); dbuf->mctx = mctx; *dynbuffer = dbuf; } isc_result_t isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) { unsigned char *bdata; uint64_t len; REQUIRE(dynbuffer != NULL); REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); len = (*dynbuffer)->length; if ((len - (*dynbuffer)->used) >= size) { return (ISC_R_SUCCESS); } if ((*dynbuffer)->mctx == NULL) { return (ISC_R_NOSPACE); } /* Round to nearest buffer size increment */ len = size + (*dynbuffer)->used; len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR)); /* Cap at UINT_MAX */ if (len > UINT_MAX) { len = UINT_MAX; } if ((len - (*dynbuffer)->used) < size) { return (ISC_R_NOMEMORY); } /* * XXXMUKS: This is far more expensive than plain realloc() as * it doesn't remap pages, but does ordinary copy. So is * isc_mem_reallocate(), which has additional issues. */ bdata = isc_mem_get((*dynbuffer)->mctx, (unsigned int)len); memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length); isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base, (*dynbuffer)->length); (*dynbuffer)->base = bdata; (*dynbuffer)->length = (unsigned int)len; return (ISC_R_SUCCESS); } void isc_buffer_free(isc_buffer_t **dynbuffer) { isc_buffer_t *dbuf; isc_mem_t *mctx; REQUIRE(dynbuffer != NULL); REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); REQUIRE((*dynbuffer)->mctx != NULL); dbuf = *dynbuffer; *dynbuffer = NULL; /* destroy external reference */ mctx = dbuf->mctx; dbuf->mctx = NULL; isc_mem_put(mctx, dbuf->base, dbuf->length); isc_buffer_invalidate(dbuf); isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t)); } isc_result_t isc_buffer_printf(isc_buffer_t *b, const char *format, ...) { va_list ap; int n; isc_result_t result; REQUIRE(ISC_BUFFER_VALID(b)); va_start(ap, format); n = vsnprintf(NULL, 0, format, ap); va_end(ap); if (n < 0) { return (ISC_R_FAILURE); } if (ISC_UNLIKELY(b->autore)) { result = isc_buffer_reserve(&b, n + 1); if (result != ISC_R_SUCCESS) { return (result); } } if (isc_buffer_availablelength(b) < (unsigned int)n + 1) { return (ISC_R_NOSPACE); } va_start(ap, format); n = vsnprintf(isc_buffer_used(b), n + 1, format, ap); va_end(ap); if (n < 0) { return (ISC_R_FAILURE); } b->used += n; return (ISC_R_SUCCESS); }