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 29void 30isc__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 38void 39isc__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 47void 48isc_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 70void 71isc__buffer_invalidate(isc_buffer_t *b) { 72 /* 73 * Make 'b' an invalid buffer. 74 */ 75 ISC__BUFFER_INVALIDATE(b); 76} 77 78void 79isc_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 85void 86isc__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 93void 94isc__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 101void 102isc__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 109void 110isc__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 117void 118isc__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 125void 126isc__buffer_clear(isc_buffer_t *b) { 127 /* 128 * Make the used region empty. 129 */ 130 ISC__BUFFER_CLEAR(b); 131} 132 133void 134isc__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 141void 142isc__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 149void 150isc__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 157void 158isc__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 165void 166isc__buffer_first(isc_buffer_t *b) { 167 /* 168 * Make the consumed region empty. 169 */ 170 ISC__BUFFER_FIRST(b); 171} 172 173void 174isc__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 181void 182isc__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 189void 190isc_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 217uint8_t 218isc_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 236void 237isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) { 238 ISC__BUFFER_PUTUINT8(b, val); 239} 240 241uint16_t 242isc_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 262void 263isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) { 264 ISC__BUFFER_PUTUINT16(b, val); 265} 266 267void 268isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) { 269 ISC__BUFFER_PUTUINT24(b, val); 270} 271 272uint32_t 273isc_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 295void 296isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) { 297 ISC__BUFFER_PUTUINT32(b, val); 298} 299 300uint64_t 301isc_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 325void 326isc__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 344void 345isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base, 346 unsigned int length) { 347 ISC__BUFFER_PUTMEM(b, base, length); 348} 349 350void 351isc__buffer_putstr(isc_buffer_t *b, const char *source) { 352 ISC__BUFFER_PUTSTR(b, source); 353} 354 355void 356isc_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 378isc_result_t 379isc_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, ®ion); 388 389 isc_buffer_allocate(mctx, &dst, region.length); 390 391 result = isc_buffer_copyregion(dst, ®ion); 392 RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */ 393 *dstp = dst; 394 return (ISC_R_SUCCESS); 395} 396 397isc_result_t 398isc_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 423void 424isc_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 440isc_result_t 441isc_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 487void 488isc_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 506isc_result_t 507isc_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