File dp_state.h¶
File List > inc > dp_state.h
Go to the documentation of this file
#ifndef DP_STATE_H
#define DP_STATE_H
#include "clib_common.h" /* DP_OK, DP_ERR_INVALID, fixed-width ints, memcpy */
/* FourCC type tag, e.g. DP_FOURCC('A','C','Q','R'). Stored little-end-first so
* the bytes read as "ACQR" in a hex dump on a little-endian host. */
#define DP_FOURCC(a, b, c, d) \
((uint32_t) ((uint32_t) (uint8_t) (a) | ((uint32_t) (uint8_t) (b) << 8) \
| ((uint32_t) (uint8_t) (c) << 16) \
| ((uint32_t) (uint8_t) (d) << 24)))
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define DP_STATE_ENDIAN 2u
#else
# define DP_STATE_ENDIAN 1u /* little-endian (x86-64, arm64 — doppler targets) */
#endif
typedef struct
{
uint32_t magic;
uint16_t version;
uint8_t endian;
uint8_t flags;
uint32_t bytes;
uint32_t _pad;
} dp_state_hdr_t;
/* ── writer cursor ──────────────────────────────────────────────────────────
* Sticky-error model: a bounds overrun sets `err` and subsequent writes no-op,
* so call sites stay flat (check `w.err` once at the end if desired). Overrun
* cannot happen when the caller allocated obj_state_bytes() — it is a guard. */
typedef struct
{
uint8_t *buf;
size_t cap;
size_t off;
int err;
} dp_writer_t;
static inline dp_writer_t
dp_writer_init (void *blob, size_t cap)
{
dp_writer_t w = { (uint8_t *) blob, cap, 0, 0 };
return w;
}
static inline void
dp_w_bytes (dp_writer_t *w, const void *src, size_t n)
{
if (w->err || w->off + n > w->cap)
{
w->err = 1;
return;
}
memcpy (w->buf + w->off, src, n);
w->off += n;
}
static inline void *
dp_w_reserve (dp_writer_t *w, size_t n)
{
if (w->err || w->off + n > w->cap)
{
w->err = 1;
return NULL;
}
void *p = w->buf + w->off;
w->off += n;
return p;
}
static inline void
dp_w_u32 (dp_writer_t *w, uint32_t v)
{
dp_w_bytes (w, &v, sizeof v);
}
static inline void
dp_w_u64 (dp_writer_t *w, uint64_t v)
{
dp_w_bytes (w, &v, sizeof v);
}
static inline void
dp_w_f64 (dp_writer_t *w, double v)
{
dp_w_bytes (w, &v, sizeof v);
}
static inline void
dp_w_cf32 (dp_writer_t *w, const float _Complex *p, size_t n)
{
dp_w_bytes (w, p, n * sizeof (float _Complex));
}
static inline void
dp_w_f32 (dp_writer_t *w, const float *p, size_t n)
{
dp_w_bytes (w, p, n * sizeof (float));
}
static inline void
dp_w_hdr (dp_writer_t *w, uint32_t magic, uint16_t version, size_t total)
{
dp_state_hdr_t h
= { magic, version, (uint8_t) DP_STATE_ENDIAN, 0, (uint32_t) total, 0 };
dp_w_bytes (w, &h, sizeof h);
}
/* ── reader cursor ──────────────────────────────────────────────────────── */
typedef struct
{
const uint8_t *buf;
size_t cap;
size_t off;
int err;
} dp_reader_t;
static inline dp_reader_t
dp_reader_init (const void *blob, size_t cap)
{
dp_reader_t r = { (const uint8_t *) blob, cap, 0, 0 };
return r;
}
static inline void
dp_r_bytes (dp_reader_t *r, void *dst, size_t n)
{
if (r->err || r->off + n > r->cap)
{
r->err = 1;
return;
}
memcpy (dst, r->buf + r->off, n);
r->off += n;
}
static inline const void *
dp_r_reserve (dp_reader_t *r, size_t n)
{
if (r->err || r->off + n > r->cap)
{
r->err = 1;
return NULL;
}
const void *p = r->buf + r->off;
r->off += n;
return p;
}
static inline uint32_t
dp_r_u32 (dp_reader_t *r)
{
uint32_t v = 0;
dp_r_bytes (r, &v, sizeof v);
return v;
}
static inline uint64_t
dp_r_u64 (dp_reader_t *r)
{
uint64_t v = 0;
dp_r_bytes (r, &v, sizeof v);
return v;
}
static inline double
dp_r_f64 (dp_reader_t *r)
{
double v = 0.0;
dp_r_bytes (r, &v, sizeof v);
return v;
}
static inline void
dp_r_cf32 (dp_reader_t *r, float _Complex *p, size_t n)
{
dp_r_bytes (r, p, n * sizeof (float _Complex));
}
static inline void
dp_r_f32 (dp_reader_t *r, float *p, size_t n)
{
dp_r_bytes (r, p, n * sizeof (float));
}
static inline int
dp_state_validate (const void *blob, size_t expect_bytes, uint32_t magic,
uint16_t version)
{
if (expect_bytes < sizeof (dp_state_hdr_t))
return DP_ERR_INVALID;
const dp_state_hdr_t *h = (const dp_state_hdr_t *) blob;
if (h->magic != magic || h->version != version
|| h->endian != (uint8_t) DP_STATE_ENDIAN
|| h->bytes != (uint32_t) expect_bytes)
return DP_ERR_INVALID;
return DP_OK;
}
#define DP_DEFINE_RUN(pfx, STATE_T, IN_T, OUT_T) \
size_t pfx##_run (STATE_T *s, const void *state_in, void *state_out, \
const IN_T *in, size_t n_in, OUT_T *out, size_t max_out) \
{ \
if (state_in && pfx##_set_state (s, state_in) != DP_OK) \
return 0; \
size_t _n_out = pfx##_execute (s, in, n_in, out, max_out); \
if (state_out) \
pfx##_get_state (s, state_out); \
return _n_out; \
}
#define DP_DEFINE_POD_STATE(pfx, STATE_T, MAGIC, VERSION) \
size_t pfx##_state_bytes (const STATE_T *s) \
{ \
(void)s; \
return sizeof (dp_state_hdr_t) + sizeof (STATE_T); \
} \
void pfx##_get_state (const STATE_T *s, void *blob) \
{ \
dp_writer_t _w = dp_writer_init (blob, pfx##_state_bytes (s)); \
dp_w_hdr (&_w, (MAGIC), (VERSION), pfx##_state_bytes (s)); \
dp_w_bytes (&_w, s, sizeof *s); \
} \
int pfx##_set_state (STATE_T *s, const void *blob) \
{ \
int _rc = dp_state_validate (blob, pfx##_state_bytes (s), (MAGIC), \
(VERSION)); \
if (_rc != DP_OK) \
return _rc; \
dp_reader_t _r = dp_reader_init (blob, pfx##_state_bytes (s)); \
_r.off = sizeof (dp_state_hdr_t); \
dp_r_bytes (&_r, s, sizeof *s); \
return DP_OK; \
}
/* ── field-wise scaffolding ──────────────────────────────────────────────────
* For a hand-written triplet that packs a subset of fields (running state only;
* config restored by create()). Use inside `<obj>_get_state(s, blob)` /
* `<obj>_set_state(s, blob)` (those exact param names): open, then pack/unpack
* the running fields via `&_w` / `&_r`, and `return DP_OK;` from set.
*
* void foo_get_state (const foo_state_t *s, void *blob)
* { DP_GET_OPEN (FOO_MAGIC, FOO_VERSION, foo_state_bytes (s));
* dp_w_f64 (&_w, s->gain); }
* int foo_set_state (foo_state_t *s, const void *blob)
* { DP_SET_OPEN (FOO_MAGIC, FOO_VERSION, foo_state_bytes (s));
* s->gain = dp_r_f64 (&_r); return DP_OK; }
*/
#define DP_GET_OPEN(MAGIC, VERSION, BYTES) \
dp_writer_t _w = dp_writer_init (blob, (BYTES)); \
dp_w_hdr (&_w, (MAGIC), (VERSION), (BYTES))
#define DP_SET_OPEN(MAGIC, VERSION, BYTES) \
int _rc = dp_state_validate (blob, (BYTES), (MAGIC), (VERSION)); \
if (_rc != DP_OK) \
return _rc; \
dp_reader_t _r = dp_reader_init (blob, (BYTES)); \
_r.off = sizeof (dp_state_hdr_t)
/* ── composition: nest a child's self-contained sub-blob ─────────────────────
* A composition's state_bytes sums `<child>_state_bytes(child_ptr)`; its
* get/set then writes/reads each child as a nested envelope. `child_ptr` may be
* a pointer member or the address of an embedded-by-value member (`&s->lf`).
* DP_R_CHILD returns DP_ERR_INVALID from the enclosing set_state if the child
* rejects, so the whole restore is atomic-by-validation. */
#define DP_W_CHILD(w, pfx, child_ptr) \
do \
{ \
void *_cr = dp_w_reserve ((w), pfx##_state_bytes (child_ptr)); \
if (_cr) \
pfx##_get_state ((child_ptr), _cr); \
} \
while (0)
#define DP_R_CHILD(r, pfx, child_ptr) \
do \
{ \
const void *_cr = dp_r_reserve ((r), pfx##_state_bytes (child_ptr)); \
if (!_cr || pfx##_set_state ((child_ptr), _cr) != DP_OK) \
return DP_ERR_INVALID; \
} \
while (0)
#endif /* DP_STATE_H */