DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
wamp.cpp
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 wamp.cpp
6 * @brief WAMP message builders (over JsonWriter) + positional array parser (pure, host-tested).
7 */
8
10
11#if DETWS_ENABLE_WAMP
12
14
15// Emit a uint64 as a JSON number (JsonWriter's integer() is only platform-long wide).
16static void emit_uint(JsonWriter &w, uint64_t v)
17{
18 char rev[20];
19 size_t r = 0;
20 char tmp[21];
21 size_t n = 0;
22 if (v == 0)
23 {
24 w.raw("0");
25 return;
26 }
27 while (v)
28 {
29 rev[r++] = (char)('0' + (int)(v % 10));
30 v /= 10;
31 }
32 while (r)
33 tmp[n++] = rev[--r];
34 tmp[n] = '\0';
35 w.raw(tmp);
36}
37
38static size_t finish(JsonWriter &w)
39{
40 return w.ok() ? w.length() : 0;
41}
42
43// Append the trailing Arguments / ArgumentsKw of a PUBLISH / CALL / YIELD.
44static void emit_args(JsonWriter &w, const char *args_json, const char *kwargs_json)
45{
46 if (!args_json && !kwargs_json)
47 return;
48 w.raw(args_json ? args_json : "[]"); // kwargs without args still needs a positional Arguments
49 if (kwargs_json)
50 w.raw(kwargs_json);
51}
52
53size_t wamp_build_hello(char *buf, size_t cap, const char *realm, const char *details_json)
54{
55 if (!buf || !realm)
56 return 0;
57 JsonWriter w(buf, cap);
58 w.begin_array();
59 w.integer(WAMP_HELLO);
60 w.str(realm);
61 w.raw(details_json ? details_json : "{}");
62 w.end_array();
63 return finish(w);
64}
65
66size_t wamp_build_goodbye(char *buf, size_t cap, const char *reason_uri, const char *details_json)
67{
68 if (!buf || !reason_uri)
69 return 0;
70 JsonWriter w(buf, cap);
71 w.begin_array();
72 w.integer(WAMP_GOODBYE);
73 w.raw(details_json ? details_json : "{}");
74 w.str(reason_uri);
75 w.end_array();
76 return finish(w);
77}
78
79size_t wamp_build_subscribe(char *buf, size_t cap, uint64_t request, const char *topic, const char *options_json)
80{
81 if (!buf || !topic)
82 return 0;
83 JsonWriter w(buf, cap);
84 w.begin_array();
85 w.integer(WAMP_SUBSCRIBE);
86 emit_uint(w, request);
87 w.raw(options_json ? options_json : "{}");
88 w.str(topic);
89 w.end_array();
90 return finish(w);
91}
92
93size_t wamp_build_unsubscribe(char *buf, size_t cap, uint64_t request, uint64_t subscription_id)
94{
95 if (!buf)
96 return 0;
97 JsonWriter w(buf, cap);
98 w.begin_array();
99 w.integer(WAMP_UNSUBSCRIBE);
100 emit_uint(w, request);
101 emit_uint(w, subscription_id);
102 w.end_array();
103 return finish(w);
104}
105
106size_t wamp_build_publish(char *buf, size_t cap, uint64_t request, const char *topic, const char *options_json,
107 const char *args_json, const char *kwargs_json)
108{
109 if (!buf || !topic)
110 return 0;
111 JsonWriter w(buf, cap);
112 w.begin_array();
113 w.integer(WAMP_PUBLISH);
114 emit_uint(w, request);
115 w.raw(options_json ? options_json : "{}");
116 w.str(topic);
117 emit_args(w, args_json, kwargs_json);
118 w.end_array();
119 return finish(w);
120}
121
122size_t wamp_build_call(char *buf, size_t cap, uint64_t request, const char *procedure, const char *options_json,
123 const char *args_json, const char *kwargs_json)
124{
125 if (!buf || !procedure)
126 return 0;
127 JsonWriter w(buf, cap);
128 w.begin_array();
129 w.integer(WAMP_CALL);
130 emit_uint(w, request);
131 w.raw(options_json ? options_json : "{}");
132 w.str(procedure);
133 emit_args(w, args_json, kwargs_json);
134 w.end_array();
135 return finish(w);
136}
137
138size_t wamp_build_register(char *buf, size_t cap, uint64_t request, const char *procedure, const char *options_json)
139{
140 if (!buf || !procedure)
141 return 0;
142 JsonWriter w(buf, cap);
143 w.begin_array();
144 w.integer(WAMP_REGISTER);
145 emit_uint(w, request);
146 w.raw(options_json ? options_json : "{}");
147 w.str(procedure);
148 w.end_array();
149 return finish(w);
150}
151
152size_t wamp_build_yield(char *buf, size_t cap, uint64_t request, const char *options_json, const char *args_json,
153 const char *kwargs_json)
154{
155 if (!buf)
156 return 0;
157 JsonWriter w(buf, cap);
158 w.begin_array();
159 w.integer(WAMP_YIELD);
160 emit_uint(w, request);
161 w.raw(options_json ? options_json : "{}");
162 emit_args(w, args_json, kwargs_json);
163 w.end_array();
164 return finish(w);
165}
166
167// ---- positional parser ----
168
169static size_t skip_ws(const char *s, size_t i)
170{
171 while (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r')
172 i++;
173 return i;
174}
175
176// Scan a JSON string at s[i]=='"'; return the index past the closing quote, or 0 on error.
177static size_t scan_string(const char *s, size_t i)
178{
179 i++; // past the opening quote
180 while (s[i])
181 {
182 if (s[i] == '\\')
183 {
184 if (!s[i + 1])
185 return 0;
186 i += 2;
187 continue;
188 }
189 if (s[i] == '"')
190 return i + 1;
191 i++;
192 }
193 return 0;
194}
195
196// Scan one JSON value at s[i] (no leading ws); return the index just past it, or 0 on error.
197static size_t scan_value(const char *s, size_t i)
198{
199 if (s[i] == '"')
200 return scan_string(s, i);
201 if (s[i] == '{' || s[i] == '[')
202 {
203 char open = s[i], close = (open == '{') ? '}' : ']';
204 int depth = 0;
205 while (s[i])
206 {
207 if (s[i] == '"')
208 {
209 size_t e = scan_string(s, i);
210 if (!e)
211 return 0;
212 i = e;
213 continue;
214 }
215 if (s[i] == open)
216 depth++;
217 else if (s[i] == close)
218 {
219 depth--;
220 if (depth == 0)
221 return i + 1;
222 }
223 i++;
224 }
225 return 0;
226 }
227 // bare token: number / true / false / null
228 size_t start = i;
229 while (s[i] && s[i] != ',' && s[i] != ']' && s[i] != '}' && s[i] != ' ' && s[i] != '\t' && s[i] != '\n' &&
230 s[i] != '\r')
231 i++;
232 return i > start ? i : 0;
233}
234
235bool wamp_element(const char *msg, size_t index, const char **start, size_t *len)
236{
237 if (!msg)
238 return false;
239 size_t i = skip_ws(msg, 0);
240 if (msg[i] != '[')
241 return false;
242 i++;
243 for (size_t idx = 0;; idx++)
244 {
245 i = skip_ws(msg, i);
246 if (msg[i] == ']' || msg[i] == '\0')
247 return false; // ran out before reaching index
248 size_t s = i;
249 size_t e = scan_value(msg, i);
250 if (!e)
251 return false;
252 if (idx == index)
253 {
254 if (start)
255 *start = msg + s;
256 if (len)
257 *len = e - s;
258 return true;
259 }
260 i = skip_ws(msg, e);
261 if (msg[i] == ',')
262 i++;
263 else
264 return false; // ']' (index past end), NUL, or malformed
265 }
266}
267
268bool wamp_get_uint(const char *msg, size_t index, uint64_t *out)
269{
270 const char *s;
271 size_t n;
272 if (!wamp_element(msg, index, &s, &n) || n == 0)
273 return false;
274 uint64_t v = 0;
275 for (size_t i = 0; i < n; i++)
276 {
277 if (s[i] < '0' || s[i] > '9')
278 return false;
279 v = v * 10 + (uint64_t)(s[i] - '0');
280 }
281 if (out)
282 *out = v;
283 return true;
284}
285
286bool wamp_get_type(const char *msg, int *out)
287{
288 uint64_t v;
289 if (!wamp_get_uint(msg, 0, &v))
290 return false;
291 if (out)
292 *out = (int)v;
293 return true;
294}
295
296bool wamp_get_uri(const char *msg, size_t index, char *out, size_t out_cap)
297{
298 const char *s;
299 size_t n;
300 if (!out || out_cap == 0 || !wamp_element(msg, index, &s, &n))
301 return false;
302 if (n < 2 || s[0] != '"' || s[n - 1] != '"') // must be a quoted string
303 return false;
304 size_t body = n - 2;
305 if (body + 1 > out_cap) // need room for the NUL
306 return false;
307 for (size_t i = 0; i < body; i++)
308 out[i] = s[i + 1];
309 out[body] = '\0';
310 return true;
311}
312
313#endif // DETWS_ENABLE_WAMP
Builds a JSON document into a fixed caller buffer, no heap.
Definition json.h:59
void str(const char *v)
Emit a quoted, escaped string value.
Definition json.cpp:171
void raw(const char *literal)
Emit a pre-formatted literal verbatim.
Definition json.cpp:207
void end_array()
Close ].
Definition json.cpp:156
void integer(long v)
Emit a signed integer value.
Definition json.cpp:179
bool ok() const
< False after any overflow / structural error.
Definition json.h:90
size_t length() const
< Bytes written so far (excludes the NUL).
Definition json.h:94
void begin_array()
Open [.
Definition json.cpp:152
Layer 6 (Presentation) - zero-heap JSON: a bounded writer and top-level reader.
WAMP (Web Application Messaging Protocol) codec (DETWS_ENABLE_WAMP) - zero-heap builders + a position...