Resizable binary buffers¶
#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 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 cork_buffer_to_slice()
or 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 buf
and 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 Managed binary buffers or Binary slices.
-
struct
cork_buffer
¶ A resizable binary buffer.
-
void *
buf
¶ The current contents of the buffer.
-
size_t
size
¶ The current size of the buffer.
-
void *
-
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.
-
struct cork_buffer *
cork_buffer_new
(void)¶ Allocate and initialize a new buffer instance.
-
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
cork_buffer_init()
orCORK_BUFFER_INIT()
. You must not use this function to free a buffer allocated usingcork_buffer_free()
.
-
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
cork_buffer_new()
. You must not use this function to free a buffer initialized usingcork_buffer_init()
orCORK_BUFFER_INIT()
.
-
bool
cork_buffer_equal
(const struct cork_buffer *buffer1, const struct cork_buffer *buffer2)¶ Compare two buffers for equality.
-
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.
-
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 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.
-
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.
-
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.
-
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.
-
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.
-
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 thesizeof
operator to determine the length of the string at compile time. The_string
variants work with any C string; we use the builtinstrlen
function to determine the length of the string.
-
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-standardva_list
instance.
Pretty-printing¶
We also provide several helper functions for adding pretty-printed content to a
cork_buffer
.
-
void
cork_buffer_append_indent
(struct cork_buffer *buffer, size_t indent)¶ Append indent spaces to buffer.
-
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.
-
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 thehex_dump
representation. If it spans multiple lines, we’ll use themultiline
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.
-
struct cork_managed_buffer *
cork_buffer_to_managed_buffer
(struct cork_buffer *buffer)¶ Create a new managed buffer to manage the contents of a
cork_buffer
instance. buffer must have been allocated on the heap (i.e., usingcork_buffer_new()
, and notcork_buffer_init()
). We take ownership of buffer, regardless of whether we’re able to successfully create a newcork_managed_buffer
instance. You must not try to free buffer yourself.
-
int
cork_buffer_to_slice
(struct cork_buffer *buffer, struct cork_slice *slice)¶ Initialize a new slice to manage the contents of buffer. buffer must have been allocated on the heap (i.e., using
cork_buffer_new()
, and notcork_buffer_init()
). We take ownership of buffer, regardless of whether we’re able to successfully create a newcork_managed_buffer
instance. You must not try to free buffer yourself.The slice will point into the contents of a new managed buffer instance. The managed buffer isn’t returned directly, though you can create additional slices into it using the usual
cork_slice
methods.Regardless of whether we can initialize the slice successfully, you must call
cork_slice_finish()
on slice when you’re done with the slice.
-
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.