|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
Declarative frame builder: a frame is a static table of typed fields, built by one engine. More...
Go to the source code of this file.
Classes | |
| struct | pc_field |
One field of a frame. Frames are static const pc_field[], so they live in rodata. More... | |
Macros | |
| #define | PC_STR {PC_FK_STR, 0, 0, nullptr} |
| #define | PC_U32 {PC_FK_U32, 0, 0, nullptr} |
| #define | PC_U64 {PC_FK_U64, 0, 0, nullptr} |
| #define | PC_I64 {PC_FK_I64, 0, 0, nullptr} |
| #define | PC_CH {PC_FK_CH, 0, 0, nullptr} |
| #define | PC_JSON {PC_FK_JSON, 0, 0, nullptr} |
| #define | PC_XML {PC_FK_XML, 0, 0, nullptr} |
| #define | PC_END {PC_FK_END, 0, 0, nullptr} |
Enumerations | |
| enum | pc_fk : uint8_t { PC_FK_END = 0 , PC_FK_LIT , PC_FK_STR , PC_FK_U32 , PC_FK_U64 , PC_FK_I64 , PC_FK_DEC , PC_FK_HEX , PC_FK_OCT , PC_FK_G , PC_FK_FIX , PC_FK_CH , PC_FK_JSON , PC_FK_XML } |
| Field kinds. The value is an opcode, so this is deliberately a plain byte enum. More... | |
Functions | |
| size_t | pc_frame_build (char *out, size_t cap, const pc_field *spec,...) |
Build spec into out (capacity cap), taking one variadic argument per valued field. | |
| size_t | pc_frame_vbuild (char *out, size_t cap, const pc_field *spec, va_list ap) |
| va_list form, for a caller that already has one. | |
| size_t | pc_frame_append (char *out, size_t cap, const pc_field *spec,...) |
Append spec to the NUL-terminated contents already in out. | |
Declarative frame builder: a frame is a static table of typed fields, built by one engine.
The problem this solves. Replacing printf-style formatting with hand-written pc_sb append sequences trades one call for a dozen, and every one of those dozens is new code with its own chance of a wrong order, a missing separator, or a forgotten overflow test. Across the ~160 formatting sites in this library that is well over a thousand new lines, none of which any test covers individually. A frame SPEC moves that structure into data: the shape of the frame is a static const pc_field[] in rodata, the engine that walks it is one function, and the engine is what gets tested. Adding a frame after that adds a table, not logic.
Why not a format string. A format string encodes the same table, but re-parses it from text on every call - scanning characters, decoding widths, dispatching per conversion - to rediscover what was known when the code was written. The spec is pre-decoded: the engine reads an opcode and a width from a struct and jumps. Nothing is parsed at runtime, and no float formatter is linked in unless a frame actually declares a float field.
Contract.
. There is no truncation: a partial frame is a protocol violation, and a caller that ignores the return still finds a valid empty C string rather than half a header or stale bytes from a previous build. -outis always NUL-terminated on both paths, so a caller may always read it as a string.A NULLPC_FK_STR` argument renders as empty, never as a crash or "(null)".Arguments are passed variadically in spec order, one per field that declares one (PC_FK_LIT and PC_FK_END take none). They are read at their default-promoted types, so a uint8_t passed to PC_FK_U32 arrives as unsigned and a float passed to PC_FK_G arrives as double, which is what the engine expects. The compiler cannot check that arity, but a spec's field count and its call sites are visible in the same translation unit, so it is checkable offline - that gate lands with the rollout, and until it does a mismatched spec is caught only by its test.
Definition in file frame.h.
| #define PC_JSON {PC_FK_JSON, 0, 0, nullptr} |
| enum pc_fk : uint8_t |
Field kinds. The value is an opcode, so this is deliberately a plain byte enum.
| size_t pc_frame_build | ( | char * | out, |
| size_t | cap, | ||
| const pc_field * | spec, | ||
| ... | |||
| ) |
Build spec into out (capacity cap), taking one variadic argument per valued field.
out is set empty). Definition at line 100 of file frame.cpp.
References pc_frame_vbuild().
| size_t pc_frame_vbuild | ( | char * | out, |
| size_t | cap, | ||
| const pc_field * | spec, | ||
| va_list | ap | ||
| ) |
va_list form, for a caller that already has one.
Definition at line 25 of file frame.cpp.
References pc_field::kind, pc_sb::len, PC_FK_CH, PC_FK_DEC, PC_FK_END, PC_FK_FIX, PC_FK_G, PC_FK_HEX, PC_FK_I64, PC_FK_JSON, PC_FK_LIT, PC_FK_OCT, PC_FK_STR, PC_FK_U32, PC_FK_U64, PC_FK_XML, pc_sb_ch(), pc_sb_finish(), pc_sb_fixed(), pc_sb_g(), pc_sb_hex(), pc_sb_i64(), pc_sb_json(), pc_sb_put(), pc_sb_put_n(), pc_sb_u32(), pc_sb_u32w(), pc_sb_u64(), pc_sb_uint(), and pc_sb_xml().
Referenced by pc_frame_append(), and pc_frame_build().
| size_t pc_frame_append | ( | char * | out, |
| size_t | cap, | ||
| const pc_field * | spec, | ||
| ... | |||
| ) |
Append spec to the NUL-terminated contents already in out.
The append idiom this library uses for header and cookie accumulation: on overflow the buffer is rewound to its previous length, so a frame is added whole or not at all and a half-written line never reaches the wire.
Definition at line 109 of file frame.cpp.
References pc_frame_vbuild().