PipeWire 1.1.0
Loading...
Searching...
No Matches
json.h
Go to the documentation of this file.
1/* Simple Plugin API */
2/* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */
3/* SPDX-License-Identifier: MIT */
4
5#ifndef SPA_UTILS_JSON_H
6#define SPA_UTILS_JSON_H
7
8#ifdef __cplusplus
9extern "C" {
10#else
11#include <stdbool.h>
12#endif
13#include <stddef.h>
14#include <stdlib.h>
15#include <stdint.h>
16#include <string.h>
17#include <math.h>
18#include <float.h>
19
20#include <spa/utils/defs.h>
21#include <spa/utils/string.h>
22
32/* a simple JSON compatible tokenizer */
33struct spa_json {
34 const char *cur;
35 const char *end;
36 struct spa_json *parent;
37 uint32_t state;
38 uint32_t depth;
39};
41#define SPA_JSON_INIT(data,size) ((struct spa_json) { (data), (data)+(size), 0, 0, 0 })
43static inline void spa_json_init(struct spa_json * iter, const char *data, size_t size)
44{
45 *iter = SPA_JSON_INIT(data, size);
46}
47#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), 0, 0 })
48
49static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub)
50{
51 *sub = SPA_JSON_ENTER(iter);
52}
53
54#define SPA_JSON_SAVE(iter) ((struct spa_json) { (iter)->cur, (iter)->end, })
55
58static inline int spa_json_next(struct spa_json * iter, const char **value)
59{
60 int utf8_remain = 0;
61 enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT };
63 *value = iter->cur;
64 for (; iter->cur < iter->end; iter->cur++) {
65 unsigned char cur = (unsigned char)*iter->cur;
66 again:
67 switch (iter->state) {
68 case __NONE:
69 iter->state = __STRUCT;
70 iter->depth = 0;
71 goto again;
72 case __STRUCT:
73 switch (cur) {
74 case '\0': case '\t': case ' ': case '\r': case '\n': case ':': case '=': case ',':
75 continue;
76 case '#':
77 iter->state = __COMMENT;
78 continue;
79 case '"':
80 *value = iter->cur;
81 iter->state = __STRING;
82 continue;
83 case '[': case '{':
84 *value = iter->cur;
85 if (++iter->depth > 1)
86 continue;
87 iter->cur++;
88 return 1;
89 case '}': case ']':
90 if (iter->depth == 0) {
91 if (iter->parent)
92 iter->parent->cur = iter->cur;
93 return 0;
94 }
95 --iter->depth;
96 continue;
97 default:
98 *value = iter->cur;
99 iter->state = __BARE;
100 }
101 continue;
102 case __BARE:
103 switch (cur) {
104 case '\t': case ' ': case '\r': case '\n':
105 case ':': case ',': case '=': case ']': case '}':
106 iter->state = __STRUCT;
107 if (iter->depth > 0)
108 goto again;
109 return iter->cur - *value;
110 }
111 continue;
112 case __STRING:
113 switch (cur) {
114 case '\\':
115 iter->state = __ESC;
116 continue;
117 case '"':
118 iter->state = __STRUCT;
119 if (iter->depth > 0)
120 continue;
121 return ++iter->cur - *value;
122 case 240 ... 247:
123 utf8_remain++;
125 case 224 ... 239:
126 utf8_remain++;
128 case 192 ... 223:
129 utf8_remain++;
130 iter->state = __UTF8;
131 continue;
132 default:
133 if (cur >= 32 && cur <= 126)
134 continue;
135 }
136 return -1;
137 case __UTF8:
138 switch (cur) {
139 case 128 ... 191:
140 if (--utf8_remain == 0)
141 iter->state = __STRING;
142 continue;
143 }
144 return -1;
145 case __ESC:
146 switch (cur) {
147 case '"': case '\\': case '/': case 'b': case 'f':
148 case 'n': case 'r': case 't': case 'u':
149 iter->state = __STRING;
150 continue;
151 }
152 return -1;
153 case __COMMENT:
154 switch (cur) {
155 case '\n': case '\r':
156 iter->state = __STRUCT;
157 }
158 }
159
160 }
161 if (iter->depth != 0)
162 return -1;
163 if (iter->state != __STRUCT) {
164 iter->state = __STRUCT;
165 return iter->cur - *value;
166 }
167 return 0;
168}
169
170static inline int spa_json_enter_container(struct spa_json *iter, struct spa_json *sub, char type)
171{
172 const char *value;
173 if (spa_json_next(iter, &value) <= 0 || *value != type)
174 return -1;
175 spa_json_enter(iter, sub);
176 return 1;
177}
179static inline int spa_json_is_container(const char *val, int len)
180{
181 return len > 0 && (*val == '{' || *val == '[');
182}
183
184static inline int spa_json_container_len(struct spa_json *iter, const char *value, int len SPA_UNUSED)
185{
186 const char *val;
187 struct spa_json sub;
188 spa_json_enter(iter, &sub);
189 while (spa_json_next(&sub, &val) > 0);
190 return sub.cur + 1 - value;
191}
193/* object */
194static inline int spa_json_is_object(const char *val, int len)
195{
196 return len > 0 && *val == '{';
197}
198static inline int spa_json_enter_object(struct spa_json *iter, struct spa_json *sub)
199{
200 return spa_json_enter_container(iter, sub, '{');
201}
203/* array */
204static inline bool spa_json_is_array(const char *val, int len)
205{
206 return len > 0 && *val == '[';
207}
208static inline int spa_json_enter_array(struct spa_json *iter, struct spa_json *sub)
209{
210 return spa_json_enter_container(iter, sub, '[');
211}
213/* null */
214static inline bool spa_json_is_null(const char *val, int len)
215{
216 return len == 4 && strncmp(val, "null", 4) == 0;
217}
218
219/* float */
220static inline int spa_json_parse_float(const char *val, int len, float *result)
221{
222 char *end;
223 if (strspn(val, "+-0123456789.Ee") < (size_t)len)
224 return 0;
225 *result = spa_strtof(val, &end);
226 return len > 0 && end == val + len;
227}
229static inline bool spa_json_is_float(const char *val, int len)
230{
231 float dummy;
232 return spa_json_parse_float(val, len, &dummy);
233}
234static inline int spa_json_get_float(struct spa_json *iter, float *res)
235{
236 const char *value;
237 int len;
238 if ((len = spa_json_next(iter, &value)) <= 0)
239 return -1;
240 return spa_json_parse_float(value, len, res);
241}
243static inline char *spa_json_format_float(char *str, int size, float val)
244{
245 if (SPA_UNLIKELY(!isnormal(val))) {
246 if (val == INFINITY)
247 val = FLT_MAX;
248 else if (val == -INFINITY)
249 val = FLT_MIN;
250 else
251 val = 0.0f;
252 }
253 return spa_dtoa(str, size, val);
254}
255
256/* int */
257static inline int spa_json_parse_int(const char *val, int len, int *result)
258{
259 char *end;
260 *result = strtol(val, &end, 0);
261 return len > 0 && end == val + len;
262}
263static inline bool spa_json_is_int(const char *val, int len)
264{
265 int dummy;
266 return spa_json_parse_int(val, len, &dummy);
267}
268static inline int spa_json_get_int(struct spa_json *iter, int *res)
269{
270 const char *value;
271 int len;
272 if ((len = spa_json_next(iter, &value)) <= 0)
273 return -1;
274 return spa_json_parse_int(value, len, res);
275}
277/* bool */
278static inline bool spa_json_is_true(const char *val, int len)
279{
280 return len == 4 && strncmp(val, "true", 4) == 0;
281}
282
283static inline bool spa_json_is_false(const char *val, int len)
284{
285 return len == 5 && strncmp(val, "false", 5) == 0;
287
288static inline bool spa_json_is_bool(const char *val, int len)
289{
290 return spa_json_is_true(val, len) || spa_json_is_false(val, len);
292
293static inline int spa_json_parse_bool(const char *val, int len, bool *result)
294{
295 if ((*result = spa_json_is_true(val, len)))
296 return 1;
297 if (!(*result = !spa_json_is_false(val, len)))
298 return 1;
299 return -1;
300}
301static inline int spa_json_get_bool(struct spa_json *iter, bool *res)
302{
303 const char *value;
304 int len;
305 if ((len = spa_json_next(iter, &value)) <= 0)
306 return -1;
307 return spa_json_parse_bool(value, len, res);
308}
310/* string */
311static inline bool spa_json_is_string(const char *val, int len)
312{
313 return len > 1 && *val == '"';
314}
315
316static inline int spa_json_parse_hex(const char *p, int num, uint32_t *res)
317{
318 int i;
319 *res = 0;
320 for (i = 0; i < num; i++) {
321 char v = p[i];
322 if (v >= '0' && v <= '9')
323 v = v - '0';
324 else if (v >= 'a' && v <= 'f')
325 v = v - 'a' + 10;
326 else if (v >= 'A' && v <= 'F')
327 v = v - 'A' + 10;
328 else
329 return -1;
330 *res = (*res << 4) | v;
331 }
332 return 1;
333}
334
335static inline int spa_json_parse_stringn(const char *val, int len, char *result, int maxlen)
336{
337 const char *p;
338 if (maxlen <= len)
339 return -1;
340 if (!spa_json_is_string(val, len)) {
341 if (result != val)
342 strncpy(result, val, len);
343 result += len;
344 } else {
345 for (p = val+1; p < val + len; p++) {
346 if (*p == '\\') {
347 p++;
348 if (*p == 'n')
349 *result++ = '\n';
350 else if (*p == 'r')
351 *result++ = '\r';
352 else if (*p == 'b')
353 *result++ = '\b';
354 else if (*p == 't')
355 *result++ = '\t';
356 else if (*p == 'f')
357 *result++ = '\f';
358 else if (*p == 'u') {
359 uint8_t prefix[] = { 0, 0xc0, 0xe0, 0xf0 };
360 uint32_t idx, n, v, cp, enc[] = { 0x80, 0x800, 0x10000 };
361 if (val + len - p < 5 ||
362 spa_json_parse_hex(p+1, 4, &cp) < 0) {
363 *result++ = *p;
364 continue;
365 }
366 p += 4;
367
368 if (cp >= 0xd800 && cp <= 0xdbff) {
369 if (val + len - p < 7 ||
370 p[1] != '\\' || p[2] != 'u' ||
371 spa_json_parse_hex(p+3, 4, &v) < 0 ||
372 v < 0xdc00 || v > 0xdfff)
373 continue;
374 p += 6;
375 cp = 0x010000 | ((cp & 0x3ff) << 10) | (v & 0x3ff);
376 } else if (cp >= 0xdc00 && cp <= 0xdfff)
377 continue;
378
379 for (idx = 0; idx < 3; idx++)
380 if (cp < enc[idx])
381 break;
382 for (n = idx; n > 0; n--, cp >>= 6)
383 result[n] = (cp | 0x80) & 0xbf;
384 *result++ = (cp | prefix[idx]) & 0xff;
385 result += idx;
386 } else
387 *result++ = *p;
388 } else if (*p == '\"') {
389 break;
390 } else
391 *result++ = *p;
392 }
393 }
394 *result = '\0';
395 return 1;
396}
397
398static inline int spa_json_parse_string(const char *val, int len, char *result)
399{
400 return spa_json_parse_stringn(val, len, result, len+1);
401}
402
403static inline int spa_json_get_string(struct spa_json *iter, char *res, int maxlen)
404{
405 const char *value;
406 int len;
407 if ((len = spa_json_next(iter, &value)) <= 0)
408 return -1;
409 return spa_json_parse_stringn(value, len, res, maxlen);
410}
412static inline int spa_json_encode_string(char *str, int size, const char *val)
413{
414 int len = 0;
415 static const char hex[] = { "0123456789abcdef" };
416#define __PUT(c) { if (len < size) *str++ = c; len++; }
417 __PUT('"');
418 while (*val) {
419 switch (*val) {
420 case '\n':
421 __PUT('\\'); __PUT('n');
422 break;
423 case '\r':
424 __PUT('\\'); __PUT('r');
425 break;
426 case '\b':
427 __PUT('\\'); __PUT('b');
428 break;
429 case '\t':
430 __PUT('\\'); __PUT('t');
431 break;
432 case '\f':
433 __PUT('\\'); __PUT('f');
434 break;
435 case '\\':
436 case '"':
437 __PUT('\\'); __PUT(*val);
438 break;
439 default:
440 if (*val > 0 && *val < 0x20) {
441 __PUT('\\'); __PUT('u');
442 __PUT('0'); __PUT('0');
443 __PUT(hex[((*val)>>4)&0xf]); __PUT(hex[(*val)&0xf]);
444 } else {
445 __PUT(*val);
446 }
447 break;
448 }
449 val++;
450 }
451 __PUT('"');
452 __PUT('\0');
453#undef __PUT
454 return len-1;
455}
456
461#ifdef __cplusplus
462} /* extern "C" */
463#endif
464
465#endif /* SPA_UTILS_JSON_H */
spa/utils/defs.h
static bool spa_json_is_string(const char *val, int len)
Definition json.h:319
static bool spa_json_is_float(const char *val, int len)
Definition json.h:237
static int spa_json_parse_float(const char *val, int len, float *result)
Definition json.h:228
static bool spa_json_is_true(const char *val, int len)
Definition json.h:286
static int spa_json_parse_stringn(const char *val, int len, char *result, int maxlen)
Definition json.h:343
static int spa_json_enter_container(struct spa_json *iter, struct spa_json *sub, char type)
Definition json.h:178
static void spa_json_enter(struct spa_json *iter, struct spa_json *sub)
Definition json.h:56
static int spa_json_parse_hex(const char *p, int num, uint32_t *res)
Definition json.h:324
static bool spa_json_is_false(const char *val, int len)
Definition json.h:291
static int spa_json_get_int(struct spa_json *iter, int *res)
Definition json.h:276
static int spa_json_parse_bool(const char *val, int len, bool *result)
Definition json.h:301
static int spa_json_enter_object(struct spa_json *iter, struct spa_json *sub)
Definition json.h:206
static int spa_json_get_string(struct spa_json *iter, char *res, int maxlen)
Definition json.h:411
static bool spa_json_is_bool(const char *val, int len)
Definition json.h:296
#define SPA_JSON_INIT(data, size)
Definition json.h:47
static char * spa_json_format_float(char *str, int size, float val)
Definition json.h:251
static bool spa_json_is_array(const char *val, int len)
Definition json.h:212
static int spa_json_get_bool(struct spa_json *iter, bool *res)
Definition json.h:309
static bool spa_json_is_null(const char *val, int len)
Definition json.h:222
#define SPA_JSON_ENTER(iter)
Definition json.h:54
static int spa_json_encode_string(char *str, int size, const char *val)
Definition json.h:420
static int spa_json_parse_int(const char *val, int len, int *result)
Definition json.h:265
static int spa_json_next(struct spa_json *iter, const char **value)
Get the next token.
Definition json.h:66
static int spa_json_is_container(const char *val, int len)
Definition json.h:187
static void spa_json_init(struct spa_json *iter, const char *data, size_t size)
Definition json.h:49
static bool spa_json_is_int(const char *val, int len)
Definition json.h:271
static int spa_json_enter_array(struct spa_json *iter, struct spa_json *sub)
Definition json.h:216
static int spa_json_get_float(struct spa_json *iter, float *res)
Definition json.h:242
static int spa_json_is_object(const char *val, int len)
Definition json.h:202
static int spa_json_parse_string(const char *val, int len, char *result)
Definition json.h:406
static int spa_json_container_len(struct spa_json *iter, const char *value, int len 1)
Definition json.h:192
static float spa_strtof(const char *str, char **endptr)
Convert str to a float in the C locale.
Definition string.h:261
static char * spa_dtoa(char *str, size_t size, double val)
Definition string.h:354
#define SPA_UNUSED
Definition defs.h:285
#define SPA_UNLIKELY(x)
Definition defs.h:347
#define SPA_FALLTHROUGH
SPA_FALLTHROUGH is an annotation to suppress compiler warnings about switch cases that fall through w...
Definition defs.h:70
#define __PUT(c)
spa/utils/string.h
Definition json.h:38
uint32_t depth
Definition json.h:43
const char * cur
Definition json.h:39
uint32_t state
Definition json.h:42
const char * end
Definition json.h:40
struct spa_json * parent
Definition json.h:41