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(name_, decl_) \
123 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) { \
124 size_t nfield = decl_(ujson_count, ujson_count); \
125 TRY(ujson_putbuf(uj, "{", 1)); \
126 decl_(ujson_ser_field, ujson_ser_string) \
127 TRY(ujson_putbuf(uj, "}", 1)); \
128 return OK_STATUS(); \
129 } \
130 extern const int __never_referenced___here_to_eat_a_semicolon[]
131
132#define ujson_ser_enum(formal_name_, name_, ...) \
133 case k ##formal_name_ ## name_: \
134 TRY(ujson_serialize_string(uj, #name_)); break;
135
136#define UJSON_IMPL_SERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
137 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) { \
138 switch(*self) { \
139 decl_(formal_name_, ujson_ser_enum) \
140 default: { \
141 const uint32_t value = (uint32_t)(*self); \
142 if (ujson_get_flags(__VA_ARGS__) & WITH_UNKNOWN) { \
143 TRY(ujson_serialize_uint32_t(uj, &value)); \
144 } else { \
145 TRY(ujson_putbuf(uj, \
146 "{\"" RUST_ENUM_INTVALUE_STR "\":", \
147 sizeof("{\"" RUST_ENUM_INTVALUE_STR "\":") - 1)); \
148 TRY(ujson_serialize_uint32_t(uj, &value)); \
149 TRY(ujson_putbuf(uj, "}", 1)); \
150 } \
151 } \
152 } \
153 return OK_STATUS(); \
154 } \
155 extern const int __never_referenced___here_to_eat_a_semicolon[]
156
157//////////////////////////////////////////////////////////////////////
158// Deserialize Implementation
159//////////////////////////////////////////////////////////////////////
160#define ujson_de_loop_indirect() ujson_de_loop
161#define ujson_de_loop(mult, expr, count, ...) \
162 TRY(ujson_consume(uj, '[')); \
163 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
164 ( /*then*/ \
165 size_t i = 0; \
166 while(true) { \
167 if (TRY(ujson_consume_maybe(uj, ']'))) break; \
168 if (i) TRY(ujson_consume(uj, ',')); \
169 if (i < count) { expr; ++i; } \
170 } \
171 if (i < count) { p += (count - i) * mult; } \
172 , /*else*/ \
173 for(size_t x=0;; ++x) { \
174 if (TRY(ujson_consume_maybe(uj, ']'))) break; \
175 if (x) TRY(ujson_consume(uj, ',')); \
176 OT_OBSTRUCT(ujson_de_loop_indirect)()(mult, expr, __VA_ARGS__) \
177 } \
178 ) /*endif*/ \
179
180#define ujson_de_field(name_, type_, ...) \
181 else if (ujson_streq(key, #name_)) { \
182 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
183 ( /*then*/ \
184 TRY(ujson_deserialize_##type_(uj, &self->name_)); \
185 , /*else*/ \
186 type_ *p = (type_*)self->name_; \
187 OT_EVAL(ujson_de_loop(1, \
188 TRY(ujson_deserialize_##type_(uj, p++)), __VA_ARGS__)) \
189 ) /*endif*/ \
190 }
191
192#define ujson_de_string(name_, size_, ...) \
193 else if (ujson_streq(key, #name_)) { \
194 OT_IIF(OT_NOT(OT_VA_ARGS_COUNT(dummy, ##__VA_ARGS__))) \
195 ( /*then*/ \
196 TRY(ujson_parse_qs(uj, self->name_, sizeof(self->name_))); \
197 , /*else*/ \
198 char *p = (char*)self->name_; \
199 OT_EVAL(ujson_de_loop(size_, \
200 TRY(ujson_parse_qs(uj, p, sizeof(self->name_))); p+=size_, __VA_ARGS__)) \
201 ) /*endif*/ \
202 }
203
204#define UJSON_IMPL_DESERIALIZE_STRUCT(name_, decl_) \
205 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) { \
206 size_t nfield = 0; \
207 char key[128]; \
208 TRY(ujson_consume(uj, '{')); \
209 while(TRY(ujson_consume_maybe(uj, '}')) == 0) { \
210 if (nfield++ > 0) { \
211 TRY(ujson_consume(uj, ',')); \
212 } \
213 TRY(ujson_parse_qs(uj, key, sizeof(key))); \
214 TRY(ujson_consume(uj, ':')); \
215 if (0) {} \
216 decl_(ujson_de_field, ujson_de_string) \
217 else { \
218 return INVALID_ARGUMENT(); \
219 } \
220 } \
221 return OK_STATUS(); \
222 } \
223 extern const int __never_referenced___here_to_eat_a_semicolon[]
224
225#define ujson_de_enum(formal_name_, name_, ...) \
226 else if (ujson_streq(value, #name_)) { *self = k ##formal_name_ ## name_; }
227
228#define UJSON_IMPL_DESERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
229 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) { \
230 char value[128]; \
231 if (TRY(ujson_consume_maybe(uj, '"'))) { \
232 TRY(ujson_ungetc(uj, '"')); \
233 TRY(ujson_parse_qs(uj, value, sizeof(value))); \
234 if (0) {} \
235 decl_(formal_name_, ujson_de_enum) \
236 else { \
237 return INVALID_ARGUMENT(); \
238 } \
239 } else if(TRY(ujson_consume_maybe(uj, '{'))) { \
240 TRY(ujson_parse_qs(uj, value, sizeof(value))); \
241 TRY(ujson_consume(uj, ':')); \
242 if (ujson_streq(value, RUST_ENUM_INTVALUE_STR)) { \
243 TRY(ujson_deserialize_uint32_t(uj, (uint32_t*)self)); \
244 } else { \
245 return INVALID_ARGUMENT(); \
246 } \
247 TRY(ujson_consume(uj, '}')); \
248 } else { \
249 TRY(ujson_deserialize_uint32_t(uj, (uint32_t*)self)); \
250 } \
251 return OK_STATUS(); \
252 } \
253 extern const int __never_referenced___here_to_eat_a_semicolon[]
254
255#ifndef UJSON_SERDE_IMPL
256#define UJSON_SERDE_IMPL 0
257#endif
258
259#define UJSON_SERIALIZE_STRUCT(name_, decl_) \
260 OT_IIF(UJSON_SERDE_IMPL) \
261 ( /*then*/ \
262 UJSON_IMPL_SERIALIZE_STRUCT(name_, decl_) \
263 , /*else*/ \
264 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) \
265 ) /*endif*/
266
267#define UJSON_SERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
268 OT_IIF(UJSON_SERDE_IMPL) \
269 ( /*then*/ \
270 UJSON_IMPL_SERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__) \
271 , /*else*/ \
272 status_t ujson_serialize_##name_(ujson_t *uj, const name_ *self) \
273 ) /*endif*/
274
275#define UJSON_DESERIALIZE_STRUCT(name_, decl_) \
276 OT_IIF(UJSON_SERDE_IMPL) \
277 ( /*then*/ \
278 UJSON_IMPL_DESERIALIZE_STRUCT(name_, decl_) \
279 , /*else*/ \
280 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) \
281 ) /*endif*/
282
283#define UJSON_DESERIALIZE_ENUM(formal_name_, name_, decl_, ...) \
284 OT_IIF(UJSON_SERDE_IMPL) \
285 ( /*then*/ \
286 UJSON_IMPL_DESERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__) \
287 , /*else*/ \
288 status_t ujson_deserialize_##name_(ujson_t *uj, name_ *self) \
289 ) /*endif*/
290// clang-format on
291
292//////////////////////////////////////////////////////////////////////
293// Combined build-everything macros
294//////////////////////////////////////////////////////////////////////
295#define UJSON_SERDE_STRUCT(formal_name_, name_, decl_, ...) \
296 UJSON_DECLARE_STRUCT(formal_name_, name_, decl_, ##__VA_ARGS__); \
297 UJSON_SERIALIZE_STRUCT(name_, decl_); \
298 UJSON_DESERIALIZE_STRUCT(name_, decl_)
299
300#define UJSON_SERDE_ENUM(formal_name_, name_, decl_, ...) \
301 UJSON_DECLARE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__); \
302 UJSON_SERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__); \
303 UJSON_DESERIALIZE_ENUM(formal_name_, name_, decl_, ##__VA_ARGS__)
304
305#define C_ONLY(x) x
306#define RUST_ONLY(x) \
307 extern const int __never_referenced___here_to_eat_a_semicolon[]
308
309#else // RUST_PREPROCESSOR_EMIT
310#include "sw/device/lib/ujson/ujson_rust.h"
311#endif // RUST_PREPROCESSOR_EMIT
312#endif // OPENTITAN_SW_DEVICE_LIB_UJSON_UJSON_DERIVE_H_