DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
strbuf.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file strbuf.h
6 * @brief Bounded no-heap string builder that fails closed on overflow (one shared copy).
7 *
8 * The same little `Buf` appender was open-coded inside the anonymous namespace of ~5
9 * codecs (utmc, sep2, openadr, atc, exc_decoder). It bump-appends into a caller-owned
10 * `char[]` and latches @c ok to false the first time something would not fit, so every
11 * later append is a no-op and callers test one flag at the end. These header-only inline
12 * helpers are the single home for it, mirroring hex.h / numparse.h - no `<stdlib.h>`,
13 * no heap, and zero link cost when unused. Per-codec numeric/JSON formatters stay local;
14 * only the verbatim pieces (struct + raw append + XML escape + terminate) live here.
15 *
16 * @author Douglas Quigg (dstroy0)
17 * @date 2026
18 */
19
20#ifndef DETERMINISTICESPASYNCWEBSERVER_DET_STRBUF_H
21#define DETERMINISTICESPASYNCWEBSERVER_DET_STRBUF_H
22
23#include <stddef.h>
24#include <string.h>
25
26/** @brief Bump-append target; @c ok latches false once an append would overflow @c cap. */
27struct DetSb
28{
29 char *p;
30 size_t cap;
31 size_t len;
32 bool ok;
33};
34
35/** @brief Append NUL-terminated @p s; leaves the buffer untouched and clears @c ok if it would not fit. */
36inline void det_sb_put(DetSb *b, const char *s)
37{
38 if (!b->ok)
39 return;
40 size_t sl = strnlen(s, b->cap);
41 if (b->len + sl >= b->cap)
42 {
43 b->ok = false;
44 return;
45 }
46 memcpy(b->p + b->len, s, sl);
47 b->len += sl;
48}
49
50/** @brief Append @p s XML-escaped (&amp; &lt; &gt; &quot;); a NULL @p s appends nothing. */
51inline void det_sb_xml(DetSb *b, const char *s)
52{
53 if (!b->ok || !s)
54 return;
55 for (; *s; s++)
56 {
57 const char *rep = nullptr;
58 switch (*s)
59 {
60 case '&':
61 rep = "&amp;";
62 break;
63 case '<':
64 rep = "&lt;";
65 break;
66 case '>':
67 rep = "&gt;";
68 break;
69 case '"':
70 rep = "&quot;";
71 break;
72 default:
73 break;
74 }
75 if (rep)
76 det_sb_put(b, rep);
77 else
78 {
79 if (b->len + 1 >= b->cap)
80 {
81 b->ok = false;
82 return;
83 }
84 b->p[b->len++] = *s;
85 }
86 }
87}
88
89/** @brief NUL-terminate and return the built length, or 0 if the build overflowed. */
90inline size_t det_sb_finish(DetSb *b)
91{
92 if (!b->ok)
93 return 0;
94 b->p[b->len] = '\0';
95 return b->len;
96}
97
98#endif // DETERMINISTICESPASYNCWEBSERVER_DET_STRBUF_H
size_t det_sb_finish(DetSb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
Definition strbuf.h:90
void det_sb_put(DetSb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:36
void det_sb_xml(DetSb *b, const char *s)
Append s XML-escaped (& < > "); a NULL s appends nothing.
Definition strbuf.h:51
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:28
size_t len
Definition strbuf.h:31
char * p
Definition strbuf.h:29
bool ok
Definition strbuf.h:32
size_t cap
Definition strbuf.h:30