ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
strbuf.h File Reference

Bounded no-heap string builder that fails closed on overflow (one shared copy). More...

#include <stddef.h>
#include <stdint.h>
#include <string.h>

Go to the source code of this file.

Classes

struct  pc_sb
 Bump-append target; ok latches false once an append would overflow cap. More...
 

Functions

void pc_sb_put_n (pc_sb *b, const char *s, size_t sl)
 Append sl bytes of s - the primitive the others build on.
 
void pc_sb_put (pc_sb *b, const char *s)
 Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
 
template<size_t N>
void pc_sb_lit (pc_sb *b, const char(&s)[N])
 Append a string literal. The array parameter deduces N, so the length is a constant.
 
void pc_sb_put_clip (pc_sb *b, const char *s)
 Append as much of s as fits and stop, WITHOUT latching ok.
 
void pc_sb_u64_clip (pc_sb *b, uint64_t v, uint8_t columns)
 Append v as decimal, right-aligned in at least columns with leading spaces, if the whole field fits - else append nothing, without latching ok.
 
void pc_sb_xml (pc_sb *b, const char *s)
 Append s XML-escaped (& < > "); a NULL s appends nothing.
 
void pc_sb_ch (pc_sb *b, char c)
 Append a single character.
 
void pc_sb_uint (pc_sb *b, uint64_t v, unsigned base, unsigned min_digits)
 Append v in base (10 or 16), left-padded with '0' to at least min_digits.
 
void pc_sb_u32w (pc_sb *b, uint32_t v, unsigned min_digits)
 Append v as decimal, zero-padded to at least min_digits (printf "%0Nu").
 
void pc_sb_hex (pc_sb *b, uint64_t v, unsigned min_digits)
 Append v as lowercase hex, zero-padded to at least min_digits (printf "%0Nx").
 
void pc_sb_u32 (pc_sb *b, uint32_t v)
 Append v as decimal (no leading zeros; "0" for zero).
 
void pc_sb_u64 (pc_sb *b, uint64_t v)
 Append v as decimal (64-bit).
 
void pc_sb_i64 (pc_sb *b, int64_t v)
 Append v as signed decimal (64-bit), with a leading '-' when negative.
 
bool pc_signbit (double v)
 True if v carries the IEEE-754 sign bit, including for -0.0 (a mask, not a divide).
 
bool pc_isinf (double v)
 True if v is an infinity: all exponent bits set, zero significand.
 
double pc_pow10i (int p)
 10^p for p >= 0, by binary composition.
 
int pc_dec_exp (double v)
 Decimal exponent of v (the X such that 10^X <= |v| < 10^(X+1)), for v > 0.
 
void pc_sb_digits (pc_sb *b, uint64_t mant, unsigned digits, unsigned point_after)
 Emit digits decimal digits of mant, with a '.' after the first point_after.
 
void pc_sb_g (pc_sb *b, double v, unsigned sig)
 Append v with sig significant digits, choosing fixed or scientific form - the printf "%.<sig>g" rendering, including trailing-zero removal.
 
void pc_sb_fixed (pc_sb *b, double v, unsigned decimals)
 Append v with exactly decimals digits after the point (printf "%.<decimals>f").
 
void pc_sb_json (pc_sb *b, const char *s)
 Append s as a JSON string literal: double-quoted, with "</tt> and <tt>\\</tt> backslash-escaped. A NULL @p s emits <tt>"". (Control chars are passed through, matching the emitters this replaced.)
 
size_t pc_sb_finish (pc_sb *b)
 NUL-terminate and return the built length, or 0 if the build overflowed.
 

Detailed Description

Bounded no-heap string builder that fails closed on overflow (one shared copy).

The same little Buf appender was open-coded inside the anonymous namespace of ~5 codecs (utmc, sep2, openadr, atc, exc_decoder). It bump-appends into a caller-owned char[] and latches ok to false the first time something would not fit, so every later append is a no-op and callers test one flag at the end. These header-only inline helpers are the single home for it, mirroring hex.h / numparse.h - no <stdlib.h>, no heap, and zero link cost when unused. The verbatim pieces (struct + raw append + XML escape + decimal + JSON-string + terminate) live here; the JSON emitters (hw_health, http_delivery, ble_gatt, ...) shared them byte-for-byte, so they are no longer per-codec.

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file strbuf.h.

Function Documentation

◆ pc_sb_put_n()

void pc_sb_put_n ( pc_sb b,
const char *  s,
size_t  sl 
)
inline

Append sl bytes of s - the primitive the others build on.

Takes the length rather than finding it. Every frame here is mostly literal text whose length the compiler already knows; scanning for a NUL to rediscover it is the same waste as re-parsing a format string. Appending a literal goes through pc_sb_lit, which deduces the length.

Definition at line 44 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, and pc_sb::p.

Referenced by pc_frame_vbuild(), pc_sb_lit(), and pc_sb_put().

◆ pc_sb_put()

void pc_sb_put ( pc_sb b,
const char *  s 
)
inline

Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.

Definition at line 60 of file strbuf.h.

References pc_sb::cap, pc_sb::ok, and pc_sb_put_n().

Referenced by PC::add_response_header(), fs_path_join(), pc_frame_vbuild(), pc_sb_fixed(), pc_sb_g(), pc_sb_json(), pc_sb_xml(), PC::redirect(), PC::send(), PC::send_chunked(), PC::send_empty(), PC::send_template(), PC::set_cache_control(), PC::set_cookie(), and PC::set_cors().

◆ pc_sb_lit()

template<size_t N>
void pc_sb_lit ( pc_sb b,
const char(&)  s[N] 
)
inline

Append a string literal. The array parameter deduces N, so the length is a constant.

pc_sb_put(b, "HTTP/1.1 ") scans nine bytes at runtime to learn what the type already states. Binding to const char (&)[N] takes the length from the type instead, and a pointer will not bind here, so a runtime string cannot reach this overload by mistake.

Definition at line 76 of file strbuf.h.

References pc_sb_put_n().

Referenced by PC::send_template().

◆ pc_sb_put_clip()

void pc_sb_put_clip ( pc_sb b,
const char *  s 
)
inline

Append as much of s as fits and stop, WITHOUT latching ok.

For display text only - an ls -l line, a log message - where a short rendering is better than none and nothing downstream parses the result. Never use it for a protocol field: a clipped header or frame has no terminator and desynchronizes the peer, which is exactly what the latching pc_sb_put exists to prevent. The two are deliberately different functions so the choice is visible at the call site rather than being a flag someone sets once and forgets.

Definition at line 90 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, and pc_sb::p.

◆ pc_sb_u64_clip()

void pc_sb_u64_clip ( pc_sb b,
uint64_t  v,
uint8_t  columns 
)
inline

Append v as decimal, right-aligned in at least columns with leading spaces, if the whole field fits - else append nothing, without latching ok.

The display-text counterpart to pc_sb_u64. A half-written number reads as a different number, so this one is all-or-nothing where pc_sb_put_clip is byte-wise. columns is what aligns a fixed-width column (an ls -l date is %2d day and %5d year); 0 asks for the natural width. Space padding, not the zero padding pc_sb_uint does: a column pads to align, a field pads to a fixed digit count, and the two are not interchangeable on the wire.

Definition at line 112 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, and pc_sb::p.

◆ pc_sb_xml()

void pc_sb_xml ( pc_sb b,
const char *  s 
)
inline

Append s XML-escaped (& < > "); a NULL s appends nothing.

Definition at line 143 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, pc_sb::p, and pc_sb_put().

Referenced by pc_frame_vbuild().

◆ pc_sb_ch()

void pc_sb_ch ( pc_sb b,
char  c 
)
inline

Append a single character.

Definition at line 186 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, and pc_sb::p.

Referenced by pc_frame_vbuild(), pc_sb_digits(), pc_sb_fixed(), pc_sb_g(), and pc_sb_i64().

◆ pc_sb_uint()

void pc_sb_uint ( pc_sb b,
uint64_t  v,
unsigned  base,
unsigned  min_digits 
)
inline

Append v in base (10 or 16), left-padded with '0' to at least min_digits.

The one shared engine behind the decimal and hex appenders: measure the field, bounds-check once, then fill it back-to-front in place. Same shape as pc_sb_u32 so neither needs a scratch array. min_digits is what carries a printf width like %08lx or %02d.

Definition at line 207 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, and pc_sb::p.

Referenced by pc_frame_vbuild(), pc_sb_hex(), pc_sb_i64(), pc_sb_u32(), pc_sb_u32w(), and pc_sb_u64().

◆ pc_sb_u32w()

void pc_sb_u32w ( pc_sb b,
uint32_t  v,
unsigned  min_digits 
)
inline

Append v as decimal, zero-padded to at least min_digits (printf "%0Nu").

Definition at line 291 of file strbuf.h.

References pc_sb_uint().

Referenced by pc_frame_vbuild(), pc_sb_fixed(), and pc_sb_g().

◆ pc_sb_hex()

void pc_sb_hex ( pc_sb b,
uint64_t  v,
unsigned  min_digits 
)
inline

Append v as lowercase hex, zero-padded to at least min_digits (printf "%0Nx").

Definition at line 297 of file strbuf.h.

References pc_sb_uint().

Referenced by pc_frame_vbuild().

◆ pc_sb_u32()

void pc_sb_u32 ( pc_sb b,
uint32_t  v 
)
inline

Append v as decimal (no leading zeros; "0" for zero).

Definition at line 303 of file strbuf.h.

References pc_sb_uint().

Referenced by pc_frame_vbuild(), PC::send_chunked(), PC::send_template(), and JsonWriter::uinteger().

◆ pc_sb_u64()

void pc_sb_u64 ( pc_sb b,
uint64_t  v 
)
inline

Append v as decimal (64-bit).

Definition at line 309 of file strbuf.h.

References pc_sb_uint().

Referenced by pc_frame_vbuild(), and pc_sb_fixed().

◆ pc_sb_i64()

void pc_sb_i64 ( pc_sb b,
int64_t  v 
)
inline

Append v as signed decimal (64-bit), with a leading '-' when negative.

Definition at line 315 of file strbuf.h.

References pc_sb_ch(), and pc_sb_uint().

Referenced by JsonWriter::integer(), pc_frame_vbuild(), PC::redirect(), PC::send(), and PC::send_empty().

◆ pc_signbit()

bool pc_signbit ( double  v)
inline

True if v carries the IEEE-754 sign bit, including for -0.0 (a mask, not a divide).

Definition at line 332 of file strbuf.h.

Referenced by pc_sb_fixed(), and pc_sb_g().

◆ pc_isinf()

bool pc_isinf ( double  v)
inline

True if v is an infinity: all exponent bits set, zero significand.

Definition at line 340 of file strbuf.h.

Referenced by pc_sb_fixed(), and pc_sb_g().

◆ pc_pow10i()

double pc_pow10i ( int  p)
inline

10^p for p >= 0, by binary composition.

Definition at line 348 of file strbuf.h.

Referenced by pc_dec_exp(), and pc_sb_g().

◆ pc_dec_exp()

int pc_dec_exp ( double  v)
inline

Decimal exponent of v (the X such that 10^X <= |v| < 10^(X+1)), for v > 0.

The binary exponent is a field of the IEEE-754 encoding, so it is a mask and a shift rather than something to converge on: multiplying it by log10(2) in fixed point (78913/2^18) gives the decimal exponent to within one, which a single comparison settles. The loop this replaces stepped one decade per iteration - about 320 of them for a denormal - to recover a number the representation was already carrying.

Definition at line 370 of file strbuf.h.

References pc_pow10i().

Referenced by pc_sb_g().

◆ pc_sb_digits()

void pc_sb_digits ( pc_sb b,
uint64_t  mant,
unsigned  digits,
unsigned  point_after 
)
inline

Emit digits decimal digits of mant, with a '.' after the first point_after.

Peels digits by division so no scratch array is needed. point_after == 0 or == digits emits no point.

Definition at line 406 of file strbuf.h.

References pc_sb_ch().

Referenced by pc_sb_g().

◆ pc_sb_g()

void pc_sb_g ( pc_sb b,
double  v,
unsigned  sig 
)
inline

Append v with sig significant digits, choosing fixed or scientific form - the printf "%.<sig>g" rendering, including trailing-zero removal.

Needed because g is a wire format here, not a debug convenience: SCPI's NR2/NR3 numeric forms and SenML/JSON numbers are both defined by what this produces, so the shape has to match what the format string produced rather than being approximated with fixed decimals.

Byte-identical to printf %.<sig>g for sig <= 10, which covers every call site in this library (SCPI uses 10, JSON/SenML use the default 6); verified against libc over 450k values including ties, denormals, +/-0, inf and NaN. Above that the scaling is done in double, which runs out of precision around 16 significant digits, so sig >= 15 can differ from libc in the last digit.

Definition at line 437 of file strbuf.h.

References pc_sb::ok, pc_dec_exp(), pc_isinf(), pc_pow10i(), pc_sb_ch(), pc_sb_digits(), pc_sb_put(), pc_sb_u32w(), and pc_signbit().

Referenced by pc_frame_vbuild(), and pc_sb_fixed().

◆ pc_sb_fixed()

void pc_sb_fixed ( pc_sb b,
double  v,
unsigned  decimals 
)
inline

Append v with exactly decimals digits after the point (printf "%.<decimals>f").

Byte-identical to printf for |v| < 2^64, which is the range a fixed-decimal reading occupies. A larger magnitude falls back to the significant-digit form (see pc_sb_g) rather than being rendered wrong: its exact f expansion needs big-integer arithmetic.

Definition at line 565 of file strbuf.h.

References pc_sb::ok, pc_isinf(), pc_sb_ch(), pc_sb_g(), pc_sb_put(), pc_sb_u32w(), pc_sb_u64(), and pc_signbit().

Referenced by pc_frame_vbuild().

◆ pc_sb_json()

void pc_sb_json ( pc_sb b,
const char *  s 
)
inline

Append s as a JSON string literal: double-quoted, with "</tt> and <tt>\\</tt> backslash-escaped. A NULL @p s emits <tt>"". (Control chars are passed through, matching the emitters this replaced.)

Definition at line 620 of file strbuf.h.

References pc_sb::cap, pc_sb::len, pc_sb::ok, pc_sb::p, and pc_sb_put().

Referenced by pc_frame_vbuild().

◆ pc_sb_finish()

size_t pc_sb_finish ( pc_sb b)
inline