Closes internal-consistency items 19 through 36, 38 and 41. Item 37, the ~180 redundant casts, is deliberately left open with its reasoning in TODO.md: the benefit only arrives once the build turns on the warnings those casts suppress, and doing it before that is churn across the two files with the most outstanding functional defects. The two that were real bugs are in the actor state table. AKGL_ACTOR_STATE_STRING_NAMES was declared [AKGL_ACTOR_MAX_STATES+1] and defined [32], so a consumer trusting the declared bound read past the object; and indices 11 and 12 were named UNDEFINED_11 and UNDEFINED_12 where actor.h has MOVING_IN and MOVING_OUT, so no character JSON could bind a sprite to either state. tests/registry.c now walks the whole table -- every entry non-NULL, every entry resolving to its own bit, no two entries sharing a name. The bitmask macros are parenthesized and AKGL_BITMASK_CLEAR has lost the semicolon inside its body. Writing tests/bitmasks.c for that turned up something worth knowing: the obvious test does not catch it. For a bit that is set, the misparse `!(mask & bit) == bit` gives the same answer as the correct one. It only diverges for an unset bit whose value is not 1, and that is the shape the suite uses now. akgl_draw_background was the last public function outside the error protocol. It takes a backend like everything else in draw.h, restores the draw colour it found, and is tested -- TODO.md had it filed under "needs the offscreen renderer harness", which was never true; what it needed was to stop reading the global. All eight registry initializers go through one helper, so the seven that leaked an SDL_PropertiesID on every call after the first no longer do. Fixed in the same place because it is the same function: akgl_registry_init never called akgl_registry_init_properties, which made akgl_set_property a silent no-op for anyone not going through akgl_game_init -- Defects, Known and still open item 3. Also: AKGL_COLLIDE_RECTANGLES (three open parens, two closes) and akgl_Frame deleted, float32_t/float64_t used consistently, the developer-specific debug logging removed from the controller inner loop, the abandoned SDL_GetBasePath comments removed, nine unused locals removed, and dst renamed to dest. akgl_game_update's default flags no longer OR the same bit twice. That changes nothing today, and the reason is Performance item 32: the loop never reads either bit, which is why every actor is updated sixteen times a frame. Still open. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
72 lines
3.4 KiB
C
72 lines
3.4 KiB
C
/**
|
|
* @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 <string.h>
|
|
#include <akerror.h>
|
|
#include <limits.h>
|
|
|
|
#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 at most #AKGL_MAX_STRING_LENGTH bytes out of @p init, or zeroes the
|
|
* buffer 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 longer than
|
|
* #AKGL_MAX_STRING_LENGTH is truncated *and left unterminated*,
|
|
* because this is `strncpy` semantics, not `strlcpy`.
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
* @throws AKERR_NULLPOINTER If @p obj is `NULL`.
|
|
*
|
|
* @note Known defect: the `NULL` @p init path zeroes `sizeof(akgl_String)`
|
|
* bytes starting at `data`, which is four bytes past the end of the
|
|
* buffer -- `refcount` sits in front of it. TODO.md, "Known and still
|
|
* open" item 6.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init);
|
|
/**
|
|
* @brief Copy the contents of one pooled string into another.
|
|
*
|
|
* A bounded `strncpy` 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
|
|
* without writing a terminator; a @p count longer than the source
|
|
* zero-pads the remainder, per `strncpy`. Values above
|
|
* #AKGL_MAX_STRING_LENGTH overrun both buffers and are not
|
|
* rejected.
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
* @throws AKERR_NULLPOINTER If @p src or @p dest is `NULL`.
|
|
* @throws errno Whatever `errno` holds if `strncpy` returns something other
|
|
* than @p dest. In practice `strncpy` always returns its destination, so
|
|
* this path is unreachable rather than merely rare.
|
|
*/
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dest, int count);
|
|
#endif //_AKGL_STATICSTRING_H_
|