1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
.. _buffer:
************************
Resizable binary buffers
************************
.. highlight:: c
::
#include <libcork/ds.h>
This section defines a resizable binary buffer type. This class can
also be used to construct C strings, when you don't know the size of
the string in advance.
This class is not reference counted; we assume that there's a single
owner of the buffer. The contents of a :c:type:`cork_buffer` are fully
mutable. If you want to turn the buffer into something that's safe to
pass between threads, you can use the :c:func:`cork_buffer_to_slice()`
or :c:func:`cork_buffer_to_managed_buffer()` functions to create an
immutable managed wrapper around the buffer.
You can read the contents of the buffer by accessing the :c:member:`buf
<cork_buffer.buf>` and :c:member:`size <cork_buffer.size>` fields
directly. However, to modify the contents of a buffer, you should use
the mutator functions described below, since they take care of
automatically resizing the underlying buffer when necessary.
.. note::
This class always creates its own copy of any data added to the
buffer; there aren't any methods for wrapping existing buffers
without copying. If you want to do that, you should use
:ref:`managed-buffer` or :ref:`slice`.
.. type:: struct cork_buffer
A resizable binary buffer.
.. member:: void \*buf
The current contents of the buffer.
.. member:: size_t size
The current size of the buffer.
.. function:: void cork_buffer_init(struct cork_buffer \*buffer)
struct cork_buffer CORK_BUFFER_INIT()
Initialize a new buffer instance that you've allocated yourself
(usually on the stack). The ``CORK_BUFFER_INIT`` version can only be
used as a static initializer.
The preallocated ``cork_buffer`` instance that you provide doesn't
include space for the content of the buffer; this will be allocated
automatically as content is added.
.. function:: struct cork_buffer \*cork_buffer_new(void)
Allocate and initialize a new buffer instance.
.. function:: void cork_buffer_done(struct cork_buffer \*buffer)
Finalize a buffer, freeing any content that it contains. This
function should only be used for buffers that you allocated yourself,
and initialized using :c:func:`cork_buffer_init()` or
:c:func:`CORK_BUFFER_INIT()`. You must **not** use this function to
free a buffer allocated using :c:func:`cork_buffer_free()`.
.. function:: void cork_buffer_free(struct cork_buffer \*buffer)
Finalize and deallocate a buffer, freeing any content that it
contains. This function should only be used for buffers allocated
using :c:func:`cork_buffer_new()`. You must **not** use this
function to free a buffer initialized using
:c:func:`cork_buffer_init()` or :c:func:`CORK_BUFFER_INIT()`.
.. function:: bool cork_buffer_equal(const struct cork_buffer \*buffer1, const struct cork_buffer \*buffer2)
Compare two buffers for equality.
.. function:: void cork_buffer_ensure_size(struct cork_buffer \*buffer, size_t desired_size)
Ensure that a buffer has allocated enough space to store at least
*desired_size* bytes. We won't shrink the size of the buffer's
internal storage; if the buffer has already allocated at least
*desired_size* bytes, the function acts as a no-op.
.. function:: uint8_t cork_buffer_byte(struct cork_buffer \*buffer, size_t index)
char cork_buffer_char(struct cork_buffer \*buffer, size_t index)
Return the byte or character at the given index in *buffer*.
Mutator functions
-----------------
Most of the mutator functions defined in this section come in two
variants: a ``_set`` function, which clears the buffer before adding new
content, and an ``_append`` function, which retains the old content,
adding the new content to the end of the buffer.
Each mutator function will automatically append an extra ``NUL`` byte to
the end of whatever content is placed into the buffer. However, this
``NUL`` byte will **not** be included in the :c:member:`size
<cork_buffer.size>` of the buffer. This ensures that the contents of
any ``cork_buffer`` can be used as a ``NUL``\ -terminated C string
(assuming that there aren't any internal ``NUL``\ s), even if the buffer
is constructed from a data source that doesn't include ``NUL``
terminators.
.. function:: void cork_buffer_clear(struct cork_buffer \*buffer)
Clear a buffer. This does not free any storage that the buffer has
allocated; this storage will be reused if you add contents back to the
buffer.
.. function:: void cork_buffer_truncate(struct cork_buffer \*buffer, size_t length)
Truncate a buffer so that contains no more than *length* bytes. If the
buffer is already shorter than this, it is not modified.
.. function:: void cork_buffer_copy(struct cork_buffer \*dest, const struct cork_buffer \*src)
void cork_buffer_append_copy(struct cork_buffer \*dest, const struct cork_buffer \*src)
Copy the contents of the *src* buffer into *dest*. The ``_set`` variant
clears the buffer first, while the ``_append`` variant adds *src* to whatever
content is already there.
.. function:: void cork_buffer_set(struct cork_buffer \*buffer, const void \*src, size_t length)
void cork_buffer_append(struct cork_buffer \*buffer, const void \*src, size_t length)
Copy the contents of *src* into a buffer. The ``_set`` variant
clears the buffer first, while the ``_append`` variant adds *src* to
whatever content is already there.
.. function:: void cork_buffer_set_string(struct cork_buffer \*buffer, const char \*str)
void cork_buffer_append_string(struct cork_buffer \*buffer, const char \*str)
void cork_buffer_set_literal(struct cork_buffer \*buffer, const char \*str)
void cork_buffer_append_literal(struct cork_buffer \*buffer, const char \*str)
Copy the contents of *str* (which must be a ``NUL``\ -terminated C
string) into a buffer. The ``_set`` variants clears the buffer first,
while the ``_append`` variants adds *str* to whatever content is
already there. The ``_literal`` variants only work when *str* is a C string
literal; we use the ``sizeof`` operator to determine the length of the string
at compile time. The ``_string`` variants work with any C string; we use the
builtin ``strlen`` function to determine the length of the string.
.. function:: void cork_buffer_printf(struct cork_buffer \*buffer, const char \*format, ...)
void cork_buffer_vprintf(struct cork_buffer \*buffer, const char \*format, va_list args)
void cork_buffer_append_printf(struct cork_buffer \*buffer, const char \*format, ...)
void cork_buffer_append_vprintf(struct cork_buffer \*buffer, const char \*format, va_list args)
Format data according to a ``printf`` format string, placing the
result into a buffer. The ``_append`` variants add the formatted
string to whatever content is already in the buffer; the non-\
``_append`` variants clear the buffer first. The ``_printf``
variants are vararg functions, and take in the format string's data
as direct parameters. The ``_vprintf`` variants can be used within
another vararg function, and let you pass in the format string's data
as a C99-standard ``va_list`` instance.
Pretty-printing
---------------
We also provide several helper functions for adding pretty-printed content to a
``cork_buffer``.
.. function:: void cork_buffer_append_indent(struct cork_buffer \*buffer, size_t indent)
Append *indent* spaces to *buffer*.
.. function:: void cork_buffer_append_c_string(struct cork_buffer \*buffer, const char \*str, size_t length)
Append the C string literal representation of *str* to *buffer*. This will
include opening and closing double quotes, and any non-printable characters
will be escaped. (We will use the standard letter-based escapes where
possible, and fall back on ``"\xXX"`` hexadecimal escapes for other
non-printable characters.) The result is guaranteed to stay on a single
line, since any embedded newlines will be converted to a ``\n`` escape
sequence.
.. function:: void cork_buffer_append_hex_dump(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length)
void cork_buffer_append_multiline(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length)
void cork_buffer_append_binary(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length)
Append a pretty-printed representation of *str* to *buffer*. All of these
functions can produce multiple lines of output. All lines except for the
first will be prefaced with *indent* space characters. The final line will
**not** have a trailing newline.
The ``hex_dump`` variant will output a hex-dump representation of *str*.
This will include the hexadecimal representation of each byte, and the actual
character of any printable byte.
The ``multiline`` variant appends the raw content of *str* to the buffer,
without making any attempt to sanitize non-printable characters. (That means
you should only call this variant if you know that *str* contains only
printable characters.) If *str* itself spans multiple lines, then we'll
insert indentation to make sure that we satisfy the indentation rules
described above.
The ``binary`` variant autodetects how to best render *str*. If it contains
any non-printable characters, then we'll use the ``hex_dump`` representation.
If it spans multiple lines, we'll use the ``multiline`` representation.
Otherwise, we'll append the content directly without any modification.
Other binary data structures
----------------------------
The ``cork_buffer`` class is the only binary data class that is mutable;
this comes at the cost of only being usable by a single owner thread or
function at a time. Once you have constructed a binary string or
payload using a ``cork_buffer``, you can use the functions in this
section to produce a corresponding instance of one of libcork's
sharable, immutable binary data types.
.. function:: struct cork_managed_buffer \*cork_buffer_to_managed_buffer(struct cork_buffer \*buffer)
Create a new :ref:`managed buffer <managed-buffer>` to manage the
contents of a ``cork_buffer`` instance. *buffer* must have been
allocated on the heap (i.e., using :c:func:`cork_buffer_new()`, and
not :c:func:`cork_buffer_init()`). We take ownership of *buffer*,
regardless of whether we're able to successfully create a new
:c:type:`cork_managed_buffer` instance. You must **not** try to free
*buffer* yourself.
.. function:: int cork_buffer_to_slice(struct cork_buffer \*buffer, struct cork_slice \*slice)
Initialize a new :ref:`slice <slice>` to manage the contents of
*buffer*. *buffer* must have been allocated on the heap (i.e., using
:c:func:`cork_buffer_new()`, and not :c:func:`cork_buffer_init()`).
We take ownership of *buffer*, regardless of whether we're able to
successfully create a new :c:type:`cork_managed_buffer` instance.
You must **not** try to free *buffer* yourself.
The slice will point into the contents of a new :ref:`managed buffer
<managed-buffer>` instance. The managed buffer isn't returned
directly, though you can create additional slices into it using the
usual :c:type:`cork_slice` methods.
Regardless of whether we can initialize the slice successfully, you
**must** call :c:func:`cork_slice_finish()` on *slice* when you're
done with the slice.
.. function:: struct cork_stream_consumer \*cork_buffer_to_stream_consumer(struct cork_buffer \*buffer)
Create a new stream consumer that appends any received data into
*buffer*.
We do **not** take control of *buffer*. You retain responsibility
for freeing the buffer, and you must ensure that it remains allocated
and valid for the entire lifetime of the stream consumer that we
return.
|