Software APIs
ujson_derive.h
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#ifndef OPENTITAN_SW_DEVICE_LIB_UJSON_UJSON_DERIVE_H_
6#define OPENTITAN_SW_DEVICE_LIB_UJSON_UJSON_DERIVE_H_
7// This is what we'll use as the Rust enumeration name for C-enum values
8// that do not have a symbolic name.
9#define RUST_ENUM_INTVALUE IntValue
10
11#ifndef RUST_PREPROCESSOR_EMIT
12#include <stdint.h>
13
14#include "sw/device/lib/base/adv_macros.h"
15#include "sw/device/lib/base/status.h"
16#include "sw/device/lib/ujson/ujson.h"
17
18// If there is a pre-existing Rust `with_unknown! { ... }` enum, you can
19// add this flag to UJSON_SERDE_ENUM(...) to cause the C-based serializer
20// and deserializer to emit/parse the correct format.
21#define WITH_UNKNOWN 1
22#define RUST_ENUM_INTVALUE_STR OT_STRINGIFY(RUST_ENUM_INTVALUE)
23
24// clang-format off
25// clang-format is turned off; as scary as these macros look, they look
26// even scarier after clang-format is done with them.
27#define ujson_get_flags(...) \
28 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
29 ( /*then*/ \
30 0 \
31 , /*else*/ \
32 __VA_ARGS__ \
33 ) /*endif*/
34
35#define ujson_struct_field_array_indirect() ujson_struct_field_array
36#define ujson_struct_field_array(nt_, sz_, ...) \
37 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
38 ( /*then*/ \
39 nt_[sz_] \
40 , /*else*/ \
41 OT_OBSTRUCT(ujson_struct_field_array_indirect)()(nt_[sz_], __VA_ARGS__) \
42 ) /*endif*/
43
44#define ujson_struct_field(name_, type_, ...) \
45 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
46 ( /*then*/ \
47 type_ name_; \
48 , /*else*/ \
49 OT_EVAL(ujson_struct_field_array(type_ name_, __VA_ARGS__)); \
50 ) /*endif*/
51
52#define ujson_struct_string(name_, size_, ...) \
53 ujson_struct_field(name_, char, ##__VA_ARGS__, size_)
54
55#define UJSON_DECLARE_STRUCT(formal_name_, name_, decl_, ...) \
56 typedef struct formal_name_ { \
57 decl_(ujson_struct_field, ujson_struct_string) \
58 } name_
59
60#define ujson_enum_value(formal_name_, name_, ...) \
61 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
62 ( /*then*/ \
63 k ##formal_name_ ## name_ \
64 , /*else*/ \
65 k ##formal_name_ ## name_ = __VA_ARGS__ \
66 ) /*endif*/ ,
67
68#define UJSON_DECLARE_ENUM(formal_name_, name_, decl_, ...) \
69 typedef enum formal_name_ { \
70 decl_(formal_name_, ujson_enum_value) \
71 } name_
72
73
74// Helper to count number of fields.
75#define ujson_count(name_, type_, ...) +1
76
77//////////////////////////////////////////////////////////////////////
78// Serialize Implementation
79//////////////////////////////////////////////////////////////////////
80#define ujson_ser_loop_indirect() ujson_ser_loop
81#define ujson_ser_loop(expr, count, ...) \
82 TRY(ujson_putbuf(uj, "[", 1)); \
83 for(size_t x=0; x < count; ++x) { \
84 if (x) TRY(ujson_putbuf(uj, ",", 1)); \
85 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
86 ( /*then*/ \
87 expr; \
88 , /*else*/ \
89 OT_OBSTRUCT(ujson_ser_loop_indirect)()(expr, __VA_ARGS__) \
90 ) /*endif*/ \
91 } \
92 TRY(ujson_putbuf(uj, "]", 1));
93
94#define ujson_ser_field(name_, type_, ...) { \
95 TRY(ujson_serialize_string(uj, #name_)); \
96 TRY(ujson_putbuf(uj, ":", 1)); \
97 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
98 ( /*then*/ \
99 TRY(ujson_serialize_##type_(uj, &self->name_)); \
100 , /*else*/ \
101 const type_ *p = (const type_*)self->name_; \
102 OT_EVAL(ujson_ser_loop( \
103 TRY(ujson_serialize_##type_(uj, p++)), __VA_ARGS__)) \
104 ) /*endif*/ \
105 if (--nfield) TRY(ujson_putbuf(uj, ",", 1)); \
106 }
107
108#define ujson_ser_string(name_, size_, ...) { \
109 TRY(ujson_serialize_string(uj, #name_)); \
110 TRY(ujson_putbuf(uj, ":", 1)); \
111 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
112 ( /*then*/ \
113 TRY(ujson_serialize_string(uj, self->name_)); \
114 , /*else*/ \
115 const char *p = (const char*)self->name_; \
116 OT_EVAL(ujson_ser_loop( \
117 TRY(ujson_serialize_string(uj, p)); p+=size_, __VA_ARGS__)) \
118 ) /*endif*/ \
119 if (--nfield) TRY(ujson_putbuf(uj, ",", 1)); \
120 }
121
122#define UJSON_IMPL_SERIALIZE_STRUCT_WITH_PADDING(name_, decl_) \
123 status_t ujson_serialize_with_padding_##name_(ujson_t *uj, const name_ *self, size_t max_size) { \
124 size_t nfield = decl_(ujson_count, ujson_count); \
125 uj->str_size = 0; \
126 TRY(ujson_putbuf(uj, "{", 1)); \
127 decl_(ujson_ser_field, ujson_ser_string) \
128 if (max_size > uj->str_size + 1) { \
129 for (size_t i = 0; i < max_size - uj->str_size - 1; i++) { \
130 TRY(ujson_putbuf(uj, " ", 1)); \
131 } \
132 } \
133 TRY(ujson_putbuf(uj, "}", 1)); \
134 return OK_STATUS(); \
135 } \
136 extern const int __never_referenced___here_to_eat_a_semicolon[]
137
138#define UJSON_IMPL_SERIALIZE_STRUCT(name_, decl_) \
139 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) { \
140 size_t nfield = decl_(ujson_count, ujson_count); \
141 TRY(ujson_putbuf(uj, "{", 1)); \
142 decl_(ujson_ser_field, ujson_ser_string) \
143 TRY(ujson_putbuf(uj, "}", 1)); \
144 return OK_STATUS(); \
145 } \
146 extern const int __never_referenced___here_to_eat_a_semicolon[]
147
148#define ujson_ser_enum(formal_name_, name_, ...) \
149 case k ##formal_name_ ## name_: \
150 TRY(ujson_serialize_string(uj, #name_)); break;
151
152#define UJSON_IMPL_SERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
153 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) { \
154 switch(*self) { \
155 decl_(formal_name_, ujson_ser_enum) \
156 default: { \
157 const uint32_t value = (uint32_t)(*self); \
158 if (ujson_get_flags(__VA_ARGS__) & WITH_UNKNOWN) { \
159 TRY(ujson_serialize_uint32_t(uj, &value)); \
160 } else { \
161 TRY(ujson_putbuf(uj, \
162 "{\"" RUST_ENUM_INTVALUE_STR "\":", \
163 sizeof("{\"" RUST_ENUM_INTVALUE_STR "\":") - 1)); \
164 TRY(ujson_serialize_uint32_t(uj, &value)); \
165 TRY(ujson_putbuf(uj, "}", 1)); \
166 } \
167 } \
168 } \
169 return OK_STATUS(); \
170 } \
171 extern const int __never_referenced___here_to_eat_a_semicolon[]
172
173//////////////////////////////////////////////////////////////////////
174// Deserialize Implementation
175//////////////////////////////////////////////////////////////////////
176#define ujson_de_loop_indirect() ujson_de_loop
177#define ujson_de_loop(mult, expr, count, ...) \
178 TRY(ujson_consume(uj, '[')); \
179 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
180 ( /*then*/ \
181 size_t i = 0; \
182 while(true) { \
183 if (TRY(ujson_consume_maybe(uj, ']'))) break; \
184 if (i) TRY(ujson_consume(uj, ',')); \
185 if (i < count) { expr; ++i; } \
186 } \
187 if (i < count) { p += (count - i) * mult; } \
188 , /*else*/ \
189 for(size_t x=0;; ++x) { \
190 if (TRY(ujson_consume_maybe(uj, ']'))) break; \
191 if (x) TRY(ujson_consume(uj, ',')); \
192 OT_OBSTRUCT(ujson_de_loop_indirect)()(mult, expr, __VA_ARGS__) \
193 } \
194 ) /*endif*/ \
195
196#define ujson_de_field(name_, type_, ...) \
197 else if (ujson_streq(key, #name_)) { \
198 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
199 ( /*then*/ \
200 TRY(ujson_deserialize_##type_(uj, &self->name_)); \
201 , /*else*/ \
202 type_ *p = (type_*)self->name_; \
203 OT_EVAL(ujson_de_loop(1, \
204 TRY(ujson_deserialize_##type_(uj, p++)), __VA_ARGS__)) \
205 ) /*endif*/ \
206 }
207
208#define ujson_de_string(name_, size_, ...) \
209 else if (ujson_streq(key, #name_)) { \
210 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
211 ( /*then*/ \
212 TRY(ujson_parse_qs(uj, self->name_, sizeof(self->name_))); \
213 , /*else*/ \
214 char *p = (char*)self->name_; \
215 OT_EVAL(ujson_de_loop(size_, \
216 TRY(ujson_parse_qs(uj, p, sizeof(self->name_))); p+=size_, __VA_ARGS__)) \
217 ) /*endif*/ \
218 }
219
220#define UJSON_IMPL_DESERIALIZE_STRUCT(name_, decl_) \
221 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) { \
222 size_t nfield = 0; \
223 char key[128]; \
224 uj->str_size = 0; \
225 TRY(ujson_consume(uj, '{')); \
226 while(TRY(ujson_consume_maybe(uj, '}')) == 0) { \
227 if (nfield++ > 0) { \
228 TRY(ujson_consume(uj, ',')); \
229 } \
230 TRY(ujson_parse_qs(uj, key, sizeof(key))); \
231 TRY(ujson_consume(uj, ':')); \
232 if (0) {} \
233 decl_(ujson_de_field, ujson_de_string) \
234 else { \
235 return INVALID_ARGUMENT(); \
236 } \
237 } \
238 return OK_STATUS(); \
239 } \
240 extern const int __never_referenced___here_to_eat_a_semicolon[]
241
242#define ujson_de_enum(formal_name_, name_, ...) \
243 else if (ujson_streq(value, #name_)) { *self = k ##formal_name_ ## name_; }
244
245#define UJSON_IMPL_DESERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
246 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) { \
247 char value[128]; \
248 uj->str_size = 0; \
249 if (TRY(ujson_consume_maybe(uj, '"'))) { \
250 TRY(ujson_ungetc(uj, '"')); \
251 TRY(ujson_parse_qs(uj, value, sizeof(value))); \
252 if (0) {} \
253 decl_(formal_name_, ujson_de_enum) \
254 else { \
255 return INVALID_ARGUMENT(); \
256 } \
257 } else if(TRY(ujson_consume_maybe(uj, '{'))) { \
258 TRY(ujson_parse_qs(uj, value, sizeof(value))); \
259 TRY(ujson_consume(uj, ':')); \
260 if (ujson_streq(value, RUST_ENUM_INTVALUE_STR)) { \
261 TRY(ujson_deserialize_uint32_t(uj, (uint32_t*)self)); \
262 } else { \
263 return INVALID_ARGUMENT(); \
264 } \
265 TRY(ujson_consume(uj, '}')); \
266 } else { \
267 TRY(ujson_deserialize_uint32_t(uj, (uint32_t*)self)); \
268 } \
269 return OK_STATUS(); \
270 } \
271 extern const int __never_referenced___here_to_eat_a_semicolon[]
272
273#ifndef UJSON_SERDE_IMPL
274#define UJSON_SERDE_IMPL 0
275#endif
276
277#define UJSON_SERIALIZE_STRUCT(name_, decl_) \
278 OT_IIF(UJSON_SERDE_IMPL) \
279 ( /*then*/ \
280 UJSON_IMPL_SERIALIZE_STRUCT(name_, decl_) \
281 , /*else*/ \
282 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) \
283 ) /*endif*/
284
285#define UJSON_SERIALIZE_STRUCT_WITH_PADDING(name_, decl_) \
286 OT_IIF(UJSON_SERDE_IMPL) \
287 ( /*then*/ \
288 UJSON_IMPL_SERIALIZE_STRUCT_WITH_PADDING(name_, decl_) \
289 , /*else*/ \
290 status_t ujson_serialize_with_padding_##name_(ujson_t *uj, const name_ *self, size_t max_size) \
291 ) /*endif*/
292
293#define UJSON_SERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
294 OT_IIF(UJSON_SERDE_IMPL) \
295 ( /*then*/ \
296 UJSON_IMPL_SERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__) \
297 , /*else*/ \
298 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) \
299 ) /*endif*/
300
301#define UJSON_DESERIALIZE_STRUCT(name_, decl_) \
302 OT_IIF(UJSON_SERDE_IMPL) \
303 ( /*then*/ \
304 UJSON_IMPL_DESERIALIZE_STRUCT(name_, decl_) \
305 , /*else*/ \
306 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) \
307 ) /*endif*/
308
309#define UJSON_DESERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
310 OT_IIF(UJSON_SERDE_IMPL) \
311 ( /*then*/ \
312 UJSON_IMPL_DESERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__) \
313 , /*else*/ \
314 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) \
315 ) /*endif*/
316// clang-format on
317
318//////////////////////////////////////////////////////////////////////
319// Combined build-everything macros
320//////////////////////////////////////////////////////////////////////
321#define UJSON_SERDE_STRUCT(formal_name_, name_, decl_, ...) \
322 UJSON_DECLARE_STRUCT(formal_name_, name_, decl_, ##__VA_ARGS__); \
323 UJSON_SERIALIZE_STRUCT(name_, decl_); \
324 UJSON_SERIALIZE_STRUCT_WITH_PADDING(name_, decl_); \
325 UJSON_DESERIALIZE_STRUCT(name_, decl_)
326
327#define UJSON_SERDE_ENUM(formal_name_, name_, decl_, ...) \
328 UJSON_DECLARE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__); \
329 UJSON_SERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__); \
330 UJSON_DESERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__)
331
332#define C_ONLY(x) x
333#define RUST_ONLY(x) \
334 extern const int __never_referenced___here_to_eat_a_semicolon[]
335
336#else // RUST_PREPROCESSOR_EMIT
337#include "sw/device/lib/ujson/ujson_rust.h"
338#endif // RUST_PREPROCESSOR_EMIT
339#endif // OPENTITAN_SW_DEVICE_LIB_UJSON_UJSON_DERIVE_H_