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_