DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
bytes.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 bytes.h
6 * @brief Shared byte-cursor mechanics for the binary codecs (one source of truth).
7 *
8 * CBOR and MessagePack (and any future binary codec) kept their own copies of the
9 * exact same write cursor (`{buf, cap, pos, overflow}` with bounds-checked put +
10 * big-endian put) and read cursor (`{buf, len, pos, err}` with a big-endian take).
11 * The subtle invariants - keep counting `pos` past `cap` on overflow so the caller
12 * can size the buffer; sticky `err`; network (big-endian) byte order - now live
13 * here once, so a bug is fixed in one place and every codec inherits it.
14 *
15 * Header-only and templated on the cursor type, so each codec keeps its own public
16 * struct (CborWriter, MsgpackReader, ...) - these helpers just require the matching
17 * field names. No .cpp to wire into the per-env test src filters.
18 *
19 * @author Douglas Quigg (dstroy0)
20 * @date 2026
21 */
22
23#ifndef DETERMINISTICESPASYNCWEBSERVER_DET_BYTES_H
24#define DETERMINISTICESPASYNCWEBSERVER_DET_BYTES_H
25
26#include <stddef.h>
27#include <stdint.h>
28
29// --- write cursor: requires fields { uint8_t *buf; size_t cap; size_t pos; bool overflow; } ---
30
31/** @brief Bind a write cursor to @p buf (capacity @p cap) and reset it. */
32template <typename W> inline void det_bw_init(W *w, uint8_t *buf, size_t cap)
33{
34 w->buf = buf;
35 w->cap = cap;
36 w->pos = 0;
37 w->overflow = false;
38}
39
40/** @brief Bytes the payload needs so far (keeps counting past @p cap on overflow). */
41template <typename W> inline size_t det_bw_len(const W *w)
42{
43 return w->pos;
44}
45
46/** @brief True while every write has fit in the buffer. */
47template <typename W> inline bool det_bw_ok(const W *w)
48{
49 return !w->overflow;
50}
51
52/** @brief Append one byte; on overflow set the flag but keep counting @p pos. */
53template <typename W> inline void det_bw_put(W *w, uint8_t b)
54{
55 if (w->pos < w->cap)
56 w->buf[w->pos] = b;
57 else
58 w->overflow = true;
59 w->pos++; // keep counting so det_bw_len() reports the size the payload needs
60}
61
62/** @brief Append the low @p nbytes of @p val, big-endian (network order). */
63template <typename W> inline void det_bw_put_be(W *w, uint64_t val, int nbytes)
64{
65 for (int s = (nbytes - 1) * 8; s >= 0; s -= 8)
66 det_bw_put(w, (uint8_t)(val >> s));
67}
68
69// --- read cursor: requires fields { const uint8_t *buf; size_t len; size_t pos; bool err; } ---
70
71/** @brief Bind a read cursor to @p buf (length @p len) at offset 0. */
72template <typename R> inline void det_br_init(R *r, const uint8_t *buf, size_t len)
73{
74 r->buf = buf;
75 r->len = len;
76 r->pos = 0;
77 r->err = false;
78}
79
80/** @brief True while no malformed / out-of-bounds read has occurred. */
81template <typename R> inline bool det_br_ok(const R *r)
82{
83 return !r->err;
84}
85
86/**
87 * @brief Read @p nbytes big-endian immediately after the tag byte at @p pos,
88 * advancing past the tag and the argument (pos += 1 + nbytes).
89 *
90 * Both CBOR heads and MessagePack format bytes are a 1-byte tag followed by a
91 * big-endian argument, so this consumes the tag + argument in one step. Sets the
92 * sticky err and returns false if the read would run past the buffer.
93 */
94template <typename R> inline bool det_br_take_be(R *r, size_t nbytes, uint64_t *out)
95{
96 if (r->pos + 1 + nbytes > r->len)
97 {
98 r->err = true;
99 return false;
100 }
101 uint64_t v = 0;
102 for (size_t i = 0; i < nbytes; i++)
103 v = (v << 8) | r->buf[r->pos + 1 + i];
104 *out = v;
105 r->pos += 1 + nbytes;
106 return true;
107}
108
109#endif // DETERMINISTICESPASYNCWEBSERVER_DET_BYTES_H
void det_bw_put_be(W *w, uint64_t val, int nbytes)
Append the low nbytes of val, big-endian (network order).
Definition bytes.h:63
bool det_br_take_be(R *r, size_t nbytes, uint64_t *out)
Read nbytes big-endian immediately after the tag byte at pos, advancing past the tag and the argument...
Definition bytes.h:94
void det_br_init(R *r, const uint8_t *buf, size_t len)
Bind a read cursor to buf (length len) at offset 0.
Definition bytes.h:72
void det_bw_put(W *w, uint8_t b)
Append one byte; on overflow set the flag but keep counting pos.
Definition bytes.h:53
bool det_br_ok(const R *r)
True while no malformed / out-of-bounds read has occurred.
Definition bytes.h:81
bool det_bw_ok(const W *w)
True while every write has fit in the buffer.
Definition bytes.h:47
void det_bw_init(W *w, uint8_t *buf, size_t cap)
Bind a write cursor to buf (capacity cap) and reset it.
Definition bytes.h:32
size_t det_bw_len(const W *w)
Bytes the payload needs so far (keeps counting past cap on overflow).
Definition bytes.h:41