Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: buffer.c,v 1.1 2024/02/18 20:57:48 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <stdarg.h>
     20 #include <stdbool.h>
     21 
     22 #include <isc/buffer.h>
     23 #include <isc/mem.h>
     24 #include <isc/print.h>
     25 #include <isc/region.h>
     26 #include <isc/string.h>
     27 #include <isc/util.h>
     28 
     29 void
     30 isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) {
     31 	/*
     32 	 * Make 'b' refer to the 'length'-byte region starting at 'base'.
     33 	 * XXXDCL see the comment in buffer.h about base being const.
     34 	 */
     35 	ISC__BUFFER_INIT(b, base, length);
     36 }
     37 
     38 void
     39 isc__buffer_initnull(isc_buffer_t *b) {
     40 	/*
     41 	 * Initialize a new buffer which has no backing store.  This can
     42 	 * later be grown as needed and swapped in place.
     43 	 */
     44 	ISC__BUFFER_INIT(b, NULL, 0);
     45 }
     46 
     47 void
     48 isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
     49 	/*
     50 	 * Re-initialize the buffer enough to reconfigure the base of the
     51 	 * buffer.  We will swap in the new buffer, after copying any
     52 	 * data we contain into the new buffer and adjusting all of our
     53 	 * internal pointers.
     54 	 *
     55 	 * The buffer must not be smaller than the length of the original
     56 	 * buffer.
     57 	 */
     58 	REQUIRE(b->length <= length);
     59 	REQUIRE(base != NULL);
     60 	REQUIRE(!b->autore);
     61 
     62 	if (b->length > 0U) {
     63 		(void)memmove(base, b->base, b->length);
     64 	}
     65 
     66 	b->base = base;
     67 	b->length = length;
     68 }
     69 
     70 void
     71 isc__buffer_invalidate(isc_buffer_t *b) {
     72 	/*
     73 	 * Make 'b' an invalid buffer.
     74 	 */
     75 	ISC__BUFFER_INVALIDATE(b);
     76 }
     77 
     78 void
     79 isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) {
     80 	REQUIRE(ISC_BUFFER_VALID(b));
     81 	REQUIRE(b->mctx != NULL);
     82 	b->autore = enable;
     83 }
     84 
     85 void
     86 isc__buffer_region(isc_buffer_t *b, isc_region_t *r) {
     87 	/*
     88 	 * Make 'r' refer to the region of 'b'.
     89 	 */
     90 	ISC__BUFFER_REGION(b, r);
     91 }
     92 
     93 void
     94 isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) {
     95 	/*
     96 	 * Make 'r' refer to the used region of 'b'.
     97 	 */
     98 	ISC__BUFFER_USEDREGION(b, r);
     99 }
    100 
    101 void
    102 isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) {
    103 	/*
    104 	 * Make 'r' refer to the available region of 'b'.
    105 	 */
    106 	ISC__BUFFER_AVAILABLEREGION(b, r);
    107 }
    108 
    109 void
    110 isc__buffer_add(isc_buffer_t *b, unsigned int n) {
    111 	/*
    112 	 * Increase the 'used' region of 'b' by 'n' bytes.
    113 	 */
    114 	ISC__BUFFER_ADD(b, n);
    115 }
    116 
    117 void
    118 isc__buffer_subtract(isc_buffer_t *b, unsigned int n) {
    119 	/*
    120 	 * Decrease the 'used' region of 'b' by 'n' bytes.
    121 	 */
    122 	ISC__BUFFER_SUBTRACT(b, n);
    123 }
    124 
    125 void
    126 isc__buffer_clear(isc_buffer_t *b) {
    127 	/*
    128 	 * Make the used region empty.
    129 	 */
    130 	ISC__BUFFER_CLEAR(b);
    131 }
    132 
    133 void
    134 isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) {
    135 	/*
    136 	 * Make 'r' refer to the consumed region of 'b'.
    137 	 */
    138 	ISC__BUFFER_CONSUMEDREGION(b, r);
    139 }
    140 
    141 void
    142 isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) {
    143 	/*
    144 	 * Make 'r' refer to the remaining region of 'b'.
    145 	 */
    146 	ISC__BUFFER_REMAININGREGION(b, r);
    147 }
    148 
    149 void
    150 isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) {
    151 	/*
    152 	 * Make 'r' refer to the active region of 'b'.
    153 	 */
    154 	ISC__BUFFER_ACTIVEREGION(b, r);
    155 }
    156 
    157 void
    158 isc__buffer_setactive(isc_buffer_t *b, unsigned int n) {
    159 	/*
    160 	 * Sets the end of the active region 'n' bytes after current.
    161 	 */
    162 	ISC__BUFFER_SETACTIVE(b, n);
    163 }
    164 
    165 void
    166 isc__buffer_first(isc_buffer_t *b) {
    167 	/*
    168 	 * Make the consumed region empty.
    169 	 */
    170 	ISC__BUFFER_FIRST(b);
    171 }
    172 
    173 void
    174 isc__buffer_forward(isc_buffer_t *b, unsigned int n) {
    175 	/*
    176 	 * Increase the 'consumed' region of 'b' by 'n' bytes.
    177 	 */
    178 	ISC__BUFFER_FORWARD(b, n);
    179 }
    180 
    181 void
    182 isc__buffer_back(isc_buffer_t *b, unsigned int n) {
    183 	/*
    184 	 * Decrease the 'consumed' region of 'b' by 'n' bytes.
    185 	 */
    186 	ISC__BUFFER_BACK(b, n);
    187 }
    188 
    189 void
    190 isc_buffer_compact(isc_buffer_t *b) {
    191 	unsigned int length;
    192 	void *src;
    193 
    194 	/*
    195 	 * Compact the used region by moving the remaining region so it occurs
    196 	 * at the start of the buffer.  The used region is shrunk by the size
    197 	 * of the consumed region, and the consumed region is then made empty.
    198 	 */
    199 
    200 	REQUIRE(ISC_BUFFER_VALID(b));
    201 
    202 	src = isc_buffer_current(b);
    203 	length = isc_buffer_remaininglength(b);
    204 	if (length > 0U) {
    205 		(void)memmove(b->base, src, (size_t)length);
    206 	}
    207 
    208 	if (b->active > b->current) {
    209 		b->active -= b->current;
    210 	} else {
    211 		b->active = 0;
    212 	}
    213 	b->current = 0;
    214 	b->used = length;
    215 }
    216 
    217 uint8_t
    218 isc_buffer_getuint8(isc_buffer_t *b) {
    219 	unsigned char *cp;
    220 	uint8_t result;
    221 
    222 	/*
    223 	 * Read an unsigned 8-bit integer from 'b' and return it.
    224 	 */
    225 
    226 	REQUIRE(ISC_BUFFER_VALID(b));
    227 	REQUIRE(b->used - b->current >= 1);
    228 
    229 	cp = isc_buffer_current(b);
    230 	b->current += 1;
    231 	result = ((uint8_t)(cp[0]));
    232 
    233 	return (result);
    234 }
    235 
    236 void
    237 isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) {
    238 	ISC__BUFFER_PUTUINT8(b, val);
    239 }
    240 
    241 uint16_t
    242 isc_buffer_getuint16(isc_buffer_t *b) {
    243 	unsigned char *cp;
    244 	uint16_t result;
    245 
    246 	/*
    247 	 * Read an unsigned 16-bit integer in network byte order from 'b',
    248 	 * convert it to host byte order, and return it.
    249 	 */
    250 
    251 	REQUIRE(ISC_BUFFER_VALID(b));
    252 	REQUIRE(b->used - b->current >= 2);
    253 
    254 	cp = isc_buffer_current(b);
    255 	b->current += 2;
    256 	result = ((unsigned int)(cp[0])) << 8;
    257 	result |= ((unsigned int)(cp[1]));
    258 
    259 	return (result);
    260 }
    261 
    262 void
    263 isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) {
    264 	ISC__BUFFER_PUTUINT16(b, val);
    265 }
    266 
    267 void
    268 isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) {
    269 	ISC__BUFFER_PUTUINT24(b, val);
    270 }
    271 
    272 uint32_t
    273 isc_buffer_getuint32(isc_buffer_t *b) {
    274 	unsigned char *cp;
    275 	uint32_t result;
    276 
    277 	/*
    278 	 * Read an unsigned 32-bit integer in network byte order from 'b',
    279 	 * convert it to host byte order, and return it.
    280 	 */
    281 
    282 	REQUIRE(ISC_BUFFER_VALID(b));
    283 	REQUIRE(b->used - b->current >= 4);
    284 
    285 	cp = isc_buffer_current(b);
    286 	b->current += 4;
    287 	result = ((unsigned int)(cp[0])) << 24;
    288 	result |= ((unsigned int)(cp[1])) << 16;
    289 	result |= ((unsigned int)(cp[2])) << 8;
    290 	result |= ((unsigned int)(cp[3]));
    291 
    292 	return (result);
    293 }
    294 
    295 void
    296 isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) {
    297 	ISC__BUFFER_PUTUINT32(b, val);
    298 }
    299 
    300 uint64_t
    301 isc_buffer_getuint48(isc_buffer_t *b) {
    302 	unsigned char *cp;
    303 	uint64_t result;
    304 
    305 	/*
    306 	 * Read an unsigned 48-bit integer in network byte order from 'b',
    307 	 * convert it to host byte order, and return it.
    308 	 */
    309 
    310 	REQUIRE(ISC_BUFFER_VALID(b));
    311 	REQUIRE(b->used - b->current >= 6);
    312 
    313 	cp = isc_buffer_current(b);
    314 	b->current += 6;
    315 	result = ((int64_t)(cp[0])) << 40;
    316 	result |= ((int64_t)(cp[1])) << 32;
    317 	result |= ((int64_t)(cp[2])) << 24;
    318 	result |= ((int64_t)(cp[3])) << 16;
    319 	result |= ((int64_t)(cp[4])) << 8;
    320 	result |= ((int64_t)(cp[5]));
    321 
    322 	return (result);
    323 }
    324 
    325 void
    326 isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) {
    327 	isc_result_t result;
    328 	uint16_t valhi;
    329 	uint32_t vallo;
    330 
    331 	REQUIRE(ISC_BUFFER_VALID(b));
    332 	if (ISC_UNLIKELY(b->autore)) {
    333 		result = isc_buffer_reserve(&b, 6);
    334 		REQUIRE(result == ISC_R_SUCCESS);
    335 	}
    336 	REQUIRE(isc_buffer_availablelength(b) >= 6);
    337 
    338 	valhi = (uint16_t)(val >> 32);
    339 	vallo = (uint32_t)(val & 0xFFFFFFFF);
    340 	ISC__BUFFER_PUTUINT16(b, valhi);
    341 	ISC__BUFFER_PUTUINT32(b, vallo);
    342 }
    343 
    344 void
    345 isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base,
    346 		   unsigned int length) {
    347 	ISC__BUFFER_PUTMEM(b, base, length);
    348 }
    349 
    350 void
    351 isc__buffer_putstr(isc_buffer_t *b, const char *source) {
    352 	ISC__BUFFER_PUTSTR(b, source);
    353 }
    354 
    355 void
    356 isc_buffer_putdecint(isc_buffer_t *b, int64_t v) {
    357 	unsigned int l = 0;
    358 	unsigned char *cp;
    359 	char buf[21];
    360 	isc_result_t result;
    361 
    362 	REQUIRE(ISC_BUFFER_VALID(b));
    363 
    364 	/* xxxwpk do it more low-level way ? */
    365 	l = snprintf(buf, 21, "%" PRId64, v);
    366 	RUNTIME_CHECK(l <= 21);
    367 	if (ISC_UNLIKELY(b->autore)) {
    368 		result = isc_buffer_reserve(&b, l);
    369 		REQUIRE(result == ISC_R_SUCCESS);
    370 	}
    371 	REQUIRE(isc_buffer_availablelength(b) >= l);
    372 
    373 	cp = isc_buffer_used(b);
    374 	memmove(cp, buf, l);
    375 	b->used += l;
    376 }
    377 
    378 isc_result_t
    379 isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) {
    380 	isc_buffer_t *dst = NULL;
    381 	isc_region_t region;
    382 	isc_result_t result;
    383 
    384 	REQUIRE(dstp != NULL && *dstp == NULL);
    385 	REQUIRE(ISC_BUFFER_VALID(src));
    386 
    387 	isc_buffer_usedregion(src, &region);
    388 
    389 	isc_buffer_allocate(mctx, &dst, region.length);
    390 
    391 	result = isc_buffer_copyregion(dst, &region);
    392 	RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */
    393 	*dstp = dst;
    394 	return (ISC_R_SUCCESS);
    395 }
    396 
    397 isc_result_t
    398 isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
    399 	isc_result_t result;
    400 
    401 	REQUIRE(ISC_BUFFER_VALID(b));
    402 	REQUIRE(r != NULL);
    403 
    404 	if (ISC_UNLIKELY(b->autore)) {
    405 		result = isc_buffer_reserve(&b, r->length);
    406 		if (result != ISC_R_SUCCESS) {
    407 			return (result);
    408 		}
    409 	}
    410 
    411 	if (r->length > isc_buffer_availablelength(b)) {
    412 		return (ISC_R_NOSPACE);
    413 	}
    414 
    415 	if (r->length > 0U) {
    416 		memmove(isc_buffer_used(b), r->base, r->length);
    417 		b->used += r->length;
    418 	}
    419 
    420 	return (ISC_R_SUCCESS);
    421 }
    422 
    423 void
    424 isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
    425 		    unsigned int length) {
    426 	REQUIRE(dynbuffer != NULL && *dynbuffer == NULL);
    427 
    428 	isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
    429 	unsigned char *bdata = isc_mem_get(mctx, length);
    430 
    431 	isc_buffer_init(dbuf, bdata, length);
    432 
    433 	ENSURE(ISC_BUFFER_VALID(dbuf));
    434 
    435 	dbuf->mctx = mctx;
    436 
    437 	*dynbuffer = dbuf;
    438 }
    439 
    440 isc_result_t
    441 isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
    442 	unsigned char *bdata;
    443 	uint64_t len;
    444 
    445 	REQUIRE(dynbuffer != NULL);
    446 	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
    447 
    448 	len = (*dynbuffer)->length;
    449 	if ((len - (*dynbuffer)->used) >= size) {
    450 		return (ISC_R_SUCCESS);
    451 	}
    452 
    453 	if ((*dynbuffer)->mctx == NULL) {
    454 		return (ISC_R_NOSPACE);
    455 	}
    456 
    457 	/* Round to nearest buffer size increment */
    458 	len = size + (*dynbuffer)->used;
    459 	len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR));
    460 
    461 	/* Cap at UINT_MAX */
    462 	if (len > UINT_MAX) {
    463 		len = UINT_MAX;
    464 	}
    465 
    466 	if ((len - (*dynbuffer)->used) < size) {
    467 		return (ISC_R_NOMEMORY);
    468 	}
    469 
    470 	/*
    471 	 * XXXMUKS: This is far more expensive than plain realloc() as
    472 	 * it doesn't remap pages, but does ordinary copy. So is
    473 	 * isc_mem_reallocate(), which has additional issues.
    474 	 */
    475 	bdata = isc_mem_get((*dynbuffer)->mctx, (unsigned int)len);
    476 
    477 	memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length);
    478 	isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base,
    479 		    (*dynbuffer)->length);
    480 
    481 	(*dynbuffer)->base = bdata;
    482 	(*dynbuffer)->length = (unsigned int)len;
    483 
    484 	return (ISC_R_SUCCESS);
    485 }
    486 
    487 void
    488 isc_buffer_free(isc_buffer_t **dynbuffer) {
    489 	isc_buffer_t *dbuf;
    490 	isc_mem_t *mctx;
    491 
    492 	REQUIRE(dynbuffer != NULL);
    493 	REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
    494 	REQUIRE((*dynbuffer)->mctx != NULL);
    495 
    496 	dbuf = *dynbuffer;
    497 	*dynbuffer = NULL; /* destroy external reference */
    498 	mctx = dbuf->mctx;
    499 	dbuf->mctx = NULL;
    500 
    501 	isc_mem_put(mctx, dbuf->base, dbuf->length);
    502 	isc_buffer_invalidate(dbuf);
    503 	isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
    504 }
    505 
    506 isc_result_t
    507 isc_buffer_printf(isc_buffer_t *b, const char *format, ...) {
    508 	va_list ap;
    509 	int n;
    510 	isc_result_t result;
    511 
    512 	REQUIRE(ISC_BUFFER_VALID(b));
    513 
    514 	va_start(ap, format);
    515 	n = vsnprintf(NULL, 0, format, ap);
    516 	va_end(ap);
    517 
    518 	if (n < 0) {
    519 		return (ISC_R_FAILURE);
    520 	}
    521 
    522 	if (ISC_UNLIKELY(b->autore)) {
    523 		result = isc_buffer_reserve(&b, n + 1);
    524 		if (result != ISC_R_SUCCESS) {
    525 			return (result);
    526 		}
    527 	}
    528 
    529 	if (isc_buffer_availablelength(b) < (unsigned int)n + 1) {
    530 		return (ISC_R_NOSPACE);
    531 	}
    532 
    533 	va_start(ap, format);
    534 	n = vsnprintf(isc_buffer_used(b), n + 1, format, ap);
    535 	va_end(ap);
    536 
    537 	if (n < 0) {
    538 		return (ISC_R_FAILURE);
    539 	}
    540 
    541 	b->used += n;
    542 
    543 	return (ISC_R_SUCCESS);
    544 }
    545