1 # Buffer
2
3 This buffer implementation can be used to read from or write to memory like you would do with a stream.
4
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 stream to the buffer and vice versa.
7
8 More features for convenient use of the buffer can be enabled, like automatic memory management,
9 or automatic resizing of the buffer space.
10
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()`.
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.
15
16 ## Example
17
18 In the following example we use a `CxBuffer`, the `bprintf()` macro from the [UCX printf header](printf.h.md),
19 and a [UCX map](map.h.md) to create a simple HTTP GET request.
20
21 ```C
22 #include <cx/buffer.h>
23 #include <cx/printf.h>
24 #include <cx/map.h>
25
26 void send_get_request(
27 void *net_target,
28 cx_write_func net_write,
29 const char *host,
30 const char *path,
31 CxMap *header
32 ) {
33 // initialize buffer and use stack memory for small requests
34 char stackmem[128];
35 CxBuffer buf;
36 cxBufferInit(cxDefaultAllocator,
37 &buf, stackmem, sizeof(stackmem),
38 CX_BUFFER_COPY_ON_EXTEND // move to heap when request is larger
39 );
40
41 // basic request data
42 cx_bprintf(&buf, "GET %s HTTP/1.1\r\n", path);
43 cx_bprintf(&buf, "Host: %s\r\n", host);
44
45 // add headers
46 CxMapIterator iter = cxMapIterator(header);
47 cx_foreach(CxMapEntry*, entry, iter) {
48 cxstring name = cx_strn(entry->key->data, entry->key->len);
49 cx_bprintf(&buf, "%" CX_PRIstr ": %s\r\n",
50 CX_SFMT(name), entry->value
51 );
52 }
53
54 // blank line terminates
55 cxBufferWrite("\r\n", 1, 2, &buf);
56
57 // write request to the network stream and destroy the buffer
58 net_write(buf.space, 1, buf.size, net_target);
59 cxBufferDestroy(&buf);
60 }
61 ```
62
63 ## Create
64
65 ```C
66 #include <cx/buffer.h>
67
68 int cxBufferInit(CxBuffer *buffer,
69 const CxAllocator *allocator,
70 void *space, size_t capacity, int flags);
71
72 CxBuffer *cxBufferCreate(const CxAllocator *allocator,
73 void *space,size_t capacity, int flags);
74
75 // available flags:
76 #define CX_BUFFER_DEFAULT
77 #define CX_BUFFER_FREE_CONTENTS
78 #define CX_BUFFER_AUTO_EXTEND
79 #define CX_BUFFER_COPY_ON_WRITE
80 #define CX_BUFFER_COPY_ON_EXTEND
81 ```
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.
84
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`.
87 If `space` is `NULL`, the `allocator` is used to allocate enough space to match the desired `capacity`.
88
89 The `flags` argument is a bit-wise-or combination of the constants listed below.
90
91 The function `cxBufferCreate()` is equivalent, except that it uses the `allocator` to allocate a new `CxBuffer` structure, instead of initializing an existing one.
92
93 If any allocation fails, `cxBufferInit()` returns non-zero, or `cxBufferCreate()` returns `NULL`, respectively.
94
95 | Flag | Description |
96 |--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
97 | CX_BUFFER_DEFAULT | Alias for zero, meaning no flags are set. |
98 | CX_BUFFER_FREE_CONTENTS | When the buffer structure is [destroyed](#destroy), the memory for the contents is also automatically deallocated. |
99 | CX_BUFFER_AUTO_EXTEND | When a write operation would exceed the buffers capacity, the buffer is grown automatically with a call to `cxBufferMinimumCapacity()`. |
100 | CX_BUFFER_COPY_ON_WRITE | Any write operation will result in allocating a copy of the data, first. This is particularly useful, when a buffer is initialized with read-only memory. |
101 | CX_BUFFER_COPY_ON_EXTEND | Only when a write operation would exceed the buffers capacity, the copy-on-write operation is performed. This is useful, when a buffer is initialized with read-write memory which is allocated on the stack. |
102
103
104 ## Destroy
105
106 ```C
107 #include <cx/buffer.h>
108
109 void cxBufferDestroy(CxBuffer *buffer);
110
111 void cxBufferFree(CxBuffer *buffer);
112 ```
113
114 The above functions destroy the buffer and deallocate the buffer's memory when the `CX_BUFFER_FREE_CONTENTS` flag is set.
115
116 The function `cxBufferDestroy()` is to be used when the buffer was initialized with `cxBufferInit()`,
117 and the function `cxBufferFree()` is to be used when the buffer was created with `cxBufferCreate()`.
118 The only difference is, that `cxBufferFree()` additionally deallocates the memory of the `CxBuffer` structure.
119
120 ## Capacity Management
121
122 ```C
123 #include <cx/buffer.h>
124
125 int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
126
127 int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
128
129 int cxBufferReserve(CxBuffer *buffer, size_t capacity);
130
131 void cxBufferShrink(CxBuffer *buffer, size_t reserve);
132 ```
133
134 The function `cxBufferMaximumCapacity()` specifies an upper limit for auto-growing the buffer's capacity.
135 If the new threshold is smaller than the current capacity, this function fails and returns non-zero.
136
137 The function `cxBufferMinimumCapacity()` guarantees a buffer capacity of _at least_ `capacity`.
138 It will grow the capacity in powers of two until the system's page size is reached.
139 Then, the new capacity will be a multiple of the page size.
140 The function returns non-zero if increasing the capacity was attempted unsuccessfully;
141 that includes attempts to specify a minimum capacity that exceeds the maximum capacity.
142
143 The function `cxBufferReserve()`, on the other hand, will reallocate the buffer's space to match exactly the requested `capacity`
144 and the contents are truncated if required.
145
146 You should use `cxBufferReserve()` when you know precisely the required capacity beforehand
147 and `cxBufferMinimumCapacity()` when it is likely that the buffer needs to regrow soon.
148
149 The function `cxBufferShrink()` can be used to shrink the capacity of the buffer to its current size
150 plus a number of `reserve` bytes.
151 If the current capacity is not larger than the size plus the reserve bytes, the function will do nothing.
152
153 > If the buffer is in a copy-on-write state, `cxBufferMinimumCapacity()` will perform the copy-on-write action
154 > before reallocating the space.
155 > The function `cxBufferShrink()` on the other hand does _nothing_ when the buffer is in a copy-on-write state,
156 > because it makes little sense to copy the memory just to have it in a smaller memory region.
157
158 ## Write
159
160 ```C
161 #include <cx/buffer.h>
162
163 size_t cxBufferWrite(const void *ptr, size_t size, size_t nitems,
164 CxBuffer *buffer);
165
166 int cxBufferPut(CxBuffer *buffer, int c);
167
168 size_t cxBufferPutString(CxBuffer *buffer, AnyStr str);
169
170 int cxBufferTerminate(CxBuffer *buffer);
171
172 size_t cxBufferAppend(const void *ptr, size_t size, size_t nitems,
173 CxBuffer *buffer);
174
175 size_t cxBufferAppendString(CxBuffer *buffer, AnyStr str);
176 ```
177
178 The primary function for writing to a buffer is `cxBufferWrite()`
179 which writes up to `nitems` with `size` bytes each from the memory pointed to by `ptr` to the buffer.
180
181 When the capacity of the buffer is not sufficient and the `CX_BUFFER_AUTO_EXTEND` is not set in the buffer,
182 items that do not fit into the buffer are discarded.
183 The function then returns the actual number of items successfully written.
184 This equals the number of bytes if and only if `size=1`.
185 If `CX_BUFFER_AUTO_EXTEND` is set, the buffer is grown to it's maximum capacity
186 (see `cxBufferMaximumCapacity()` in [](#capacity-management)).
187 In case the allocation for auto-extension fails, the function immediately returns zero and does not write any data.
188
189 The function `cxBufferPut()` is a `putc()`-like wrapper for `cxBufferWrite()` which writes the character `c`,
190 converted to an `unsigned char` to the buffer.
191 Just like `putc()` this function returns the written character on success, and `EOF` on failure,
192 but it does _not_ set `errno` on failure.
193
194 The function `cxBufferPutString()` is a convenience function that uses `cx_strcast()` to convert any supported string type to a `cxstring` and then invokes `cxBufferWrite()`.
195 Similarly, `cxBufferAppendString()` performs the same operation with `cxBufferAppend()`.
196
197 All the above functions advance the buffer position by the number of bytes written
198 and cause the _size_ of the buffer to grow, if necessary, to contain all written bytes.
199 On the other hand, `cxBufferTerminate()` writes a zero-byte at the current position and shrinks the buffer,
200 effectively creating a zero-terminated string whose size equals the buffer size.
201
202 > If you use cxBufferTerminate() on a buffer with the `CX_BUFFER_COPY_ON_EXTEND` flag set, the shrink operation is skipped.
203 > Using `cxBufferTerminate()` on a buffer with the `CX_BUFFER_COPY_ON_WRITE` flag set, will copy the entire memory just to add the zero-terminator.
204
205 The function `cxBufferAppend()` writes the data to the end of the buffer (given by its size) regardless of the current position,
206 and it also does _not_ advance the position.
207
208 ## Read
209
210 ```C
211 #include <cx/buffer.h>
212
213 size_t cxBufferRead(void *ptr, size_t size, size_t nitems,
214 CxBuffer *buffer);
215
216 int cxBufferGet(CxBuffer *buffer);
217
218 bool cxBufferEof(const CxBuffer *buffer);
219 ```
220
221 The function `cxBufferRead()` reads `nitems` number of items of `size` bytes each from the `buffer`
222 and stores them into the memory pointed to by `ptr`, which must be large enough to hold the contents.
223 The function returns the actual bytes read, which might be lower if the desired number of items is not available.
224
225 The function `cxBufferGet()` is a `fgetc()`-like function which returns the next byte in the buffer converted to an `int`.
226 Similar to `fgetc()` it returns `EOF` when there are no more bytes in the buffer.
227
228 When all bytes from the buffer have been read, the function `cxBufferEof()` returns true.
229
230 All read functions advance the position in the buffer by the number of read bytes.
231
232 > When you want to read from a buffer that you previously used for writing, do not forget to set the position
233 > in the buffer to zero, e.g. by calling `cxBufferSeek()`.
234
235 ## Reset and Clear
236
237 ```C
238 #include <cx/buffer.h>
239
240 void cxBufferReset(CxBuffer *buffer);
241
242 void cxBufferClear(CxBuffer *buffer);
243
244 size_t cxBufferPop(CxBuffer *buffer,
245 size_t size, size_t nitems);
246 ```
247
248 The function `cxBufferReset()` sets both size and position of the buffer to zero,
249 and `cxBufferClear()` additionally uses `memset()` to set every byte in the buffer to zero.
250
251 > When clearing the buffer, only the "live" data, i.e., bytes with indices `[0..size)`, are cleared.
252 > If you want to clear the entire buffer's memory, you would need to set the size to the capacity, first.
253
254 The function `cxBufferPop()` erases the last `nitems` with the given `size` and returns the number of items that could be erased.
255 When the buffer contains fewer bytes than requested and the number of bytes is not divisible by the item `size`,
256 the remainder will stay in the buffer.
257
258 > If the `CX_BUFFER_COPY_ON_WRITE` flag is set, `cxBufferClear()` behaves exactly like `cxBufferReset()`,
259 > because writing to the contents is disallowed.
260 >{style="note"}
261
262 ## Random Access
263
264 ```C
265 #include <cx/buffer.h>
266
267 int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
268 ```
269
270 The function `cxBufferSeek()` is a `fseek()`-like function that sets the current position in the buffer
271 relative to the start (when `whence` is `SEEK_SET`), the current position (when `whence` is `SEEK_CUR`),
272 or end (when `whence` is `SEEK_END`) of the buffer.
273
274 If the resulting position is negative, or larger than the size (i.e. the first unused byte),
275 this function returns non-zero and sets `errno` to `EINVAL`.
276
277 > Note that the behavior of `cxBufferSeek()` when seeking beyond the end of the buffer is not exactly the same as for POSIX `fseek()`.
278
279 ## Shift Contents
280
281 ```C
282 #include <cx/buffer.h>
283
284 int cxBufferShift(CxBuffer *buffer, off_t shift);
285
286 int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
287
288 int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
289 ```
290
291 The function `cxBufferShift()` moves the contents within the buffer by the specified `shift` offset,
292 where a negative offset means a shift to the left, and a positive offset means a shift to the right.
293 It also adjusts the current position within the buffer, and in the case of a right shift also the size, by the same offset.
294
295 Data shifted to the left is always discarded when the new position of a byte would be smaller than zero.
296 When bytes are discarded, the new position of the buffer is set to zero.
297
298 When data is shift to the right, the behavior depends on the `CX_BUFFER_AUTO_EXTEND` flag.
299 If set, the function extends the buffer's capacity before moving the data.
300 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
301 (which means, `cxBufferEof()` returns `true` after the operation).
302
303 The functions `cxBufferShiftRight()` and `cxBufferShiftLeft()` accept a larger (but in both cases positive) shift offset,
304 which usually makes little sense on a 64-bit platform where `off_t` is already large enough to represent any reasonable offset.
305 You may, however, still use those functions to express more explicitly in your code in which direction you want the contents to be shifted.
306
307 <seealso>
308 <category ref="apidoc">
309 <a href="https://ucx.sourceforge.io/api/buffer_8h.html">buffer.h</a>
310 </category>
311 </seealso>
312