diff options
Diffstat (limited to 'third_party/pipewire/spa/utils/json.h')
-rw-r--r-- | third_party/pipewire/spa/utils/json.h | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/third_party/pipewire/spa/utils/json.h b/third_party/pipewire/spa/utils/json.h new file mode 100644 index 0000000000..73cad7f596 --- /dev/null +++ b/third_party/pipewire/spa/utils/json.h @@ -0,0 +1,483 @@ +/* Simple Plugin API + * + * Copyright © 2020 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SPA_UTILS_JSON_H +#define SPA_UTILS_JSON_H + +#ifdef __cplusplus +extern "C" { +#else +#include <stdbool.h> +#endif +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#include <spa/utils/defs.h> +#include <spa/utils/string.h> + +/** \defgroup spa_json JSON + * Relaxed JSON variant parsing + */ + +/** + * \addtogroup spa_json + * \{ + */ + +/* a simple JSON compatible tokenizer */ +struct spa_json { + const char *cur; + const char *end; + struct spa_json *parent; + uint32_t state; + uint32_t depth; +}; + +#define SPA_JSON_INIT(data,size) (struct spa_json) { (data), (data)+(size), } + +static inline void spa_json_init(struct spa_json * iter, const char *data, size_t size) +{ + *iter = SPA_JSON_INIT(data, size); +} +#define SPA_JSON_ENTER(iter) (struct spa_json) { (iter)->cur, (iter)->end, (iter), } + +static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub) +{ + *sub = SPA_JSON_ENTER(iter); +} + +#define SPA_JSON_SAVE(iter) (struct spa_json) { (iter)->cur, (iter)->end, } + +/** Get the next token. \a value points to the token and the return value + * is the length. */ +static inline int spa_json_next(struct spa_json * iter, const char **value) +{ + int utf8_remain = 0; + enum { __NONE, __STRUCT, __BARE, __STRING, __UTF8, __ESC, __COMMENT }; + + *value = iter->cur; + for (; iter->cur < iter->end; iter->cur++) { + unsigned char cur = (unsigned char)*iter->cur; + again: + switch (iter->state) { + case __NONE: + iter->state = __STRUCT; + iter->depth = 0; + goto again; + case __STRUCT: + switch (cur) { + case '\0': case '\t': case ' ': case '\r': case '\n': case ':': case '=': case ',': + continue; + case '#': + iter->state = __COMMENT; + continue; + case '"': + *value = iter->cur; + iter->state = __STRING; + continue; + case '[': case '{': + *value = iter->cur; + if (++iter->depth > 1) + continue; + iter->cur++; + return 1; + case '}': case ']': + if (iter->depth == 0) { + if (iter->parent) + iter->parent->cur = iter->cur; + return 0; + } + --iter->depth; + continue; + default: + *value = iter->cur; + iter->state = __BARE; + } + continue; + case __BARE: + switch (cur) { + case '\t': case ' ': case '\r': case '\n': + case ':': case ',': case '=': case ']': case '}': + iter->state = __STRUCT; + if (iter->depth > 0) + goto again; + return iter->cur - *value; + } + continue; + case __STRING: + switch (cur) { + case '\\': + iter->state = __ESC; + continue; + case '"': + iter->state = __STRUCT; + if (iter->depth > 0) + continue; + return ++iter->cur - *value; + case 240 ... 247: + utf8_remain++; + SPA_FALLTHROUGH; + case 224 ... 239: + utf8_remain++; + SPA_FALLTHROUGH; + case 192 ... 223: + utf8_remain++; + iter->state = __UTF8; + continue; + default: + if (cur >= 32 && cur <= 126) + continue; + } + return -1; + case __UTF8: + switch (cur) { + case 128 ... 191: + if (--utf8_remain == 0) + iter->state = __STRING; + continue; + } + return -1; + case __ESC: + switch (cur) { + case '"': case '\\': case '/': case 'b': case 'f': + case 'n': case 'r': case 't': case 'u': + iter->state = __STRING; + continue; + } + return -1; + case __COMMENT: + switch (cur) { + case '\n': case '\r': + iter->state = __STRUCT; + } + } + + } + if (iter->depth != 0) + return -1; + if (iter->state != __STRUCT) { + iter->state = __STRUCT; + return iter->cur - *value; + } + return 0; +} + +static inline int spa_json_enter_container(struct spa_json *iter, struct spa_json *sub, char type) +{ + const char *value; + if (spa_json_next(iter, &value) <= 0 || *value != type) + return -1; + spa_json_enter(iter, sub); + return 1; +} + +static inline int spa_json_is_container(const char *val, int len) +{ + return len > 0 && (*val == '{' || *val == '['); +} + +static inline int spa_json_container_len(struct spa_json *iter, const char *value, int len) +{ + const char *val; + struct spa_json sub; + spa_json_enter(iter, &sub); + while (spa_json_next(&sub, &val) > 0); + return sub.cur + 1 - value; +} + +/* object */ +static inline int spa_json_is_object(const char *val, int len) +{ + return len > 0 && *val == '{'; +} +static inline int spa_json_enter_object(struct spa_json *iter, struct spa_json *sub) +{ + return spa_json_enter_container(iter, sub, '{'); +} + +/* array */ +static inline bool spa_json_is_array(const char *val, int len) +{ + return len > 0 && *val == '['; +} +static inline int spa_json_enter_array(struct spa_json *iter, struct spa_json *sub) +{ + return spa_json_enter_container(iter, sub, '['); +} + +/* null */ +static inline bool spa_json_is_null(const char *val, int len) +{ + return len == 4 && strncmp(val, "null", 4) == 0; +} + +/* float */ +static inline int spa_json_parse_float(const char *val, int len, float *result) +{ + char *end; + *result = spa_strtof(val, &end); + return len > 0 && end == val + len; +} + +static inline bool spa_json_is_float(const char *val, int len) +{ + float dummy; + return spa_json_parse_float(val, len, &dummy); +} +static inline int spa_json_get_float(struct spa_json *iter, float *res) +{ + const char *value; + int len; + if ((len = spa_json_next(iter, &value)) <= 0) + return -1; + return spa_json_parse_float(value, len, res); +} + +static inline char *spa_json_format_float(char *str, int size, float val) +{ + if (SPA_UNLIKELY(!isnormal(val))) { + if (val == INFINITY) + val = FLT_MAX; + else if (val == -INFINITY) + val = FLT_MIN; + else + val = 0.0f; + } + return spa_dtoa(str, size, val); +} + +/* int */ +static inline int spa_json_parse_int(const char *val, int len, int *result) +{ + char *end; + *result = strtol(val, &end, 0); + return len > 0 && end == val + len; +} +static inline bool spa_json_is_int(const char *val, int len) +{ + int dummy; + return spa_json_parse_int(val, len, &dummy); +} +static inline int spa_json_get_int(struct spa_json *iter, int *res) +{ + const char *value; + int len; + if ((len = spa_json_next(iter, &value)) <= 0) + return -1; + return spa_json_parse_int(value, len, res); +} + +/* bool */ +static inline bool spa_json_is_true(const char *val, int len) +{ + return len == 4 && strncmp(val, "true", 4) == 0; +} + +static inline bool spa_json_is_false(const char *val, int len) +{ + return len == 5 && strncmp(val, "false", 5) == 0; +} + +static inline bool spa_json_is_bool(const char *val, int len) +{ + return spa_json_is_true(val, len) || spa_json_is_false(val, len); +} + +static inline int spa_json_parse_bool(const char *val, int len, bool *result) +{ + if ((*result = spa_json_is_true(val, len))) + return 1; + if (!(*result = !spa_json_is_false(val, len))) + return 1; + return -1; +} +static inline int spa_json_get_bool(struct spa_json *iter, bool *res) +{ + const char *value; + int len; + if ((len = spa_json_next(iter, &value)) <= 0) + return -1; + return spa_json_parse_bool(value, len, res); +} + +/* string */ +static inline bool spa_json_is_string(const char *val, int len) +{ + return len > 1 && *val == '"'; +} + +static inline int spa_json_parse_hex(const char *p, int num, uint32_t *res) +{ + int i; + *res = 0; + for (i = 0; i < num; i++) { + char v = p[i]; + if (v >= '0' && v <= '9') + v = v - '0'; + else if (v >= 'a' && v <= 'f') + v = v - 'a' + 10; + else if (v >= 'A' && v <= 'F') + v = v - 'A' + 10; + else + return -1; + *res = (*res << 4) | v; + } + return 1; +} + +static inline int spa_json_parse_stringn(const char *val, int len, char *result, int maxlen) +{ + const char *p; + if (maxlen <= len) + return -1; + if (!spa_json_is_string(val, len)) { + if (result != val) + strncpy(result, val, len); + result += len; + } else { + for (p = val+1; p < val + len; p++) { + if (*p == '\\') { + p++; + if (*p == 'n') + *result++ = '\n'; + else if (*p == 'r') + *result++ = '\r'; + else if (*p == 'b') + *result++ = '\b'; + else if (*p == 't') + *result++ = '\t'; + else if (*p == 'f') + *result++ = '\f'; + else if (*p == 'u') { + uint8_t prefix[] = { 0, 0xc0, 0xe0, 0xf0 }; + uint32_t idx, n, v, cp, enc[] = { 0x80, 0x800, 0x10000 }; + if (val + len - p < 5 || + spa_json_parse_hex(p+1, 4, &cp) < 0) { + *result++ = *p; + continue; + } + p += 4; + + if (cp >= 0xd800 && cp <= 0xdbff) { + if (val + len - p < 7 || + p[1] != '\\' || p[2] != 'u' || + spa_json_parse_hex(p+3, 4, &v) < 0 || + v < 0xdc00 || v > 0xdfff) + continue; + p += 6; + cp = 0x010000 | ((cp & 0x3ff) << 10) | (v & 0x3ff); + } else if (cp >= 0xdc00 && cp <= 0xdfff) + continue; + + for (idx = 0; idx < 3; idx++) + if (cp < enc[idx]) + break; + for (n = idx; n > 0; n--, cp >>= 6) + result[n] = (cp | 0x80) & 0xbf; + *result++ = (cp | prefix[idx]) & 0xff; + result += idx; + } else + *result++ = *p; + } else if (*p == '\"') { + break; + } else + *result++ = *p; + } + } + *result = '\0'; + return 1; +} + +static inline int spa_json_parse_string(const char *val, int len, char *result) +{ + return spa_json_parse_stringn(val, len, result, len+1); +} + +static inline int spa_json_get_string(struct spa_json *iter, char *res, int maxlen) +{ + const char *value; + int len; + if ((len = spa_json_next(iter, &value)) <= 0) + return -1; + return spa_json_parse_stringn(value, len, res, maxlen); +} + +static inline int spa_json_encode_string(char *str, int size, const char *val) +{ + int len = 0; + static const char hex[] = { "0123456789abcdef" }; +#define __PUT(c) { if (len < size) *str++ = c; len++; } + __PUT('"'); + while (*val) { + switch (*val) { + case '\n': + __PUT('\\'); __PUT('n'); + break; + case '\r': + __PUT('\\'); __PUT('r'); + break; + case '\b': + __PUT('\\'); __PUT('b'); + break; + case '\t': + __PUT('\\'); __PUT('t'); + break; + case '\f': + __PUT('\\'); __PUT('f'); + break; + case '\\': + case '"': + __PUT('\\'); __PUT(*val); + break; + default: + if (*val > 0 && *val < 0x20) { + __PUT('\\'); __PUT('u'); + __PUT('0'); __PUT('0'); + __PUT(hex[((*val)>>4)&0xf]); __PUT(hex[(*val)&0xf]); + } else { + __PUT(*val); + } + break; + } + val++; + } + __PUT('"'); + __PUT('\0'); +#undef __PUT + return len-1; +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_UTILS_JSON_H */ |