@@ -20,6 +20,15 @@
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
+/**
+ * @file
+ * @brief Buffer management functions and garbage collection.
+ *
+ * This module provides the core \c buffer type used throughout OpenVPN to
+ * hold packet data, as well as a simple memory management system
+ * (\c gc_arena / \c gc_malloc()) and various string/memory utility functions.
+ */
+
#ifndef BUFFER_H
#define BUFFER_H
@@ -27,6 +36,7 @@
#include "error.h"
#include "integer.h"
+/** Maximum allowed size (in bytes) for a single buffer allocation. */
#define BUF_SIZE_MAX 1000000
/*
@@ -115,51 +125,213 @@
*/
struct gc_arena
{
- struct gc_entry *list; /**< First element of the linked list of
- * \c gc_entry structures. */
- struct gc_entry_special *list_special;
+ struct gc_entry *list; /**< First element of the linked list of
+ * \c gc_entry structures. */
+ struct gc_entry_special *list_special; /**< First element of the linked
+ * list of \c gc_entry_special
+ * structures for allocations
+ * requiring a custom free
+ * function. */
};
+/** Return a pointer to the start of the buffer content. @see buf_bptr() */
#define BPTR(buf) (buf_bptr(buf))
+/** Return a pointer one past the end of the buffer content. @see buf_bend() */
#define BEND(buf) (buf_bend(buf))
+/** Return a pointer to the last byte of the buffer content, or NULL if empty. @see buf_blast() */
#define BLAST(buf) (buf_blast(buf))
+/** Return the length of the buffer content in bytes. @see buf_len() */
#define BLEN(buf) (buf_len(buf))
+/** Return the length of the buffer content as a \c size_t. @see buf_len() */
#define BLENZ(buf) ((size_t)buf_len(buf))
+/** Return true iff the buffer is defined (has non-NULL data pointer). @see buf_defined() */
#define BDEF(buf) (buf_defined(buf))
+/** Return the buffer content pointer cast to \c char *. @see buf_str() */
#define BSTR(buf) (buf_str(buf))
+/** Return the number of bytes available for appending to the buffer. @see buf_forward_capacity() */
#define BCAP(buf) (buf_forward_capacity(buf))
+/**
+ * Zeroise and reset a buffer.
+ *
+ * Sets all allocated memory to zero and resets \c offset and \c len to zero,
+ * but does not free the underlying memory.
+ *
+ * @param buf The buffer to clear.
+ */
void buf_clear(struct buffer *buf);
+/**
+ * Free the memory allocated for a buffer.
+ *
+ * Frees \c buf->data and resets the buffer to an undefined state.
+ *
+ * @param buf The buffer whose memory is to be freed.
+ */
void free_buf(struct buffer *buf);
+/**
+ * Assign the content of one buffer to another.
+ *
+ * Copies the content of \c src into \c dest. The destination buffer must
+ * already be allocated with sufficient capacity.
+ *
+ * @param dest Destination buffer to write into.
+ * @param src Source buffer to read from.
+ *
+ * @return true on success, false if \c dest has insufficient capacity.
+ */
bool buf_assign(struct buffer *dest, const struct buffer *src);
+/**
+ * Securely clear a null-terminated string.
+ *
+ * Overwrites all characters of \c str with zeroes.
+ *
+ * @param str The string to clear.
+ */
void string_clear(char *str);
+/**
+ * Return the number of elements in a NULL-terminated array of strings.
+ *
+ * @param array NULL-terminated array of string pointers.
+ *
+ * @return Number of non-NULL elements.
+ */
int string_array_len(const char **array);
+/**
+ * Safely compute the product of two sizes plus an extra amount.
+ *
+ * Each of \c m1, \c m2, and \c extra, as well as the final result, are
+ * checked against \c ALLOC_SIZE_MAX. If any value exceeds the limit the
+ * function calls \c msg(M_FATAL) and does not return.
+ *
+ * @param m1 First multiplicand.
+ * @param m2 Second multiplicand.
+ * @param extra Value to add to the product.
+ *
+ * @return \c m1 * \c m2 + \c extra.
+ */
size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra);
+/** Flag for print_argv(): wrap each argument in square brackets. */
#define PA_BRACKET (1 << 0)
+/**
+ * Format a NULL-terminated argument vector as a single string.
+ *
+ * @param p NULL-terminated array of string pointers to format.
+ * @param gc Garbage collection arena for the returned string.
+ * @param flags Formatting flags (e.g. \c PA_BRACKET).
+ *
+ * @return Newly allocated string representation of \c p.
+ */
char *print_argv(const char **p, struct gc_arena *gc, const unsigned int flags);
+/**
+ * Report a buffer size error and abort.
+ *
+ * Called when a requested buffer size exceeds \c BUF_SIZE_MAX or is otherwise
+ * invalid. Calls \c msg(M_FATAL) and does not return.
+ *
+ * @param size The invalid size that was requested.
+ */
void buf_size_error(const size_t size);
+/**
+ * Allocate a buffer of the given size.
+ *
+ * Calls \c out_of_memory() if the allocation fails. The caller is responsible
+ * for freeing the memory.
+ *
+ * @param size Number of bytes to allocate.
+ *
+ * @return Newly allocated buffer of capacity \c size.
+ */
struct buffer alloc_buf(size_t size);
-struct buffer alloc_buf_gc(size_t size,
- struct gc_arena *gc); /* allocate buffer with garbage collection */
+/**
+ * Allocate a buffer of the given size under garbage collection.
+ *
+ * The allocated memory is registered with \c gc so that it is freed when
+ * \c gc_free() is called on the arena. Calls \c out_of_memory() if the
+ * allocation fails.
+ *
+ * @param size Number of bytes to allocate.
+ * @param gc Garbage collection arena to register the allocation with.
+ *
+ * @return Newly allocated buffer of capacity \c size.
+ */
+struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc);
+/**
+ * Duplicate a buffer, including its content.
+ *
+ * Allocates a new buffer with the same capacity as \c buf and copies its
+ * content. Calls \c out_of_memory() if the allocation fails.
+ *
+ * @param buf The buffer to duplicate.
+ *
+ * @return A newly allocated copy of \c buf.
+ */
struct buffer clone_buf(const struct buffer *buf);
+/**
+ * Allocate memory and, optionally, zero it.
+ *
+ * The allocation is registered with \c a so that it is freed when
+ * \c gc_free() is called on the arena. Calls \c out_of_memory() if the
+ * allocation fails.
+ *
+ * @param size Number of bytes to allocate.
+ * @param clear If true, zeroise the allocated memory before returning.
+ * @param a Garbage collection arena to register the allocation with.
+ *
+ * @return Pointer to the newly allocated memory.
+ */
void *gc_malloc(size_t size, bool clear, struct gc_arena *a);
+/**
+ * Duplicate a string, allocating memory under garbage collection.
+ *
+ * @param str The string to duplicate. May be NULL, in which case NULL
+ * is returned.
+ * @param gc Garbage collection arena for the returned string, or NULL
+ * to use plain \c malloc().
+ *
+ * @return A newly allocated copy of \c str, or NULL if \c str is NULL.
+ */
char *string_alloc(const char *str, struct gc_arena *gc);
+/**
+ * Allocate a buffer containing a copy of the given string.
+ *
+ * Wraps \c string_alloc(), so \c gc may be NULL (plain \c malloc() is used
+ * in that case). The buffer \c len reflects the string length, excluding
+ * the null terminator, even though the terminator is present in the
+ * allocated memory.
+ *
+ * @param str The string to copy into the buffer. Must not be NULL.
+ * @param gc Garbage collection arena for the allocation, or NULL.
+ *
+ * @return A buffer whose content is a copy of \c str.
+ */
struct buffer string_alloc_buf(const char *str, struct gc_arena *gc);
+/**
+ * Register an address with a custom free function in a garbage collection
+ * arena.
+ *
+ * When \c gc_free() is called on \c a, \c free_function(addr) will be
+ * invoked to release the memory. Use this for allocations that require
+ * something other than plain \c free(), such as \c freeaddrinfo().
+ *
+ * @param addr Pointer to the memory to register.
+ * @param free_function Function to call to free \c addr.
+ * @param a Garbage collection arena to register with.
+ */
void gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a);
/**
@@ -177,6 +349,7 @@
#ifdef BUF_INIT_TRACKING
#define buf_init(buf, offset) buf_init_debug(buf, offset, __FILE__, __LINE__)
+/** Debug variant of \c buf_init_dowork() that records the call site for alignment verification. */
bool buf_init_debug(struct buffer *buf, int offset, const char *file, int line);
#else
@@ -185,31 +358,62 @@
/* inline functions */
+
+/**
+ * Callback to free a \c struct \c addrinfo, suitable for use with
+ * \c gc_addspecial().
+ *
+ * @param addr Pointer to the \c struct \c addrinfo to free.
+ */
static inline void
gc_freeaddrinfo_callback(void *addr)
{
freeaddrinfo((struct addrinfo *)addr);
}
-/** Return an empty struct buffer */
+/** Return an empty, undefined \c struct \c buffer (all fields zero). */
static inline struct buffer
clear_buf(void)
{
return (struct buffer){ 0 };
}
+/**
+ * Return true iff \c buf has a non-NULL data pointer.
+ *
+ * A defined buffer has been allocated but may have zero length or negative
+ * len (i.e. it is not necessarily valid).
+ *
+ * @param buf The buffer to test.
+ */
static inline bool
buf_defined(const struct buffer *buf)
{
return buf->data != NULL;
}
+/**
+ * Return true iff \c buf is valid.
+ *
+ * A buffer is valid when its data pointer is non-NULL and its \c len is
+ * non-negative.
+ *
+ * @param buf The buffer to test.
+ */
static inline bool
buf_valid(const struct buffer *buf)
{
return likely(buf->data != NULL) && likely(buf->len >= 0);
}
+/**
+ * Return a pointer to the start of the buffer content.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return Pointer to \c buf->data + \c buf->offset, or NULL if \c buf is
+ * not valid.
+ */
static inline uint8_t *
buf_bptr(const struct buffer *buf)
{
@@ -223,6 +427,13 @@
}
}
+/**
+ * Return the length of the buffer content.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return \c buf->len if \c buf is valid, otherwise 0.
+ */
static int
buf_len(const struct buffer *buf)
{
@@ -236,12 +447,27 @@
}
}
+/**
+ * Return a pointer one past the end of the buffer content.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return Pointer to the byte immediately after the last content byte.
+ */
static inline uint8_t *
buf_bend(const struct buffer *buf)
{
return buf_bptr(buf) + buf_len(buf);
}
+/**
+ * Return a pointer to the last byte of the buffer content.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return Pointer to the last byte, or NULL if the buffer is empty or
+ * invalid.
+ */
static inline uint8_t *
buf_blast(const struct buffer *buf)
{
@@ -255,24 +481,56 @@
}
}
+/**
+ * Return true iff \c size is within the allowed buffer size range.
+ *
+ * @param size The size to check.
+ *
+ * @return true if \c size < \c BUF_SIZE_MAX.
+ */
static inline bool
buf_size_valid(const size_t size)
{
return likely(size < BUF_SIZE_MAX);
}
+/**
+ * Return true iff a signed \c size is within the allowed buffer size range.
+ *
+ * Accepts negative values (used for bidirectional length adjustments) as
+ * long as the absolute value is below \c BUF_SIZE_MAX.
+ *
+ * @param size The signed size to check.
+ *
+ * @return true if \c -BUF_SIZE_MAX <= size < \c BUF_SIZE_MAX.
+ */
static inline bool
buf_size_valid_signed(const int size)
{
return likely(size >= -BUF_SIZE_MAX) && likely(size < BUF_SIZE_MAX);
}
+/**
+ * Return the buffer content pointer cast to \c char *.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return The content pointer as a \c char *, or NULL if \c buf is invalid.
+ */
static inline char *
buf_str(const struct buffer *buf)
{
return (char *)buf_bptr(buf);
}
+/**
+ * Reset a buffer to an undefined (unallocated) state.
+ *
+ * Sets all fields to zero/NULL without freeing any memory. Use \c free_buf()
+ * first if the buffer owns allocated memory.
+ *
+ * @param buf The buffer to reset.
+ */
static inline void
buf_reset(struct buffer *buf)
{
@@ -282,6 +540,14 @@
buf->data = NULL;
}
+/**
+ * Reset the length and offset of a buffer to zero.
+ *
+ * The underlying allocation is preserved and \c capacity is unchanged.
+ * Equivalent to rewinding the buffer to the beginning for fresh writing.
+ *
+ * @param buf The buffer to reset.
+ */
static inline void
buf_reset_len(struct buffer *buf)
{
@@ -289,6 +555,18 @@
buf->offset = 0;
}
+/**
+ * Initialise a buffer with a given initial offset.
+ *
+ * Sets \c buf->len to zero and \c buf->offset to \c offset. The buffer must
+ * already be allocated. Returns false (without modifying \c buf) if \c offset
+ * is negative, exceeds the buffer capacity, or the data pointer is NULL.
+ *
+ * @param buf The buffer to initialise.
+ * @param offset Initial byte offset from the start of the allocated memory.
+ *
+ * @return true on success, false if the parameters are invalid.
+ */
static inline bool
buf_init_dowork(struct buffer *buf, int offset)
{
@@ -301,6 +579,17 @@
return true;
}
+/**
+ * Initialise a buffer with an externally provided writable memory region.
+ *
+ * Sets up \c buf to use \c data as its backing store for writing. The first
+ * byte of \c data is set to zero. Calls \c buf_size_error() (which does not
+ * return) if \c size exceeds \c BUF_SIZE_MAX.
+ *
+ * @param buf The buffer to initialise.
+ * @param data Pointer to the memory region to use.
+ * @param size Size of the memory region in bytes.
+ */
static inline void
buf_set_write(struct buffer *buf, uint8_t *data, int size)
{
@@ -318,6 +607,18 @@
}
}
+/**
+ * Initialise a buffer with an externally provided read-only memory region.
+ *
+ * Sets up \c buf so that the entire \c data region is the buffer content
+ * (offset is 0, len and capacity are both set to \c size). The data pointer
+ * is cast away from const. Calls \c buf_size_error() (which does not return)
+ * if \c size exceeds \c BUF_SIZE_MAX.
+ *
+ * @param buf The buffer to initialise.
+ * @param data Pointer to the read-only memory region.
+ * @param size Size of the memory region in bytes.
+ */
static inline void
buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
{
@@ -330,7 +631,17 @@
buf->data = (uint8_t *)data;
}
-/* Like strncpy but makes sure dest is always null terminated */
+/**
+ * Like \c strncpy() but always null-terminates the destination.
+ *
+ * Copies at most \c maxlen - 1 characters from \c src into \c dest and
+ * writes a null terminator at \c dest[\c maxlen - 1]. Does nothing if
+ * \c maxlen is zero.
+ *
+ * @param dest Destination buffer.
+ * @param src Source string.
+ * @param maxlen Size of the destination buffer in bytes.
+ */
static inline void
strncpynt(char *dest, const char *src, size_t maxlen)
{
@@ -341,7 +652,13 @@
}
}
-/* return true if string contains at least one numerical digit */
+/**
+ * Return true if the string contains at least one decimal digit.
+ *
+ * @param src The null-terminated string to scan.
+ *
+ * @return true if any character in \c src satisfies \c isdigit().
+ */
static inline bool
has_digit(const char *src)
{
@@ -401,11 +718,20 @@
#endif
}
-/*
- * printf append to a buffer with overflow check,
- * due to usage of vsnprintf, it will leave space for
- * a final null character and thus use only
- * capacity - 1
+/**
+ * printf-style append to a buffer with overflow check.
+ *
+ * Formats a string and appends it to \c buf. Due to the use of
+ * \c vsnprintf(), one byte of forward capacity is reserved for a null
+ * terminator, so at most \c buf_forward_capacity(buf) - 1 bytes of
+ * formatted output are written.
+ *
+ * @param buf The buffer to append to.
+ * @param format printf-style format string.
+ * @param ... Format arguments.
+ *
+ * @return true if the entire formatted string fit in the buffer, false if
+ * it was truncated.
*/
bool buf_printf(struct buffer *buf, const char *format, ...)
#ifdef __GNUC__
@@ -417,8 +743,13 @@
#endif
;
-/*
- * puts append to a buffer with overflow check
+/**
+ * Append a string to a buffer with overflow check.
+ *
+ * @param buf The buffer to append to.
+ * @param str The null-terminated string to append.
+ *
+ * @return true if the string fit in the buffer, false if it was truncated.
*/
bool buf_puts(struct buffer *buf, const char *str);
@@ -427,22 +758,76 @@
* remove/add trailing characters
*/
+/**
+ * Force a null terminator at the end of the buffer content.
+ *
+ * If there is free capacity, a '\\0' byte is appended. If the buffer is
+ * full, the last content byte is overwritten with '\\0' (i.e. the last
+ * character is truncated to make room). The function has no effect on an
+ * empty or invalid buffer.
+ *
+ * @param buf The buffer to null-terminate.
+ */
void buf_null_terminate(struct buffer *buf);
+/**
+ * Remove trailing newline and carriage-return characters from a buffer.
+ *
+ * @param buf The buffer to chomp.
+ */
void buf_chomp(struct buffer *buf);
+/**
+ * Remove all occurrences of a specific byte from the end of a buffer.
+ *
+ * @param buf The buffer to modify.
+ * @param remove The byte value to strip from the tail.
+ */
void buf_rmtail(struct buffer *buf, uint8_t remove);
-/*
- * non-buffer string functions
+/** @name String Utility Functions
+ * @brief Non-buffer string functions
+ */
+/**@{*/
+
+/**
+ * Remove trailing newline and carriage-return characters from a string.
+ *
+ * @param str The null-terminated string to chomp.
*/
void chomp(char *str);
+/**
+ * Remove all trailing characters that appear in a given set.
+ *
+ * @param str The null-terminated string to modify in place.
+ * @param what_to_delete Null-terminated set of characters to strip.
+ */
void rm_trailing_chars(char *str, const char *what_to_delete);
+/**
+ * Return a pointer past any leading whitespace in a string.
+ *
+ * @param str The string to skip whitespace in.
+ *
+ * @return Pointer to the first non-whitespace character, or to the null
+ * terminator if \c str is all whitespace.
+ */
const char *skip_leading_whitespace(const char *str);
+/**
+ * Null-terminate a fixed-length string buffer.
+ *
+ * Ensures that \c str[\c len] is '\\0', clamping \c len to \c capacity - 1
+ * if necessary.
+ *
+ * @param str The character buffer to null-terminate.
+ * @param len Current string length.
+ * @param capacity Total size of the character buffer in bytes.
+ */
void string_null_terminate(char *str, int len, int capacity);
+/**@}*/
+/* End of string utility functions */
/**
* Write buffer contents to file.
@@ -454,40 +839,114 @@
*/
bool buffer_write_file(const char *filename, const struct buffer *buf);
-/*
- * write a string to the end of a buffer that was
- * truncated by buf_printf
+/**
+ * Append a string to the physical end of a buffer that was truncated by
+ * \c buf_printf().
+ *
+ * If the buffer's forward capacity is one byte or less (i.e. it is full),
+ * \c str is written at the very end of the allocated memory to act as a
+ * truncation marker, provided it fits within the total buffer capacity.
+ * This is used to append a "[more...]" suffix after hex-dump truncation.
+ *
+ * @param buf The buffer to append the truncation marker to.
+ * @param str The null-terminated marker string to write.
*/
void buf_catrunc(struct buffer *buf, const char *str);
-/*
- * Parse a string based on a given delimiter char
+/**
+ * Extract the next token from a buffer, delimited by a given character.
+ *
+ * Reads bytes from \c buf one at a time until \c delim or end-of-buffer is
+ * encountered. The token (without the delimiter) is written into \c line,
+ * which is always null-terminated. The delimiter byte is consumed from
+ * \c buf but not included in \c line.
+ *
+ * @param buf Source buffer to parse. The consumed portion is advanced.
+ * @param delim Delimiter character.
+ * @param line Output buffer for the extracted token.
+ * @param size Size of \c line in bytes (must be > 0).
+ *
+ * @return false only when end-of-buffer is reached and no characters were
+ * extracted; true otherwise (including when a delimiter was found
+ * or \c line was truncated).
*/
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size);
-/*
- * Hex dump -- Output a binary buffer to a hex string and return it.
+/** @name Hex Dump
+ * @brief Output a binary buffer to a hex string and return it.
*/
-#define FHE_SPACE_BREAK_MASK 0xFF /* space_break parameter in lower 8 bits */
-#define FHE_CAPS 0x100 /* output hex in caps */
+/**@{*/
+
+/** Mask for the space_break_flags field of \c format_hex_ex(): number of
+ * bytes between separators (lower 8 bits). */
+#define FHE_SPACE_BREAK_MASK 0xFF
+/** Flag for \c format_hex_ex(): output hex digits in upper case. */
+#define FHE_CAPS 0x100
+/**
+ * Format a binary buffer as a hex string.
+ *
+ * @param data Pointer to the binary data to format.
+ * @param size Number of bytes to format.
+ * @param maxoutput Maximum number of output characters (0 = unlimited).
+ * If the output is truncated, a "[more...]" suffix is
+ * appended via \c buf_catrunc().
+ * @param space_break_flags Lower 8 bits (\c FHE_SPACE_BREAK_MASK) give the
+ * number of bytes between \c separator insertions.
+ * Use \c FHE_CAPS to output upper-case hex digits.
+ * @param separator String to insert between groups of bytes.
+ * @param gc Garbage collection arena for the returned string.
+ *
+ * @return Null-terminated hex string allocated from \c gc.
+ */
char *format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags,
const char *separator, struct gc_arena *gc);
+/**
+ * Format a binary buffer as a hex string with spaces every 4 bytes.
+ *
+ * Convenience wrapper around \c format_hex_ex().
+ *
+ * @param data Pointer to the binary data to format.
+ * @param size Number of bytes to format.
+ * @param maxoutput Maximum number of output characters (0 = unlimited).
+ * @param gc Garbage collection arena for the returned string.
+ *
+ * @return Null-terminated hex string allocated from \c gc.
+ */
static inline char *
format_hex(const uint8_t *data, size_t size, size_t maxoutput, struct gc_arena *gc)
{
return format_hex_ex(data, size, maxoutput, 4, " ", gc);
}
+/**@}*/
+/* End of Hex Dump */
-/*
- * Return a buffer that is a subset of another buffer.
+/**
+ * Return a sub-buffer of another buffer.
+ *
+ * Either allocates \c size bytes from the front or back of \c buf,
+ * shrinking \c buf accordingly, and returns a buffer pointing into the
+ * same memory.
+ *
+ * @param buf Source buffer to carve the sub-buffer from.
+ * @param size Number of bytes to include in the sub-buffer.
+ * @param prepend If true, take bytes from the front of \c buf (prepend
+ * space); if false, take bytes from the end (append space).
+ *
+ * @return A buffer referencing the allocated region, or an undefined buffer
+ * if \c buf has insufficient capacity.
*/
struct buffer buf_sub(struct buffer *buf, int size, bool prepend);
-/*
- * Check if sufficient space to append to buffer.
+/**
+ * Check whether \c len bytes can be appended to a buffer.
+ *
+ * @param buf The buffer to check.
+ * @param len Number of bytes to append.
+ *
+ * @return true if \c buf is valid, \c len is within the allowed range, and
+ * there is sufficient capacity after the current content.
*/
-
static inline bool
buf_safe(const struct buffer *buf, size_t len)
{
@@ -495,6 +954,18 @@
&& buf->offset + buf->len + (int)len <= buf->capacity;
}
+/**
+ * Check whether \c len bytes can be added to or removed from a buffer.
+ *
+ * Accepts negative \c len to verify that bytes can be removed (length
+ * decreased).
+ *
+ * @param buf The buffer to check.
+ * @param len Number of bytes to add (positive) or remove (negative).
+ *
+ * @return true if the resulting length would remain non-negative and within
+ * the allocated capacity.
+ */
static inline bool
buf_safe_bidir(const struct buffer *buf, int len)
{
@@ -509,6 +980,17 @@
}
}
+/**
+ * Return the number of bytes that can still be appended to the buffer.
+ *
+ * This is the space between the end of the current content and the end of
+ * the allocated memory.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return Number of bytes available for appending, or 0 if \c buf is
+ * invalid.
+ */
static inline int
buf_forward_capacity(const struct buffer *buf)
{
@@ -527,6 +1009,17 @@
}
}
+/**
+ * Return the total number of bytes available from the current offset to the
+ * end of the allocated memory.
+ *
+ * Unlike \c buf_forward_capacity(), this includes the bytes already occupied
+ * by the current content.
+ *
+ * @param buf The buffer to query.
+ *
+ * @return Capacity minus offset, or 0 if \c buf is invalid.
+ */
static inline int
buf_forward_capacity_total(const struct buffer *buf)
{
@@ -545,6 +1038,16 @@
}
}
+/**
+ * Return the number of bytes available for prepending to the buffer.
+ *
+ * This is the number of bytes between the start of the allocated memory and
+ * the current content start (i.e. \c buf->offset).
+ *
+ * @param buf The buffer to query.
+ *
+ * @return \c buf->offset, or 0 if \c buf is invalid.
+ */
static inline int
buf_reverse_capacity(const struct buffer *buf)
{
@@ -558,6 +1061,17 @@
}
}
+/**
+ * Increase or decrease the length of a buffer.
+ *
+ * Adjusts \c buf->len by \c inc bytes. A negative \c inc shrinks the
+ * buffer. The operation is bounds-checked via \c buf_safe_bidir().
+ *
+ * @param buf The buffer to modify.
+ * @param inc Number of bytes to add to the length (may be negative).
+ *
+ * @return true on success, false if the adjustment would violate bounds.
+ */
static inline bool
buf_inc_len(struct buffer *buf, int inc)
{
@@ -569,11 +1083,18 @@
return true;
}
-/*
- * Make space to prepend to a buffer.
- * Return NULL if no space.
+/**
+ * Make space at the front of a buffer for prepending data.
+ *
+ * Moves the content start backwards by \c size bytes, increasing \c len
+ * and decreasing \c offset by the same amount.
+ *
+ * @param buf The buffer to modify.
+ * @param size Number of bytes to reserve for prepending.
+ *
+ * @return Pointer to the newly reserved space (new content start), or NULL
+ * if \c buf is invalid or there is insufficient prepend capacity.
*/
-
static inline uint8_t *
buf_prepend(struct buffer *buf, ssize_t size)
{
@@ -586,6 +1107,17 @@
return BPTR(buf);
}
+/**
+ * Advance the content start of a buffer, consuming bytes from the front.
+ *
+ * Increases \c offset and decreases \c len by \c size bytes.
+ *
+ * @param buf The buffer to advance.
+ * @param size Number of bytes to skip.
+ *
+ * @return true on success, false if \c buf is invalid or \c size exceeds
+ * the current length.
+ */
static inline bool
buf_advance(struct buffer *buf, ssize_t size)
{
@@ -598,11 +1130,18 @@
return true;
}
-/*
- * Return a pointer to allocated space inside a buffer.
- * Return NULL if no space.
+/**
+ * Reserve space at the end of a buffer for writing.
+ *
+ * Increases \c buf->len by \c size and returns a pointer to the start of
+ * the newly reserved region.
+ *
+ * @param buf The buffer to allocate space in.
+ * @param size Number of bytes to reserve.
+ *
+ * @return Pointer to the start of the reserved space, or NULL if there is
+ * insufficient capacity.
*/
-
static inline uint8_t *
buf_write_alloc(struct buffer *buf, size_t size)
{
@@ -616,6 +1155,18 @@
return ret;
}
+/**
+ * Consume bytes from the front of a buffer for reading.
+ *
+ * Returns a pointer to the current content start and advances the buffer
+ * by \c size bytes.
+ *
+ * @param buf The buffer to read from.
+ * @param size Number of bytes to consume.
+ *
+ * @return Pointer to the start of the consumed region, or NULL if \c size
+ * is negative or exceeds the current length.
+ */
static inline uint8_t *
buf_read_alloc(struct buffer *buf, int size)
{
@@ -630,6 +1181,17 @@
return ret;
}
+/**
+ * Append data to a buffer.
+ *
+ * Copies \c size bytes from \c src to the end of \c dest.
+ *
+ * @param dest Destination buffer.
+ * @param src Data to append.
+ * @param size Number of bytes to append.
+ *
+ * @return true on success, false if there is insufficient capacity.
+ */
static inline bool
buf_write(struct buffer *dest, const void *src, size_t size)
{
@@ -642,6 +1204,18 @@
return true;
}
+/**
+ * Prepend data to a buffer.
+ *
+ * Copies \c size bytes from \c src into the prepend space at the front of
+ * \c dest.
+ *
+ * @param dest Destination buffer.
+ * @param src Data to prepend.
+ * @param size Number of bytes to prepend.
+ *
+ * @return true on success, false if there is insufficient prepend capacity.
+ */
static inline bool
buf_write_prepend(struct buffer *dest, const void *src, int size)
{
@@ -654,12 +1228,28 @@
return true;
}
+/**
+ * Append a uint8_t to a buffer.
+ *
+ * @param dest Destination buffer.
+ * @param data Byte to append.
+ *
+ * @return true on success, false if there is insufficient capacity.
+ */
static inline bool
buf_write_u8(struct buffer *dest, uint8_t data)
{
return buf_write(dest, &data, sizeof(uint8_t));
}
+/**
+ * Append a uint16_t to a buffer in network byte order.
+ *
+ * @param dest Destination buffer.
+ * @param data Value to append (converted with \c htons()).
+ *
+ * @return true on success, false if there is insufficient capacity.
+ */
static inline bool
buf_write_u16(struct buffer *dest, uint16_t data)
{
@@ -667,6 +1257,14 @@
return buf_write(dest, &u16, sizeof(uint16_t));
}
+/**
+ * Append a uint32_t to a buffer in network byte order.
+ *
+ * @param dest Destination buffer.
+ * @param data Value to append (converted with \c htonl()).
+ *
+ * @return true on success, false if there is insufficient capacity.
+ */
static inline bool
buf_write_u32(struct buffer *dest, uint32_t data)
{
@@ -674,6 +1272,14 @@
return buf_write(dest, &u32, sizeof(uint32_t));
}
+/**
+ * Append a uint64_t to a buffer in network byte order.
+ *
+ * @param dest Destination buffer.
+ * @param data Value to append (converted with \c htonll()).
+ *
+ * @return true on success, false if there is insufficient capacity.
+ */
static inline bool
buf_write_u64(struct buffer *dest, uint64_t data)
{
@@ -681,12 +1287,32 @@
return buf_write(dest, &u64, sizeof(uint64_t));
}
+/**
+ * Copy the content of one buffer to the end of another.
+ *
+ * @param dest Destination buffer.
+ * @param src Source buffer.
+ *
+ * @return true on success, false if \c dest has insufficient capacity.
+ */
static inline bool
buf_copy(struct buffer *dest, const struct buffer *src)
{
return buf_write(dest, BPTR(src), BLENZ(src));
}
+/**
+ * Read \c n bytes from \c src and append them to \c dest.
+ *
+ * Consumes the bytes from \c src.
+ *
+ * @param dest Destination buffer.
+ * @param src Source buffer (modified).
+ * @param n Number of bytes to copy.
+ *
+ * @return true on success, false if \c src has fewer than \c n bytes or
+ * \c dest has insufficient capacity.
+ */
static inline bool
buf_copy_n(struct buffer *dest, struct buffer *src, int n)
{
@@ -698,6 +1324,22 @@
return buf_write(dest, cp, n);
}
+/**
+ * Copy a range of bytes from one buffer into a specific position in another.
+ *
+ * Copies \c src_len bytes starting at \c src_index within \c src content
+ * into \c dest at \c dest_index relative to the content start. Updates
+ * \c dest->len if the write extends beyond the current content. Does not
+ * advance either buffer's offset.
+ *
+ * @param dest Destination buffer.
+ * @param dest_index Byte offset within \c dest content to write at.
+ * @param src Source buffer.
+ * @param src_index Byte offset within \c src content to read from.
+ * @param src_len Number of bytes to copy.
+ *
+ * @return true on success, false if any index or length is out of range.
+ */
static inline bool
buf_copy_range(struct buffer *dest, int dest_index, const struct buffer *src, int src_index,
int src_len)
@@ -715,7 +1357,19 @@
return true;
}
-/* truncate src to len, copy excess data beyond len to dest */
+/**
+ * Truncate \c src to \c len bytes and copy any excess to \c dest.
+ *
+ * If \c src->len > \c len, \c src is truncated to \c len bytes and the
+ * remaining bytes are appended to \c dest. If \c src->len <= \c len,
+ * nothing is copied and true is returned.
+ *
+ * @param dest Destination buffer for the excess data.
+ * @param src Source buffer to truncate.
+ * @param len Maximum number of bytes to keep in \c src.
+ *
+ * @return true on success, false if \c len is negative or copying fails.
+ */
static inline bool
buf_copy_excess(struct buffer *dest, struct buffer *src, int len)
{
@@ -739,6 +1393,17 @@
}
}
+/**
+ * Read bytes from the front of a buffer into a caller-supplied destination.
+ *
+ * Consumes \c size bytes from \c src and copies them to \c dest.
+ *
+ * @param src Source buffer (modified).
+ * @param dest Destination memory.
+ * @param size Number of bytes to read.
+ *
+ * @return true on success, false if \c src has fewer than \c size bytes.
+ */
static inline bool
buf_read(struct buffer *src, void *dest, int size)
{
@@ -751,6 +1416,14 @@
return true;
}
+/**
+ * Return the first byte of the buffer without consuming it.
+ *
+ * @param buf The buffer to peek at.
+ *
+ * @return The first byte as an unsigned value (0–255), or -1 if the buffer
+ * is empty.
+ */
static inline int
buf_peek_u8(struct buffer *buf)
{
@@ -763,6 +1436,13 @@
return ret;
}
+/**
+ * Read and consume a uint8_t from the front of a buffer.
+ *
+ * @param buf The buffer to read from.
+ *
+ * @return The byte value (0–255), or -1 if the buffer is empty.
+ */
static inline int
buf_read_u8(struct buffer *buf)
{
@@ -774,6 +1454,16 @@
return ret;
}
+/**
+ * Read and consume a uint16_t from the front of a buffer.
+ *
+ * The value is converted from network byte order via \c ntohs().
+ *
+ * @param buf The buffer to read from.
+ *
+ * @return The host-byte-order value, or -1 if the buffer has fewer than 2
+ * bytes.
+ */
static inline int
buf_read_u16(struct buffer *buf)
{
@@ -785,6 +1475,17 @@
return ntohs(ret);
}
+/**
+ * Read and consume a uint32_t from the front of a buffer.
+ *
+ * The value is converted from network byte order via \c ntohl().
+ *
+ * @param buf The buffer to read from.
+ * @param good If non-NULL, set to true on success or false on failure.
+ *
+ * @return The host-byte-order value, or 0 if the buffer has fewer than 4
+ * bytes (check \c *good to distinguish from a legitimate zero).
+ */
static inline uint32_t
buf_read_u32(struct buffer *buf, bool *good)
{
@@ -807,8 +1508,17 @@
}
}
-/* Read a 64-bit big-endian value (see buf_write_u64()). Sets *good to indicate
- * success, like buf_read_u32(). */
+/**
+ * Read and consume a uint64_t from the front of a buffer.
+ *
+ * The value is converted from network byte order via \c ntohll().
+ *
+ * @param buf The buffer to read from.
+ * @param good If non-NULL, set to true on success or false on failure.
+ *
+ * @return The host-byte-order value, or 0 if the buffer has fewer than 8
+ * bytes (check \c *good to distinguish from a legitimate zero).
+ */
static inline uint64_t
buf_read_u64(struct buffer *buf, bool *good)
{
@@ -866,31 +1576,73 @@
return memcmp(BPTR(src), match, size) == 0;
}
+/**
+ * Return true if the head of \c src matches the string \c match.
+ *
+ * Compares the first \c strlen(match) bytes of \c src content with \c match.
+ * *NOT* constant time. Do not use when comparing HMACs.
+ *
+ * @param src Buffer whose head is compared.
+ * @param match Null-terminated string to compare against.
+ *
+ * @return true if the buffer starts with \c match.
+ */
bool buf_string_match_head_str(const struct buffer *src, const char *match);
+/**
+ * Compare the head of \c src with \c match and advance past it if equal.
+ *
+ * If the buffer starts with \c match, the matched bytes are consumed from
+ * \c src. *NOT* constant time. Do not use when comparing HMACs.
+ *
+ * @param src Buffer to compare and advance.
+ * @param match Null-terminated string to compare against.
+ *
+ * @return true if the buffer started with \c match (and was advanced).
+ */
bool buf_string_compare_advance(struct buffer *src, const char *match);
+/**
+ * Return the length of the next token in a buffer up to a delimiter.
+ *
+ * Scans the buffer content for \c delim and returns the number of bytes
+ * up to (but not including) the delimiter, or the full buffer length if
+ * the delimiter is not found.
+ *
+ * @param buf The buffer to scan.
+ * @param delim Delimiter byte to search for.
+ *
+ * @return Number of bytes before the first occurrence of \c delim, or -1
+ * if the buffer is empty.
+ */
int buf_substring_len(const struct buffer *buf, int delim);
-/*
- * Print a string which might be NULL
+/**
+ * Return a printable representation of a string that might be NULL.
+ *
+ * @param str String to print, or NULL.
+ *
+ * @return \c str if non-NULL, otherwise the string \c "[NULL]".
*/
const char *np(const char *str);
-/* character classes */
-
+/** @name Character classes
+ * @brief Check or modify strings based on classes of allowed
+ * or forbidden characters.
+ */
+/**@{*/
#define CC_ANY (1 << 0) /**< any character */
#define CC_NULL (1 << 1) /**< null character \0 */
-#define CC_ALNUM (1 << 2) /**< alphanumeric isalnum() */
-#define CC_ALPHA (1 << 3) /**< alphabetic isalpha() */
+#define CC_ALNUM (1 << 2) /**< alphanumeric \c isalnum() */
+#define CC_ALPHA (1 << 3) /**< alphabetic \c isalpha() */
#define CC_ASCII (1 << 4) /**< ASCII character */
-#define CC_CNTRL (1 << 5) /**< control character iscntrl() */
-#define CC_DIGIT (1 << 6) /**< digit isdigit() */
+#define CC_CNTRL (1 << 5) /**< control character \c iscntrl() */
+#define CC_DIGIT (1 << 6) /**< digit \c isdigit() */
#define CC_PRINT (1 << 7) /**< printable (>= 32, != 127) */
-#define CC_PUNCT (1 << 8) /**< punctuation ispunct() */
-#define CC_SPACE (1 << 9) /**< whitespace isspace() */
-#define CC_XDIGIT (1 << 10) /**< hex digit isxdigit() */
+#define CC_PUNCT (1 << 8) /**< punctuation \c ispunct() */
+#define CC_SPACE (1 << 9) /**< whitespace \c isspace() */
+#define CC_XDIGIT (1 << 10) /**< hex digit \c isxdigit() */
#define CC_BLANK (1 << 11) /**< space or tab */
#define CC_NEWLINE (1 << 12) /**< newline */
@@ -918,8 +1670,27 @@
#define CC_NAME (CC_ALNUM | CC_UNDERBAR) /**< alphanumeric plus underscore */
#define CC_CRLF (CC_CR | CC_NEWLINE) /**< carriage return or newline */
+/**
+ * Test whether a character belongs to one or more character classes.
+ *
+ * @param c The character to test.
+ * @param flags Bitmask of \c CC_* character class flags.
+ *
+ * @return true if \c c matches any of the classes specified in \c flags.
+ */
bool char_class(const unsigned char c, const unsigned int flags);
+/**
+ * Test whether all characters in a string satisfy a character class filter.
+ *
+ * @param str The null-terminated string to test.
+ * @param inclusive Classes that are permitted (characters must match at
+ * least one of these).
+ * @param exclusive Classes that are forbidden (characters must not match
+ * any of these, even if they also match \c inclusive).
+ *
+ * @return true if every character in \c str is permitted.
+ */
bool string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive);
/**
@@ -966,10 +1737,27 @@
*/
const char *string_mod_const(const char *str, const unsigned int inclusive,
const unsigned int exclusive, const char replace, struct gc_arena *gc);
+/**@}*/
+/**
+ * Replace all leading occurrences of a character in a string.
+ *
+ * Scans from the start of \c str and replaces every consecutive \c match
+ * character with \c replace, stopping at the first character that does not
+ * match.
+ *
+ * @param str The null-terminated string to modify in place.
+ * @param match The character to replace.
+ * @param replace The replacement character.
+ */
void string_replace_leading(char *str, const char match, const char replace);
-/** Return true iff str starts with prefix */
+/**
+ * Return true iff \c str starts with \c prefix.
+ *
+ * @param str The string to test.
+ * @param prefix The prefix to look for.
+ */
static inline bool
strprefix(const char *str, const char *prefix)
{
@@ -1004,6 +1792,16 @@
* Verify that a pointer is correctly aligned
*/
#ifdef VERIFY_ALIGNMENT
+/**
+ * Assert that the content pointer of \c buf is 4-byte aligned.
+ *
+ * Only compiled in when \c VERIFY_ALIGNMENT is defined. Called via the
+ * \c verify_align_4() macro.
+ *
+ * @param buf Buffer whose content pointer is to be checked.
+ * @param file Source file of the call site (for the error message).
+ * @param line Source line of the call site (for the error message).
+ */
void valign4(const struct buffer *buf, const char *file, const int line);
#define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__)
@@ -1011,23 +1809,58 @@
#define verify_align_4(ptr)
#endif
-/*
- * Very basic garbage collection, mostly for routines that return
- * char ptrs to malloced strings.
+/** @name Garbage Collection
+ * @brief Basic garbage collection, mostly for routines that return
+ * char ptrs to malloced strings.
*/
+/**@{*/
+/**
+ * Move all allocations from one garbage collection arena to another.
+ *
+ * After the call \c src is empty and all entries previously in \c src are
+ * owned by \c dest.
+ *
+ * @param dest Arena to move allocations into.
+ * @param src Arena to move allocations from (emptied on return).
+ */
void gc_transfer(struct gc_arena *dest, struct gc_arena *src);
+/**
+ * Free all plain allocations in a garbage collection arena.
+ *
+ * Internal implementation called by \c gc_free(). Do not call directly.
+ *
+ * @param a The arena whose \c list entries are to be freed.
+ */
void x_gc_free(struct gc_arena *a);
+/**
+ * Free all specially-allocated entries in a garbage collection arena.
+ *
+ * Internal implementation called by \c gc_free(). Do not call directly.
+ * Invokes the custom free function stored in each \c gc_entry_special.
+ *
+ * @param a The arena whose \c list_special entries are to be freed.
+ */
void x_gc_freespecial(struct gc_arena *a);
+/**
+ * Return true iff the arena contains at least one allocation.
+ *
+ * @param a The arena to test.
+ */
static inline bool
gc_defined(struct gc_arena *a)
{
return a->list != NULL;
}
+/**
+ * Initialise a garbage collection arena to an empty state.
+ *
+ * @param a The arena to initialise.
+ */
static inline void
gc_init(struct gc_arena *a)
{
@@ -1035,12 +1868,25 @@
a->list_special = NULL;
}
+/**
+ * Detach all allocations from an arena without freeing them.
+ *
+ * After this call the arena is empty. The caller takes responsibility for
+ * freeing the previously registered allocations.
+ *
+ * @param a The arena to detach.
+ */
static inline void
gc_detach(struct gc_arena *a)
{
gc_init(a);
}
+/**
+ * Allocate and return a new, empty garbage collection arena.
+ *
+ * @return An initialised \c gc_arena with empty lists.
+ */
static inline struct gc_arena
gc_new(void)
{
@@ -1049,6 +1895,14 @@
return ret;
}
+/**
+ * Free all allocations in a garbage collection arena.
+ *
+ * Calls \c x_gc_free() and \c x_gc_freespecial() as needed to release all
+ * registered memory.
+ *
+ * @param a The arena to free.
+ */
static inline void
gc_free(struct gc_arena *a)
{
@@ -1062,6 +1916,13 @@
}
}
+/**
+ * Free all allocations in a garbage collection arena and reinitialise it.
+ *
+ * Equivalent to calling \c gc_free() followed by \c gc_init().
+ *
+ * @param a The arena to reset.
+ */
static inline void
gc_reset(struct gc_arena *a)
{
@@ -1079,58 +1940,139 @@
/* 1 GB on 32bit systems, they usually can only allocate 2 GB for the
* whole process.
*/
+/** Maximum size for a single array allocation (checked by \c array_mult_safe()). */
#define ALLOC_SIZE_MAX (1u << 30)
#else
+/** Maximum size for a single array allocation (checked by \c array_mult_safe()). */
#define ALLOC_SIZE_MAX ((size_t)1 << 32) /* 4 GB */
#endif
+/**
+ * Allocate memory for a single object of the given type.
+ *
+ * Calls \c check_malloc_return() to abort on allocation failure.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Type of the object to allocate.
+ */
#define ALLOC_OBJ(dptr, type) \
{ \
check_malloc_return((dptr) = (type *)malloc(sizeof(type))); \
}
+/**
+ * Allocate and zero-initialise memory for a single object of the given type.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Type of the object to allocate.
+ */
#define ALLOC_OBJ_CLEAR(dptr, type) \
{ \
ALLOC_OBJ(dptr, type); \
memset((dptr), 0, sizeof(type)); \
}
+/**
+ * Allocate memory for an array of \c n elements of the given type.
+ *
+ * Uses \c array_mult_safe() to guard against size overflow.
+ * Calls \c check_malloc_return() to abort on allocation failure.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Element type of the array.
+ * @param n Number of elements.
+ */
#define ALLOC_ARRAY(dptr, type, n) \
{ \
check_malloc_return((dptr) = (type *)malloc(array_mult_safe(sizeof(type), (n), 0))); \
}
+/**
+ * Allocate a garbage-collected array of \c n elements of the given type.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Element type of the array.
+ * @param n Number of elements.
+ * @param gc Garbage collection arena to register the allocation with.
+ */
#define ALLOC_ARRAY_GC(dptr, type, n, gc) \
{ \
(dptr) = (type *)gc_malloc(array_mult_safe(sizeof(type), (n), 0), false, (gc)); \
}
+/**
+ * Allocate and zero-initialise an array of \c n elements of the given type.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Element type of the array.
+ * @param n Number of elements.
+ */
#define ALLOC_ARRAY_CLEAR(dptr, type, n) \
{ \
ALLOC_ARRAY(dptr, type, n); \
memset((dptr), 0, (array_mult_safe(sizeof(type), (n), 0))); \
}
+/**
+ * Allocate and zero-initialise a garbage-collected array of \c n elements.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Element type of the array.
+ * @param n Number of elements.
+ * @param gc Garbage collection arena to register the allocation with.
+ */
#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \
{ \
(dptr) = (type *)gc_malloc(array_mult_safe(sizeof(type), (n), 0), true, (gc)); \
}
+/**
+ * Allocate and zero-initialise a garbage-collected variable-length structure.
+ *
+ * Allocates \c sizeof(type) + \c n * \c sizeof(atype) bytes for a structure
+ * that embeds a variable-length array of \c atype as its last member.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Type of the enclosing structure.
+ * @param atype Element type of the variable-length array member.
+ * @param n Number of array elements.
+ * @param gc Garbage collection arena to register the allocation with.
+ */
#define ALLOC_VAR_ARRAY_CLEAR_GC(dptr, type, atype, n, gc) \
{ \
(dptr) = (type *)gc_malloc(array_mult_safe(sizeof(atype), (n), sizeof(type)), true, (gc)); \
}
+/**
+ * Allocate a garbage-collected object of the given type (uninitialised).
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Type of the object to allocate.
+ * @param gc Garbage collection arena to register the allocation with.
+ */
#define ALLOC_OBJ_GC(dptr, type, gc) \
{ \
(dptr) = (type *)gc_malloc(sizeof(type), false, (gc)); \
}
+/**
+ * Allocate and zero-initialise a garbage-collected object of the given type.
+ *
+ * @param dptr Pointer variable that receives the allocation.
+ * @param type Type of the object to allocate.
+ * @param gc Garbage collection arena to register the allocation with.
+ */
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \
{ \
(dptr) = (type *)gc_malloc(sizeof(type), true, (gc)); \
}
+/**
+ * Abort if a memory allocation returned NULL.
+ *
+ * @param p Return value from \c malloc() or similar; calls \c out_of_memory()
+ * if \c p is NULL.
+ */
static inline void
check_malloc_return(void *p)
{
@@ -1139,22 +2081,28 @@
out_of_memory();
}
}
+/**@}*/
+/* End of GC */
-/*
- * Manage lists of buffers
+/** @name Buffer Lists
+ * @brief Manage lists of buffers
*/
+/**@{*/
+
+/** One node in a \c buffer_list linked list. */
struct buffer_entry
{
- struct buffer buf;
- struct buffer_entry *next;
+ struct buffer buf; /**< The buffer stored in this list node. */
+ struct buffer_entry *next; /**< Pointer to the next node, or NULL. */
};
+/** A singly-linked list of buffers, with head/tail pointers for O(1) push. */
struct buffer_list
{
- struct buffer_entry *head; /* next item to pop/peek */
- struct buffer_entry *tail; /* last item pushed */
- size_t size; /* current number of entries */
- size_t max_size; /* maximum size list should grow to */
+ struct buffer_entry *head; /**< Next item to pop/peek. */
+ struct buffer_entry *tail; /**< Last item pushed. */
+ size_t size; /**< Current number of entries. */
+ size_t max_size; /**< Maximum number of entries allowed. */
};
/**
@@ -1215,8 +2163,24 @@
*/
struct buffer *buffer_list_peek(struct buffer_list *ol);
+/**
+ * Advance past \c n bytes in the head buffer, popping it if it becomes empty.
+ *
+ * \c n must not exceed the length of the head buffer; passing a larger value
+ * triggers an assertion failure.
+ *
+ * @param ol The list whose head buffer is to be advanced.
+ * @param n Number of bytes to skip in the head buffer.
+ */
void buffer_list_advance(struct buffer_list *ol, ssize_t n);
+/**
+ * Remove and free the head buffer of the list.
+ *
+ * Does nothing if \c ol is NULL or empty.
+ *
+ * @param ol The list to pop from.
+ */
void buffer_list_pop(struct buffer_list *ol);
/**
@@ -1244,6 +2208,20 @@
*/
void buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep);
+/**
+ * Read a file into a buffer list, one buffer per line.
+ *
+ * Opens \c fn for reading and pushes each line into a newly allocated buffer
+ * list using \c fgets(). Lines longer than \c max_line_len - 1 bytes are
+ * split across multiple consecutive buffers.
+ *
+ * @param fn Path to the file to read.
+ * @param max_line_len Maximum number of bytes (including null terminator)
+ * read per \c fgets() call.
+ *
+ * @return Pointer to the new buffer list, or NULL if the file could not be
+ * opened or memory allocation failed.
+ */
struct buffer_list *buffer_list_file(const char *fn, int max_line_len);
/**
@@ -1257,5 +2235,7 @@
* error
*/
struct buffer buffer_read_from_file(const char *filename, struct gc_arena *gc);
+/**@}*/
+/* End of Buffer Lists */
#endif /* BUFFER_H */