Software APIs
ujson.c
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 #include "sw/device/lib/ujson/ujson.h"
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <string.h>
10 
11 #include "sw/device/lib/base/crc32.h"
13 #include "sw/device/lib/base/status.h"
15 #include "sw/device/lib/ujson/private_status.h"
16 
17 static bool is_space(int c) { return c == ' ' || (c >= '\t' && c < '\t' + 5); }
18 
19 ujson_t ujson_init(void *context, status_t (*getc)(void *),
20  status_t (*putbuf)(void *, const char *, size_t)) {
21  ujson_t u = UJSON_INIT(context, getc, putbuf);
22  return u;
23 }
24 
25 void ujson_crc32_reset(ujson_t *uj) { crc32_init(&uj->crc32); }
26 
27 uint32_t ujson_crc32_finish(ujson_t *uj) { return crc32_finish(&uj->crc32); }
28 
29 status_t ujson_putbuf(ujson_t *uj, const char *buf, size_t len) {
30  crc32_add(&uj->crc32, buf, len);
31  return uj->putbuf(uj->io_context, buf, len);
32 }
33 
34 status_t ujson_getc(ujson_t *uj) {
35  int16_t buffer = uj->buffer;
36  if (buffer >= 0) {
37  uj->buffer = -1;
38  return OK_STATUS(buffer);
39  } else {
40  status_t s = uj->getc(uj->io_context);
41  if (!status_err(s)) {
42  crc32_add8(&uj->crc32, (uint8_t)s.value);
43  }
44  return s;
45  }
46 }
47 
48 status_t ujson_ungetc(ujson_t *uj, char ch) {
49  if (uj->buffer >= 0) {
50  return FAILED_PRECONDITION();
51  }
52  uj->buffer = ch;
53  return OK_STATUS();
54 }
55 
56 bool ujson_streq(const char *a, const char *b) {
57  while (*a && *b && *a == *b) {
58  ++a;
59  ++b;
60  }
61  return *a == *b;
62 }
63 
64 // Consumes whitespace returning first non-whitepsace character found.
65 static status_t consume_whitespace(ujson_t *uj) {
66  int ch;
67  do {
68  ch = TRY(ujson_getc(uj));
69  } while (is_space(ch));
70  return OK_STATUS(ch);
71 }
72 
73 static status_t consume_hexdigit(ujson_t *uj) {
74  int ch = TRY(ujson_getc(uj));
75  if (ch >= '0' && ch <= '9') {
76  return OK_STATUS(ch - '0');
77  } else if (ch >= 'A' && ch <= 'F') {
78  return OK_STATUS(ch - 'A' + 10);
79  } else if (ch >= 'a' && ch <= 'f') {
80  return OK_STATUS(ch - 'a' + 10);
81  } else {
82  return OUT_OF_RANGE();
83  }
84 }
85 
86 static status_t consume_hex(ujson_t *uj) {
87  int a = TRY(consume_hexdigit(uj));
88  int b = TRY(consume_hexdigit(uj));
89  int c = TRY(consume_hexdigit(uj));
90  int d = TRY(consume_hexdigit(uj));
91  return OK_STATUS((a << 12) | (b << 8) | (c << 4) | d);
92 }
93 
94 status_t ujson_consume(ujson_t *uj, char ch) {
95  if (ch != TRY(consume_whitespace(uj))) {
96  return NOT_FOUND();
97  }
98  return OK_STATUS();
99 }
100 
101 status_t ujson_consume_maybe(ujson_t *uj, char ch) {
102  char got = (char)TRY(consume_whitespace(uj));
103  if (ch != got) {
104  ujson_ungetc(uj, got);
105  return OK_STATUS(0);
106  }
107  return OK_STATUS(1);
108 }
109 
110 status_t ujson_parse_qs(ujson_t *uj, char *str, size_t len) {
111  char ch;
112  int n = 0;
113  len--; // One char for the nul terminator.
114  TRY(ujson_consume(uj, '"'));
115  while (true) {
116  ch = (char)TRY(ujson_getc(uj));
117  if (ch == '\"')
118  break;
119  if (ch == '\\') {
120  ch = (char)TRY(ujson_getc(uj));
121  switch (ch) {
122  case '"':
123  case '\\':
124  case '/':
125  break;
126  case 'b':
127  ch = '\b';
128  break;
129  case 'f':
130  ch = '\f';
131  break;
132  case 'n':
133  ch = '\n';
134  break;
135  case 'r':
136  ch = '\r';
137  break;
138  case 't':
139  ch = '\t';
140  break;
141  case 'u':
142  ch = (char)TRY(consume_hex(uj));
143  break;
144  default:
145  return OUT_OF_RANGE();
146  }
147  }
148  if (len > 0) {
149  *str++ = ch;
150  --len;
151  n++;
152  }
153  }
154  *str = '\0';
155  return OK_STATUS(n);
156 }
157 
158 status_t ujson_parse_integer(ujson_t *uj, void *result, size_t rsz) {
159  char ch = (char)TRY(consume_whitespace(uj));
160  bool neg = false;
161 
162  if (ch == '-') {
163  neg = true;
164  ch = (char)TRY(ujson_getc(uj));
165  }
166  uint64_t value = 0;
167 
168  if (!(ch >= '0' && ch <= '9')) {
169  return NOT_FOUND();
170  }
171  status_t s;
172  while (ch >= '0' && ch <= '9') {
173  if (value > UINT64_MAX / 10) {
174  return OUT_OF_RANGE();
175  }
176  value *= 10;
177  if (value > UINT64_MAX - (ch - '0')) {
178  return OUT_OF_RANGE();
179  }
180  value += ch - '0';
181  s = ujson_getc(uj);
182  if (status_err(s))
183  break;
184  ch = (char)s.value;
185  }
186  if (status_ok(s))
187  TRY(ujson_ungetc(uj, ch));
188 
189  if (neg) {
190  if (value > (uint64_t)INT64_MAX + 1) {
191  return OUT_OF_RANGE();
192  }
193 
194  int64_t neg_value = -1 * (int64_t)value;
195  memcpy(result, &neg_value, rsz);
196  return OK_STATUS();
197  }
198 
199  memcpy(result, &value, rsz);
200  return OK_STATUS();
201 }
202 
203 status_t ujson_deserialize_bool(ujson_t *uj, bool *value) {
204  char got = (char)TRY(consume_whitespace(uj));
205  if (got == 't') {
206  TRY(ujson_consume(uj, 'r'));
207  TRY(ujson_consume(uj, 'u'));
208  TRY(ujson_consume(uj, 'e'));
209  *value = true;
210  } else if (got == 'f') {
211  TRY(ujson_consume(uj, 'a'));
212  TRY(ujson_consume(uj, 'l'));
213  TRY(ujson_consume(uj, 's'));
214  TRY(ujson_consume(uj, 'e'));
215  *value = false;
216  } else {
217  ujson_ungetc(uj, got);
218  return NOT_FOUND();
219  }
220  return OK_STATUS();
221 }
222 
223 status_t ujson_deserialize_uint64_t(ujson_t *uj, uint64_t *value) {
224  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
225 }
226 status_t ujson_deserialize_uint32_t(ujson_t *uj, uint32_t *value) {
227  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
228 }
229 status_t ujson_deserialize_uint16_t(ujson_t *uj, uint16_t *value) {
230  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
231 }
232 status_t ujson_deserialize_uint8_t(ujson_t *uj, uint8_t *value) {
233  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
234 }
235 status_t ujson_deserialize_size_t(ujson_t *uj, size_t *value) {
236  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
237 }
238 status_t ujson_deserialize_int64_t(ujson_t *uj, int64_t *value) {
239  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
240 }
241 status_t ujson_deserialize_int32_t(ujson_t *uj, int32_t *value) {
242  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
243 }
244 status_t ujson_deserialize_int16_t(ujson_t *uj, int16_t *value) {
245  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
246 }
247 status_t ujson_deserialize_int8_t(ujson_t *uj, int8_t *value) {
248  return ujson_parse_integer(uj, (void *)value, sizeof(*value));
249 }
250 
251 static const char hex[] = "0123456789abcdef";
252 
253 status_t ujson_serialize_string(ujson_t *uj, const char *buf) {
254  uint8_t ch;
255  TRY(ujson_putbuf(uj, "\"", 1));
256  while ((ch = (uint8_t)*buf) != '\0') {
257  if (ch < 0x20 || ch == '"' || ch == '\\' || ch >= 0x7f) {
258  switch (ch) {
259  case '"':
260  TRY(ujson_putbuf(uj, "\\\"", 2));
261  break;
262  case '\\':
263  TRY(ujson_putbuf(uj, "\\\\", 2));
264  break;
265  case '\b':
266  TRY(ujson_putbuf(uj, "\\b", 2));
267  break;
268  case '\f':
269  TRY(ujson_putbuf(uj, "\\f", 2));
270  break;
271  case '\n':
272  TRY(ujson_putbuf(uj, "\\n", 2));
273  break;
274  case '\r':
275  TRY(ujson_putbuf(uj, "\\r", 2));
276  break;
277  case '\t':
278  TRY(ujson_putbuf(uj, "\\t", 2));
279  break;
280  default: {
281  char esc[] = {'\\', 'u', '0', '0', hex[ch >> 4], hex[ch & 0xF]};
282  TRY(ujson_putbuf(uj, esc, sizeof(esc)));
283  }
284  }
285  } else {
286  TRY(ujson_putbuf(uj, buf, 1));
287  }
288  ++buf;
289  }
290  TRY(ujson_putbuf(uj, "\"", 1));
291  return OK_STATUS();
292 }
293 
294 static status_t ujson_serialize_integer64(ujson_t *uj, uint64_t value,
295  bool neg) {
296  char buf[24];
297  char *end = buf + sizeof(buf);
298  size_t len = 0;
299  *--end = '\0';
300 
301  // If negative, two's complement for the absolute value.
302  if (neg)
303  value = ~value + 1;
304  do {
305  // We've banned __udivdi3; do division with the replacement function.
306  uint64_t remainder;
307  value = udiv64_slow(value, 10, &remainder);
308  *--end = '0' + (char)remainder;
309  ++len;
310  } while (value);
311  if (neg) {
312  *--end = '-';
313  ++len;
314  }
315  TRY(ujson_putbuf(uj, end, len));
316  return OK_STATUS();
317 }
318 
319 static status_t ujson_serialize_integer32(ujson_t *uj, uint32_t value,
320  bool neg) {
321  char buf[24];
322  char *end = buf + sizeof(buf);
323  size_t len = 0;
324  *--end = '\0';
325 
326  // If negative, two's complement for the absolute value.
327  if (neg)
328  value = ~value + 1;
329  do {
330  *--end = '0' + value % 10;
331  value /= 10;
332  ++len;
333  } while (value);
334  if (neg) {
335  *--end = '-';
336  ++len;
337  }
338  TRY(ujson_putbuf(uj, end, len));
339  return OK_STATUS();
340 }
341 
342 status_t ujson_serialize_bool(ujson_t *uj, const bool *value) {
343  if (*value) {
344  TRY(ujson_putbuf(uj, "true", 4));
345  } else {
346  TRY(ujson_putbuf(uj, "false", 5));
347  }
348  return OK_STATUS();
349 }
350 
351 status_t ujson_serialize_uint64_t(ujson_t *uj, const uint64_t *value) {
352  return ujson_serialize_integer64(uj, *value, false);
353 }
354 status_t ujson_serialize_uint32_t(ujson_t *uj, const uint32_t *value) {
355  return ujson_serialize_integer32(uj, *value, false);
356 }
357 
358 status_t ujson_serialize_uint16_t(ujson_t *uj, const uint16_t *value) {
359  return ujson_serialize_integer32(uj, *value, false);
360 }
361 
362 status_t ujson_serialize_uint8_t(ujson_t *uj, const uint8_t *value) {
363  return ujson_serialize_integer32(uj, *value, false);
364 }
365 
366 status_t ujson_serialize_size_t(ujson_t *uj, const size_t *value) {
367  if (sizeof(size_t) == sizeof(uint64_t)) {
368  return ujson_serialize_integer64(uj, *value, false);
369  } else {
370  return ujson_serialize_integer32(uj, *value, false);
371  }
372 }
373 
374 status_t ujson_serialize_int64_t(ujson_t *uj, const int64_t *value) {
375  return ujson_serialize_integer64(uj, (uint64_t)*value, *value < 0);
376 }
377 
378 status_t ujson_serialize_int32_t(ujson_t *uj, const int32_t *value) {
379  return ujson_serialize_integer32(uj, (uint32_t)*value, *value < 0);
380 }
381 
382 status_t ujson_serialize_int16_t(ujson_t *uj, const int16_t *value) {
383  return ujson_serialize_integer32(uj, (uint32_t)*value, *value < 0);
384 }
385 
386 status_t ujson_serialize_int8_t(ujson_t *uj, const int8_t *value) {
387  return ujson_serialize_integer32(uj, (uint32_t)*value, *value < 0);
388 }
389 
390 status_t ujson_deserialize_status_t(ujson_t *uj, status_t *value) {
391  private_status_t code;
392  uint32_t module_id = 0;
393  uint32_t arg = 0;
394  TRY(ujson_consume(uj, '{'));
395  TRY(ujson_deserialize_private_status_t(uj, &code));
396  TRY(ujson_consume(uj, ':'));
397  if (TRY(ujson_consume_maybe(uj, '['))) {
398  char module[4];
399  TRY(ujson_parse_qs(uj, module, sizeof(module)));
400  module_id = MAKE_MODULE_ID(module[0], module[1], module[2]);
401  TRY(ujson_consume(uj, ','));
402  TRY(ujson_deserialize_uint32_t(uj, &arg));
403  TRY(ujson_consume(uj, ']'));
404  } else {
405  TRY(ujson_deserialize_uint32_t(uj, &arg));
406  }
407  TRY(ujson_consume(uj, '}'));
408  *value =
409  status_create((absl_status_t)code, module_id, __FILE__, (int32_t)arg);
410  return OK_STATUS();
411 }
412 
413 status_t ujson_serialize_status_t(ujson_t *uj, const status_t *value) {
414  buffer_sink_t out = {
415  .data = uj,
416  .sink = (size_t(*)(void *, const char *, size_t))ujson_putbuf,
417  };
418  base_fprintf(out, "%!r", *value);
419  return OK_STATUS();
420 }