1 # Buffer |
1 # Buffer |
2 |
2 |
3 This buffer implementation can be used to read from or write to memory like you would do with a stream. |
3 This buffer implementation can be used to read from or write to memory like you would do with a stream. |
4 |
4 |
5 This allows the use of `cx_stream_copy()` (see [](streams.h.md)) to copy contents from one buffer to another, |
5 This allows the use of `cx_stream_copy()` (see [](streams.h.md)) to copy contents from one buffer to another, |
6 or from a file or network streams to the buffer and vice versa. |
6 or from a file or network stream to the buffer and vice versa. |
7 |
7 |
8 More features for convenient use of the buffer can be enabled, like automatic memory management, |
8 More features for convenient use of the buffer can be enabled, like automatic memory management, |
9 automatic resizing of the buffer space, or automatic flushing of contents. |
9 automatic resizing of the buffer space, or automatic flushing of contents. |
10 |
10 |
11 The functions `cxBufferRead()` and `cxBufferWrite()` are `cx_read_func` and `cx_write_func` compatible, |
11 The functions `cxBufferRead()` and `cxBufferWrite()` are `cx_read_func` and `cx_write_func` compatible, |
12 which in turn have a compatible signature to `fread()` and `fwrite()`. |
12 which in turn have a compatible signature to `fread()` and `fwrite()`. |
13 However, due to the different pointer type, the function pointers do not type check out of the box. |
13 However, due to the different pointer type the function pointers do not type check out of the box. |
14 For convenience, the macros `cxBufferReadFunc` and `cxBufferWriteFunc` are defined, which perform the necessary cast. |
14 For convenience, the macros `cxBufferReadFunc` and `cxBufferWriteFunc` are defined, which perform the necessary cast. |
15 |
15 |
16 ## Example |
16 ## Example |
17 |
17 |
18 In the following example we use a `CxBuffer`, the `bprintf()` macro from the [UCX printf header](printf.h.md), |
18 In the following example we use a `CxBuffer`, the `bprintf()` macro from the [UCX printf header](printf.h.md), |
78 #define CX_BUFFER_AUTO_EXTEND |
78 #define CX_BUFFER_AUTO_EXTEND |
79 #define CX_BUFFER_COPY_ON_WRITE |
79 #define CX_BUFFER_COPY_ON_WRITE |
80 #define CX_BUFFER_COPY_ON_EXTEND |
80 #define CX_BUFFER_COPY_ON_EXTEND |
81 ``` |
81 ``` |
82 |
82 |
83 For creating a UCX buffer you have two options: initialize a pre-allocated structure, or allocate and initialize a new structure in one call. |
83 For creating a UCX buffer, you have two options: initialize a pre-allocated structure, or allocate and initialize a new structure in one call. |
84 |
84 |
85 For the first option, you can call `cxBufferInit()` with the `buffer` argument pointing to an uninitialized `CxBuffer` structure. |
85 For the first option, you can call `cxBufferInit()` with the `buffer` argument pointing to an uninitialized `CxBuffer` structure. |
86 You can pass a pre-allocated `space`, the desired `capacity`, and an `allocator`. |
86 You can pass a pre-allocated `space`, the desired `capacity`, and an `allocator`. |
87 If `space` is `NULL`, the `allocator` is used to allocate enough space to match the desired `capacity`. |
87 If `space` is `NULL`, the `allocator` is used to allocate enough space to match the desired `capacity`. |
88 |
88 |
173 but it does _not_ set `errno` on failure. |
173 but it does _not_ set `errno` on failure. |
174 |
174 |
175 The function `cxBufferPutString()` is a convenience function, |
175 The function `cxBufferPutString()` is a convenience function, |
176 that uses stdlib `strlen()` to compute the length of `str` and then invokes `cxBufferWrite()`. |
176 that uses stdlib `strlen()` to compute the length of `str` and then invokes `cxBufferWrite()`. |
177 |
177 |
178 All of the above functions advance the buffers position by the number of bytes written, |
178 All the above functions advance the buffer position by the number of bytes written |
179 and cause the _size_ of the buffer to grow, if necessary, to contain all written bytes. |
179 and cause the _size_ of the buffer to grow, if necessary, to contain all written bytes. |
180 On the other hand, `cxBufferTerminate()` writes a zero-byte at the current position, |
180 On the other hand, `cxBufferTerminate()` writes a zero-byte at the current position, |
181 effectively creating a zero-terminated string whose size equals the buffer size. |
181 effectively creating a zero-terminated string whose size equals the buffer size. |
182 |
182 |
183 The function `cxBufferAppend()` writes the data to end of the buffer (given by its size) regardless of the current position, |
183 The function `cxBufferAppend()` writes the data to the end of the buffer (given by its size) regardless of the current position, |
184 and it also does _not_ advance the position. |
184 and it also does _not_ advance the position. |
185 If the write operation triggered a [flush](#flushing), however, the position will be shifted left alongside the shifted buffer contents. |
185 If the write operation triggered a [flush](#flushing), however, the position will be shifted left alongside the shifted buffer contents. |
186 In case the data at which the current position points gets flushed, the new position will be zero. |
186 In case the data at which the current position points gets flushed, the new position will be zero. |
187 |
187 |
188 ## Read |
188 ## Read |
197 |
197 |
198 bool cxBufferEof(const CxBuffer *buffer); |
198 bool cxBufferEof(const CxBuffer *buffer); |
199 ``` |
199 ``` |
200 |
200 |
201 The function `cxBufferRead()` reads `nitems` number of items of `size` bytes each from the `buffer` |
201 The function `cxBufferRead()` reads `nitems` number of items of `size` bytes each from the `buffer` |
202 and stores them into the memory pointed to by `ptr`, which must be sufficiently large to hold the contents. |
202 and stores them into the memory pointed to by `ptr`, which must be large enough to hold the contents. |
203 The function returns the actual bytes read, which might be lower, if the desired number of items is not available. |
203 The function returns the actual bytes read, which might be lower if the desired number of items is not available. |
204 |
204 |
205 The function `cxBufferGet()` is a `fgetc()`-like function which returns the next byte in the buffer converted to an `int`. |
205 The function `cxBufferGet()` is a `fgetc()`-like function which returns the next byte in the buffer converted to an `int`. |
206 Similar to `fgetc()` it returns `EOF` when there are no more bytes in the buffer. |
206 Similar to `fgetc()` it returns `EOF` when there are no more bytes in the buffer. |
207 |
207 |
208 When all bytes from the buffer have been read, the function `cxBufferEof()` returns true. |
208 When all bytes from the buffer have been read, the function `cxBufferEof()` returns true. |
223 ``` |
223 ``` |
224 |
224 |
225 The function `cxBufferReset()` sets both size and position of the buffer to zero, |
225 The function `cxBufferReset()` sets both size and position of the buffer to zero, |
226 and `cxBufferClear()` additionally uses `memset()` to set every byte in the buffer to zero. |
226 and `cxBufferClear()` additionally uses `memset()` to set every byte in the buffer to zero. |
227 |
227 |
228 > When clearing the buffer, only the "live" data, i.e. bytes with indices `[0..size)`, are cleared. |
228 > When clearing the buffer, only the "live" data, i.e., bytes with indices `[0..size)`, are cleared. |
229 > If you which to clear the entire buffer's memory, you would need to set the size to the capacity, first. |
229 > If you want to clear the entire buffer's memory, you would need to set the size to the capacity, first. |
230 |
230 |
231 > If the `CX_BUFFER_COPY_ON_WRITE` flag is set, `cxBufferClear()` behaves exactly like `cxBufferReset()`, |
231 > If the `CX_BUFFER_COPY_ON_WRITE` flag is set, `cxBufferClear()` behaves exactly like `cxBufferReset()`, |
232 > because writing to the contents is disallowed. |
232 > because writing to the contents is disallowed. |
233 >{style="note"} |
233 >{style="note"} |
234 |
234 |
261 int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); |
261 int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); |
262 ``` |
262 ``` |
263 |
263 |
264 The function `cxBufferShift()` moves the contents within the buffer by the specified `shift` offset, |
264 The function `cxBufferShift()` moves the contents within the buffer by the specified `shift` offset, |
265 where a negative offset means a shift to the left, and a positive offset means a shift to the right. |
265 where a negative offset means a shift to the left, and a positive offset means a shift to the right. |
266 It also adjusts the current position within the buffer, and in case of a right shift also the size, by the same offset. |
266 It also adjusts the current position within the buffer, and in the case of a right shift also the size, by the same offset. |
267 |
267 |
268 Data that is shift to the left, is always discarded when the new position of a byte would be smaller than zero. |
268 Data shifted to the left is always discarded when the new position of a byte would be smaller than zero. |
269 If the new position would be smaller than zero, it is set exactly to zero. |
269 If the new position would be smaller than zero, it is set exactly to zero. |
270 |
270 |
271 When data is shift to the right, the behavior depends on the `CX_BUFFER_AUTO_EXTEND` flag. |
271 When data is shift to the right, the behavior depends on the `CX_BUFFER_AUTO_EXTEND` flag. |
272 If set, the function extends the buffer's capacity before moving the data. |
272 If set, the function extends the buffer's capacity before moving the data. |
273 Otherwise, the function discards all data that would exceed the buffer's capacity, and both the size and the position are equal to the capacity |
273 Otherwise, the function discards all data that would exceed the buffer's capacity, and both the size and the position are equal to the capacity |
274 (which means, `cxBufferEof()` returns `true` after the operation). |
274 (which means, `cxBufferEof()` returns `true` after the operation). |
275 |
275 |
276 The functions `cxBufferShiftRight()` and `cxBufferShiftLeft()` accept a larger (but in both cases positive) shift offset, |
276 The functions `cxBufferShiftRight()` and `cxBufferShiftLeft()` accept a larger (but in both cases positive) shift offset, |
277 which usually does not make much sense on a 64-bit platform where `off_t` is already large enough to represent any reasonable offset. |
277 which usually makes little sense on a 64-bit platform where `off_t` is already large enough to represent any reasonable offset. |
278 You may, however, still use those function to express more explicitly in your code in which direction you want the contents to be shifted. |
278 You may, however, still use those functions to express more explicitly in your code in which direction you want the contents to be shifted. |
279 |
279 |
280 ## Flushing |
280 ## Flushing |
281 |
281 |
282 ```C |
282 ```C |
283 #include <cx/buffer.h> |
283 #include <cx/buffer.h> |
308 > and starts flushing only when that threshold is exceeded. |
308 > and starts flushing only when that threshold is exceeded. |
309 |
309 |
310 Flushing happens by invoking the `wfunc` up to `blkmax` times, writing up to `blksize` bytes from the buffer to the `target` with each call. |
310 Flushing happens by invoking the `wfunc` up to `blkmax` times, writing up to `blksize` bytes from the buffer to the `target` with each call. |
311 The target might not accept all bytes (i.e. the `wfunc` return value indicates that fewer items have been written than requested), |
311 The target might not accept all bytes (i.e. the `wfunc` return value indicates that fewer items have been written than requested), |
312 in which case the remaining data remains in the buffer. |
312 in which case the remaining data remains in the buffer. |
313 That means, the buffer is effectively [shifted](#shift-contents) left by the number of successfully flushed bytes. |
313 That means the buffer is effectively [shifted](#shift-contents) left by the number of successfully flushed bytes. |
314 |
314 |
315 > When you write large amounts of data to a buffer, multiple flush cycles might happen. |
315 > When you write large amounts of data to a buffer, multiple flush cycles might happen. |
316 > After the first flush operations completed, the reclaimed space in the buffer is filled first, but if that |
316 > After the first flush operations are completed, the reclaimed space in the buffer is filled first, but if that |
317 > is not sufficient, another flush may be triggered within the same invocation of the write operation. |
317 > is not enough, another flush may be triggered within the same invocation of the write operation. |
318 > |
318 > |
319 > That means, as much data is written to the buffer and/or flushed as possible, until neither the flush target, nor the buffer accept more data. |
319 > That means as much data is written to the buffer and/or flushed as possible, until neither the flush target nor the buffer accept more data. |
320 >{style="note"} |
320 >{style="note"} |
321 |
321 |
322 > The function `cxBufferFlush()` simply returns zero when flushing was not enabled via `cxBufferEnableFlushing()`. |
322 > The function `cxBufferFlush()` simply returns zero when flushing was not enabled via `cxBufferEnableFlushing()`. |
323 |
323 |
324 <seealso> |
324 <seealso> |