UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file buffer.h 31 * 32 * @brief Advanced buffer implementation. 33 * 34 * Instances of CxBuffer can be used to read from or to write to like one 35 * would do with a stream. 36 * 37 * Some features for convenient use of the buffer 38 * can be enabled. See the documentation of the macro constants for more 39 * information. 40 * 41 * @author Mike Becker 42 * @author Olaf Wintermann 43 * @copyright 2-Clause BSD License 44 */ 45 46 #ifndef UCX_BUFFER_H 47 #define UCX_BUFFER_H 48 49 #include "common.h" 50 #include "allocator.h" 51 #include "string.h" 52 53 #ifdef __cplusplus 54 extern "C" { 55 #endif 56 57 /** 58 * No buffer features enabled (all flags cleared). 59 */ 60 #define CX_BUFFER_DEFAULT 0x00 61 62 /** 63 * If this flag is enabled, the buffer will automatically free its contents when destroyed. 64 * 65 * Do NOT set this flag together with #CX_BUFFER_COPY_ON_WRITE. It will be automatically 66 * set when the copy-on-write operation is performed. 67 */ 68 #define CX_BUFFER_FREE_CONTENTS 0x01 69 70 /** 71 * If this flag is enabled, the buffer will automatically extend its capacity. 72 */ 73 #define CX_BUFFER_AUTO_EXTEND 0x02 74 75 /** 76 * If this flag is enabled, the buffer will allocate new memory when written to. 77 * 78 * The current contents of the buffer will be copied to the new memory, and the flag 79 * will be cleared while the #CX_BUFFER_FREE_CONTENTS flag will be set automatically. 80 */ 81 #define CX_BUFFER_COPY_ON_WRITE 0x04 82 83 /** 84 * If this flag is enabled, the buffer will copy its contents to a new memory area on reallocation. 85 * 86 * After performing the copy, the flag is automatically cleared. 87 * This flag has no effect on buffers which do not have #CX_BUFFER_AUTO_EXTEND set, which is why 88 * buffers automatically admit the auto-extend flag when initialized with copy-on-extend enabled. 89 */ 90 #define CX_BUFFER_COPY_ON_EXTEND 0x08 91 92 /** 93 * If this flag is enabled, the buffer will never free its contents regardless of #CX_BUFFER_FREE_CONTENTS. 94 * 95 * This is useful, for example, when you want to keep a pointer to the data after destroying the buffer. 96 */ 97 #define CX_BUFFER_DO_NOT_FREE 0x10 98 99 /** 100 * Function pointer for cxBufferWrite that is compatible with cx_write_func. 101 * @see cx_write_func 102 */ 103 #define cxBufferWriteFunc ((cx_write_func) cxBufferWrite) 104 /** 105 * Function pointer for cxBufferRead that is compatible with cx_read_func. 106 * @see cx_read_func 107 */ 108 #define cxBufferReadFunc ((cx_read_func) cxBufferRead) 109 110 111 /** Structure for the UCX buffer data. */ 112 struct cx_buffer_s { 113 /** A pointer to the buffer contents. */ 114 union { 115 /** 116 * Data is interpreted as text. 117 */ 118 char *space; 119 /** 120 * Data is interpreted as binary. 121 */ 122 unsigned char *bytes; 123 }; 124 /** The allocator to use for automatic memory management. */ 125 const CxAllocator *allocator; 126 /** Current position of the buffer. */ 127 size_t pos; 128 /** Current capacity (i.e. maximum size) of the buffer. */ 129 size_t capacity; 130 /** Maximum capacity that this buffer may grow to. */ 131 size_t max_capacity; 132 /** Current size of the buffer content. */ 133 size_t size; 134 /** 135 * Flag register for buffer features. 136 * @see #CX_BUFFER_DEFAULT 137 * @see #CX_BUFFER_FREE_CONTENTS 138 * @see #CX_BUFFER_AUTO_EXTEND 139 * @see #CX_BUFFER_COPY_ON_WRITE 140 */ 141 int flags; 142 }; 143 144 /** 145 * UCX buffer. 146 */ 147 typedef struct cx_buffer_s CxBuffer; 148 149 /** 150 * Initializes a fresh buffer. 151 * 152 * You may also provide a read-only @p space, in which case 153 * you will need to cast the pointer, and you should set the 154 * #CX_BUFFER_COPY_ON_WRITE flag. 155 * 156 * You need to set the size manually after initialization if 157 * you provide @p space which already contains data. 158 * 159 * When you specify stack memory as @p space and decide to use 160 * the auto-extension feature, you @em must use the 161 * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the 162 * #CX_BUFFER_AUTO_EXTEND flag. 163 * 164 * @note You may provide @c NULL as the argument for @p space. 165 * Then this function will allocate the space and enforce 166 * the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying 167 * copy-on-write should be avoided, because the allocated 168 * space will be leaking after the copy-on-write operation. 169 * 170 * @param buffer the buffer to initialize 171 * @param space pointer to the memory area, or @c NULL to allocate 172 * new memory 173 * @param capacity the capacity of the buffer 174 * @param allocator the allocator this buffer shall use for automatic 175 * memory management 176 * (if @c NULL, the cxDefaultAllocator will be used) 177 * @param flags buffer features (see cx_buffer_s.flags) 178 * @return zero on success, non-zero if a required allocation failed 179 */ 180 cx_attr_nonnull_arg(1) 181 CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity, 182 const CxAllocator *allocator, int flags); 183 184 /** 185 * Destroys the buffer contents. 186 * 187 * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. 188 * If you want to free the memory of the entire buffer, use cxBufferFree(). 189 * 190 * @param buffer the buffer which contents shall be destroyed 191 * @see cxBufferInit() 192 */ 193 cx_attr_nonnull 194 CX_EXPORT void cxBufferDestroy(CxBuffer *buffer); 195 196 /** 197 * Deallocates the buffer. 198 * 199 * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys 200 * the contents. If you @em only want to destroy the contents, use cxBufferDestroy(). 201 * 202 * @param buffer the buffer to deallocate 203 * @see cxBufferCreate() 204 */ 205 CX_EXPORT void cxBufferFree(CxBuffer *buffer); 206 207 /** 208 * Allocates and initializes a fresh buffer. 209 * 210 * You may also provide a read-only @p space, in which case 211 * you will need to cast the pointer, and you should set the 212 * #CX_BUFFER_COPY_ON_WRITE flag. 213 * When you specify stack memory as @p space and decide to use 214 * the auto-extension feature, you @em must use the 215 * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the 216 * #CX_BUFFER_AUTO_EXTEND flag. 217 * 218 * @note You may provide @c NULL as the argument for @p space. 219 * Then this function will allocate the space and enforce 220 * the #CX_BUFFER_FREE_CONTENTS flag. 221 * 222 * @param space pointer to the memory area, or @c NULL to allocate 223 * new memory 224 * @param capacity the capacity of the buffer 225 * @param allocator the allocator to use for allocating the structure and the automatic 226 * memory management within the buffer 227 * (if @c NULL, the cxDefaultAllocator will be used) 228 * @param flags buffer features (see cx_buffer_s.flags) 229 * @return a pointer to the buffer on success, @c NULL if a required allocation failed 230 */ 231 cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard 232 CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity, 233 const CxAllocator *allocator, int flags); 234 235 /** 236 * Shifts the contents of the buffer by the given offset. 237 * 238 * If the offset is positive, the contents are shifted to the right. 239 * If auto extension is enabled, the buffer grows, if necessary. 240 * In case the auto extension fails, this function returns a non-zero value and 241 * no contents are changed. 242 * When the auto extension is disabled, the contents that do not fit into the 243 * buffer are discarded. 244 * 245 * If the offset is negative, the contents are shifted to the left where the 246 * first @p shift bytes are discarded. 247 * The new size of the buffer is the old size minus the absolute shift value. 248 * If this value is larger than the buffer size, the buffer is emptied (but 249 * not cleared, see the security note below). 250 * 251 * The buffer position gets shifted alongside the content but is kept 252 * within the boundaries of the buffer. 253 * 254 * @note For situations where @c off_t is not large enough, there are specialized cxBufferShiftLeft() and 255 * cxBufferShiftRight() functions using a @c size_t as the parameter type. 256 * 257 * @attention 258 * Security Note: The shifting operation does @em not erase the previously occupied memory cells. 259 * But you can do that manually by calling 260 * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or 261 * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code> 262 * for a left shift. 263 * 264 * @param buffer the buffer 265 * @param shift the shift offset (negative means left shift) 266 * @retval zero success 267 * @retval non-zero if a required auto-extension or copy-on-write fails 268 * @see cxBufferShiftLeft() 269 * @see cxBufferShiftRight() 270 */ 271 cx_attr_nonnull 272 CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift); 273 274 /** 275 * Shifts the buffer to the right. 276 * See cxBufferShift() for details. 277 * 278 * @param buffer the buffer 279 * @param shift the shift offset 280 * @retval zero success 281 * @retval non-zero if a required auto-extension or copy-on-write fails 282 * @see cxBufferShift() 283 */ 284 cx_attr_nonnull 285 CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift); 286 287 /** 288 * Shifts the buffer to the left. 289 * See cxBufferShift() for details. 290 * 291 * @param buffer the buffer 292 * @param shift the positive shift offset 293 * @retval zero success 294 * @retval non-zero if the buffer uses copy-on-write and the allocation fails 295 * @see cxBufferShift() 296 */ 297 cx_attr_nonnull 298 CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); 299 300 301 /** 302 * Moves the position of the buffer. 303 * 304 * The new position is relative to the @p whence argument. 305 * 306 * @li @c SEEK_SET marks the start of the buffer. 307 * @li @c SEEK_CUR marks the current position. 308 * @li @c SEEK_END marks the end of the buffer. 309 * 310 * With an offset of zero, this function sets the buffer position to zero 311 * (@c SEEK_SET), the buffer size (@c SEEK_END) or leaves the buffer position 312 * unchanged (@c SEEK_CUR). 313 * 314 * @param buffer the buffer 315 * @param offset position offset relative to @p whence 316 * @param whence one of @c SEEK_SET, @c SEEK_CUR or @c SEEK_END 317 * @retval zero success 318 * @retval non-zero if the position is invalid 319 * 320 */ 321 cx_attr_nonnull 322 CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence); 323 324 /** 325 * Discards items from the end of the buffer. 326 * 327 * When the current position points to a byte that gets discarded, 328 * the position is set to the buffer size. 329 * 330 * @param buffer the buffer 331 * @param size the size of one item 332 * @param nitems the number of items to discard 333 * @return the actual number of discarded items 334 */ 335 cx_attr_nonnull 336 CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems); 337 338 /** 339 * Clears the buffer by resetting the position and deleting the data. 340 * 341 * The data is deleted by zeroing it with a call to memset(). 342 * If you do not need that, you can use the faster cxBufferReset(). 343 * 344 * @note If the #CX_BUFFER_COPY_ON_WRITE flag is set, this function 345 * will not erase the data and behave exactly as cxBufferReset(). 346 * 347 * @param buffer the buffer to be cleared 348 * @see cxBufferReset() 349 */ 350 cx_attr_nonnull 351 CX_EXPORT void cxBufferClear(CxBuffer *buffer); 352 353 /** 354 * Resets the buffer by resetting the position and size to zero. 355 * 356 * The data in the buffer is not deleted. If you need a safe 357 * reset of the buffer, use cxBufferClear(). 358 * 359 * @param buffer the buffer to be cleared 360 * @see cxBufferClear() 361 */ 362 cx_attr_nonnull 363 CX_EXPORT void cxBufferReset(CxBuffer *buffer); 364 365 /** 366 * Tests, if the buffer position has exceeded the buffer size. 367 * 368 * @param buffer the buffer to test 369 * @retval true if the current buffer position has exceeded the last 370 * byte of the buffer's contents 371 * @retval false otherwise 372 */ 373 cx_attr_nonnull cx_attr_nodiscard 374 CX_EXPORT bool cxBufferEof(const CxBuffer *buffer); 375 376 /** 377 * Ensures that the buffer has the required capacity. 378 * 379 * If the current capacity is not sufficient, the buffer will be extended. 380 * If the current capacity is larger, the buffer is shrunk and superfluous 381 * content is discarded. 382 * 383 * This function will reserve no more bytes than requested, in contrast to 384 * cxBufferMinimumCapacity(), which may reserve more bytes to improve the 385 * number of future necessary reallocations. 386 * 387 * @param buffer the buffer 388 * @param capacity the required capacity for this buffer 389 * @retval zero on success 390 * @retval non-zero on allocation failure 391 * @see cxBufferShrink() 392 * @see cxBufferMinimumCapacity() 393 */ 394 cx_attr_nonnull 395 CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity); 396 397 /** 398 * Limits the buffer's capacity. 399 * 400 * If the current capacity is already larger, this function fails and returns 401 * non-zero. 402 * 403 * The capacity limit will affect auto-extension features, as well as future 404 * calls to cxBufferMinimumCapacity() and cxBufferReserve(). 405 * 406 * @param buffer the buffer 407 * @param capacity the maximum allowed capacity for this buffer 408 * @retval zero the limit is applied 409 * @retval non-zero the new limit is smaller than the current capacity 410 * @see cxBufferReserve() 411 * @see cxBufferMinimumCapacity() 412 */ 413 cx_attr_nonnull 414 CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity); 415 416 /** 417 * Ensures that the buffer has a minimum capacity. 418 * 419 * If the current capacity is not sufficient, the buffer will be generously 420 * extended. 421 * 422 * The new capacity will be a power of two until the system's page size is reached. 423 * Then, the new capacity will be a multiple of the page size. 424 * 425 * @param buffer the buffer 426 * @param capacity the minimum required capacity for this buffer 427 * @retval zero the capacity was already sufficient or successfully increased 428 * @retval non-zero on allocation failure 429 * @see cxBufferMaximumCapacity() 430 * @see cxBufferReserve() 431 * @see cxBufferShrink() 432 */ 433 cx_attr_nonnull 434 CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity); 435 436 /** 437 * Shrinks the capacity of the buffer to fit its current size. 438 * 439 * If @p reserve is larger than zero, the buffer is shrunk to its size plus 440 * the number of reserved bytes. 441 * 442 * If the current capacity is not larger than the size plus the reserved bytes, 443 * nothing happens. 444 * 445 * If the #CX_BUFFER_COPY_ON_WRITE or #CX_BUFFER_COPY_ON_EXTEND flag is set, 446 * this function does nothing. 447 * 448 * @param buffer the buffer 449 * @param reserve the number of bytes that shall remain reserved 450 * @see cxBufferReserve() 451 * @see cxBufferMinimumCapacity() 452 */ 453 cx_attr_nonnull 454 CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve); 455 456 /** 457 * Writes data to a CxBuffer. 458 * 459 * If auto-extension is enabled, the buffer's capacity is automatically 460 * increased when it is not large enough to hold all data. 461 * By default, the capacity grows indefinitely, unless limited with 462 * cxBufferMaximumCapacity(). 463 * When auto-extension fails, this function writes no data and returns zero. 464 * 465 * The position of the buffer is moved alongside the written data. 466 * 467 * @note The signature is compatible with the fwrite() family of functions. 468 * 469 * @param ptr a pointer to the memory area containing the bytes to be written 470 * @param size the length of one element 471 * @param nitems the element count 472 * @param buffer the CxBuffer to write to 473 * @return the total count of elements written 474 * @see cxBufferAppend() 475 * @see cxBufferRead() 476 */ 477 cx_attr_nonnull 478 CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size, 479 size_t nitems, CxBuffer *buffer); 480 481 /** 482 * Appends data to a CxBuffer. 483 * 484 * The data is always appended to current data within the buffer, 485 * regardless of the current position. 486 * This is especially useful when the buffer is primarily meant for reading 487 * while additional data is added to the buffer occasionally. 488 * Consequently, the position of the buffer is unchanged after this operation. 489 * 490 * @note The signature is compatible with the fwrite() family of functions. 491 * 492 * @param ptr a pointer to the memory area containing the bytes to be written 493 * @param size the length of one element 494 * @param nitems the element count 495 * @param buffer the CxBuffer to write to 496 * @return the total count of elements written 497 * @see cxBufferWrite() 498 * @see cxBufferRead() 499 */ 500 cx_attr_nonnull 501 CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size, 502 size_t nitems, CxBuffer *buffer); 503 504 /** 505 * Reads data from a CxBuffer. 506 * 507 * The position of the buffer is increased by the number of bytes read. 508 * 509 * @note The signature is compatible with the fread() family of functions. 510 * 511 * @param ptr a pointer to the memory area where to store the read data 512 * @param size the length of one element 513 * @param nitems the element count 514 * @param buffer the CxBuffer to read from 515 * @return the total number of elements read 516 * @see cxBufferWrite() 517 * @see cxBufferAppend() 518 */ 519 cx_attr_nonnull 520 CX_EXPORT size_t cxBufferRead(void *ptr, size_t size, 521 size_t nitems, CxBuffer *buffer); 522 523 /** 524 * Writes a character to a buffer. 525 * 526 * The least significant byte of the argument is written to the buffer. If the 527 * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, 528 * the buffer capacity is extended, unless a limit set by 529 * cxBufferMaximumCapacity() is reached. 530 * If the feature is disabled or the buffer extension fails, @c EOF is returned. 531 * 532 * On successful writing, the position of the buffer is increased. 533 * 534 * If you just want to write a null-terminator at the current position, you 535 * should use cxBufferTerminate() instead. 536 * 537 * @param buffer the buffer to write to 538 * @param c the character to write 539 * @return the byte that has been written or @c EOF when the end of the 540 * stream is reached, and automatic extension is not enabled or not possible 541 * @see cxBufferTerminate() 542 */ 543 cx_attr_nonnull 544 CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c); 545 546 /** 547 * Writes a terminating zero to a buffer at the current position. 548 * 549 * If successful, also sets the size to the current position and shrinks the buffer. 550 * 551 * The purpose of this function is to have the written data ready to be used as 552 * a C string with the buffer's size being the length of that string. 553 * 554 * @param buffer the buffer to write to 555 * @return zero, if the terminator could be written, non-zero otherwise 556 * @see cxBufferShrink() 557 */ 558 cx_attr_nonnull 559 CX_EXPORT int cxBufferTerminate(CxBuffer *buffer); 560 561 /** 562 * Internal function - do not use. 563 * 564 * @param buffer the buffer 565 * @param str the string 566 * @return the number of bytes written 567 * @see cxBufferPutString() 568 */ 569 cx_attr_nonnull 570 CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str); 571 572 /** 573 * Writes a string to a buffer with cxBufferWrite(). 574 * 575 * @param buffer (@c CxBuffer*) the buffer 576 * @param str (any string) the zero-terminated string 577 * @return (@c size_t) the number of bytes written 578 * @see cxBufferWrite() 579 * @see cx_strcast() 580 */ 581 #define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str)) 582 583 /** 584 * Internal function - do not use. 585 * 586 * @param buffer the buffer 587 * @param str the string 588 * @return the number of bytes written 589 * @see cxBufferPutString() 590 */ 591 cx_attr_nonnull 592 CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str); 593 594 /** 595 * Appends a string to a buffer with cxBufferAppend(). 596 * 597 * @param buffer (@c CxBuffer*) the buffer 598 * @param str (any string) the zero-terminated string 599 * @return (@c size_t) the number of bytes written 600 * @see cxBufferAppend() 601 * @see cx_strcast() 602 */ 603 #define cxBufferAppendString(buffer, str) cx_buffer_append_string(buffer, cx_strcast(str)) 604 605 /** 606 * Gets a character from a buffer. 607 * 608 * The current position of the buffer is increased after a successful read. 609 * 610 * @param buffer the buffer to read from 611 * @return the character or @c EOF, if the end of the buffer is reached 612 */ 613 cx_attr_nonnull 614 CX_EXPORT int cxBufferGet(CxBuffer *buffer); 615 616 #ifdef __cplusplus 617 } 618 #endif 619 620 #endif // UCX_BUFFER_H 621