GCC Code Coverage Report


Directory: ./
File: cx/buffer.h
Date: 2025-12-31 16:19:05
Exec Total Coverage
Lines: 2 2 100.0%
Functions: 0 0 -%
Branches: 0 0 -%

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