/** * @file staticstring.h * @brief A fixed-capacity string object handed out by the akgl string heap layer. * * The library allocates nothing at runtime, so a "string" here is a * PATH_MAX-sized buffer claimed from the pool with akgl_heap_next_string and * given back with akgl_heap_release_string. Capacity is fixed at compile time: * these functions truncate rather than grow, and truncation is silent. */ #ifndef _AKGL_STATICSTRING_H_ #define _AKGL_STATICSTRING_H_ #include #include #include #define AKGL_MAX_STRING_LENGTH PATH_MAX /** @brief Provides a fixed-capacity, heap-managed string buffer. */ typedef struct { int refcount; /**< Pool bookkeeping; 0 means the slot is free. Owned by the heap layer. */ char data[AKGL_MAX_STRING_LENGTH]; /**< The characters. Not guaranteed NUL-terminated when filled to capacity. */ } akgl_String; /** * @brief Set a pooled string's contents and mark the slot in use. * * Copies @p init into the buffer, or zeroes it when @p init is `NULL`, then * sets `refcount` to 1. Callers normally reach this through * akgl_heap_next_string rather than calling it directly. * * @param obj The pooled string to (re)initialize. Required. Its previous * contents are discarded without inspection. * @param init Initial contents, NUL-terminated. Optional -- `NULL` zero-fills * the buffer instead. An @p init that does not fit is truncated, * and the result is **always NUL-terminated**. Until 0.5.0 this was * `strncpy` at exactly the buffer size, which left an over-long * string unterminated -- and a pooled string is handed to `strcmp`, * `realpath` and SDL property calls, none of which stop at 4096. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER If @p obj is `NULL`. * * @note Until 0.5.0 the `NULL` @p init path zeroed `sizeof(akgl_String)` bytes * starting at `data`, which is four bytes past the end of the buffer -- * `refcount` sits in front of it, so the overrun landed on the next pool * slot's reference count. */ akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init); /** * @brief Copy the contents of one pooled string into another. * * A bounded copy between two already-claimed pool slots. It copies bytes only: * `refcount` is left alone, so @p dest keeps whatever pool state it had. * * @param src Source string. Required. Read up to @p count bytes. * @param dest Destination string. Required. Overwritten in place; the pool * slot must already have been claimed. * @param count Maximum bytes to copy. 0 selects #AKGL_MAX_STRING_LENGTH, the * whole buffer. A @p count shorter than the source truncates, and * the result is **always NUL-terminated**. The remainder is not * zero-padded, so this no longer writes the full buffer for a * short copy. A negative @p count, or one above * #AKGL_MAX_STRING_LENGTH, is refused -- both buffers are exactly * that long, so a larger count walked off the end of two pool * slots at once. * @return `NULL` on success, otherwise an error context owned by the caller. * @throws AKERR_NULLPOINTER If @p src or @p dest is `NULL`. * @throws AKERR_OUTOFBOUNDS If @p count is negative or above * #AKGL_MAX_STRING_LENGTH. * @throws AKERR_VALUE, AKERR_OUTOFBOUNDS Whatever `aksl_strncpy` reports. The * unreachable `errno` path this used to document went with the * `strncpy` call it described. */ akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dest, int count); #endif //_AKGL_STATICSTRING_H_