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
17static bool is_space(int c) { return c == ' ' || (c >= '\t' && c < '\t' + 5); }
18
19ujson_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
25void ujson_crc32_reset(ujson_t *uj) { crc32_init(&uj->crc32); }
26
27uint32_t ujson_crc32_finish(ujson_t *uj) { return crc32_finish(&uj->crc32); }
28
29status_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
34status_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
48status_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
56bool 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.
65static 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
73static 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
86static 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
94status_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
101status_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
110status_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
158status_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
203status_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
223status_t ujson_deserialize_uint64_t(ujson_t *uj, uint64_t *value) {
224 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
225}
226status_t ujson_deserialize_uint32_t(ujson_t *uj, uint32_t *value) {
227 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
228}
229status_t ujson_deserialize_uint16_t(ujson_t *uj, uint16_t *value) {
230 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
231}
232status_t ujson_deserialize_uint8_t(ujson_t *uj, uint8_t *value) {
233 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
234}
235status_t ujson_deserialize_size_t(ujson_t *uj, size_t *value) {
236 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
237}
238status_t ujson_deserialize_int64_t(ujson_t *uj, int64_t *value) {
239 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
240}
241status_t ujson_deserialize_int32_t(ujson_t *uj, int32_t *value) {
242 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
243}
244status_t ujson_deserialize_int16_t(ujson_t *uj, int16_t *value) {
245 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
246}
247status_t ujson_deserialize_int8_t(ujson_t *uj, int8_t *value) {
248 return ujson_parse_integer(uj, (void *)value, sizeof(*value));
249}
250
251static const char hex[] = "0123456789abcdef";
252
253status_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
294static 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
319static 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
342status_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
351status_t ujson_serialize_uint64_t(ujson_t *uj, const uint64_t *value) {
352 return ujson_serialize_integer64(uj, *value, false);
353}
354status_t ujson_serialize_uint32_t(ujson_t *uj, const uint32_t *value) {
355 return ujson_serialize_integer32(uj, *value, false);
356}
357
358status_t ujson_serialize_uint16_t(ujson_t *uj, const uint16_t *value) {
359 return ujson_serialize_integer32(uj, *value, false);
360}
361
362status_t ujson_serialize_uint8_t(ujson_t *uj, const uint8_t *value) {
363 return ujson_serialize_integer32(uj, *value, false);
364}
365
366status_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
374status_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
378status_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
382status_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
386status_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
390status_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
413status_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}