153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <math.h>
2553a5a1b3Sopenharmony_ci
2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
2753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
2853a5a1b3Sopenharmony_ci#include <pulsecore/hashmap.h>
2953a5a1b3Sopenharmony_ci#include <pulsecore/json.h>
3053a5a1b3Sopenharmony_ci#include <pulsecore/strbuf.h>
3153a5a1b3Sopenharmony_ci
3253a5a1b3Sopenharmony_ci#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_cistruct pa_json_object {
3553a5a1b3Sopenharmony_ci    pa_json_type type;
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci    union {
3853a5a1b3Sopenharmony_ci        int64_t int_value;
3953a5a1b3Sopenharmony_ci        double double_value;
4053a5a1b3Sopenharmony_ci        bool bool_value;
4153a5a1b3Sopenharmony_ci        char *string_value;
4253a5a1b3Sopenharmony_ci        pa_hashmap *object_values; /* name -> object */
4353a5a1b3Sopenharmony_ci        pa_idxset *array_values; /* objects */
4453a5a1b3Sopenharmony_ci    };
4553a5a1b3Sopenharmony_ci};
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ci/* JSON encoder context type */
4853a5a1b3Sopenharmony_citypedef enum pa_json_context_type {
4953a5a1b3Sopenharmony_ci    /* Top-level context of empty encoder. JSON element can be added. */
5053a5a1b3Sopenharmony_ci    PA_JSON_CONTEXT_EMPTY  = 0,
5153a5a1b3Sopenharmony_ci    /* Top-level context of encoder with an element. JSON element cannot be added. */
5253a5a1b3Sopenharmony_ci    PA_JSON_CONTEXT_TOP    = 1,
5353a5a1b3Sopenharmony_ci    /* JSON array context. JSON elements can be added. */
5453a5a1b3Sopenharmony_ci    PA_JSON_CONTEXT_ARRAY  = 2,
5553a5a1b3Sopenharmony_ci    /* JSON object context. JSON object members can be added. */
5653a5a1b3Sopenharmony_ci    PA_JSON_CONTEXT_OBJECT = 3,
5753a5a1b3Sopenharmony_ci} pa_json_context_type_t;
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_citypedef struct encoder_context {
6053a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
6153a5a1b3Sopenharmony_ci    int counter;
6253a5a1b3Sopenharmony_ci    struct encoder_context *next;
6353a5a1b3Sopenharmony_ci} encoder_context;
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_ci/* JSON encoder structure, a wrapper for pa_strbuf and encoder context */
6653a5a1b3Sopenharmony_cistruct pa_json_encoder {
6753a5a1b3Sopenharmony_ci    pa_strbuf *buffer;
6853a5a1b3Sopenharmony_ci    encoder_context *context;
6953a5a1b3Sopenharmony_ci};
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_cistatic const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_cistatic pa_json_object* json_object_new(void) {
7453a5a1b3Sopenharmony_ci    pa_json_object *obj;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    obj = pa_xnew0(pa_json_object, 1);
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci    return obj;
7953a5a1b3Sopenharmony_ci}
8053a5a1b3Sopenharmony_ci
8153a5a1b3Sopenharmony_cistatic bool is_whitespace(char c) {
8253a5a1b3Sopenharmony_ci    return c == '\t' || c == '\n' || c == '\r' || c == ' ';
8353a5a1b3Sopenharmony_ci}
8453a5a1b3Sopenharmony_ci
8553a5a1b3Sopenharmony_cistatic bool is_digit(char c) {
8653a5a1b3Sopenharmony_ci    return c >= '0' && c <= '9';
8753a5a1b3Sopenharmony_ci}
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_cistatic bool is_end(const char c, const char *end) {
9053a5a1b3Sopenharmony_ci    if (!end)
9153a5a1b3Sopenharmony_ci        return c == '\0';
9253a5a1b3Sopenharmony_ci    else  {
9353a5a1b3Sopenharmony_ci        while (*end) {
9453a5a1b3Sopenharmony_ci            if (c == *end)
9553a5a1b3Sopenharmony_ci                return true;
9653a5a1b3Sopenharmony_ci            end++;
9753a5a1b3Sopenharmony_ci        }
9853a5a1b3Sopenharmony_ci    }
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    return false;
10153a5a1b3Sopenharmony_ci}
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_cistatic const char* consume_string(const char *str, const char *expect) {
10453a5a1b3Sopenharmony_ci    while (*expect) {
10553a5a1b3Sopenharmony_ci        if (*str != *expect)
10653a5a1b3Sopenharmony_ci            return NULL;
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci        str++;
10953a5a1b3Sopenharmony_ci        expect++;
11053a5a1b3Sopenharmony_ci    }
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    return str;
11353a5a1b3Sopenharmony_ci}
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_cistatic const char* parse_null(const char *str, pa_json_object *obj) {
11653a5a1b3Sopenharmony_ci    str = consume_string(str, "null");
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    if (str)
11953a5a1b3Sopenharmony_ci        obj->type = PA_JSON_TYPE_NULL;
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci    return str;
12253a5a1b3Sopenharmony_ci}
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_cistatic const char* parse_boolean(const char *str, pa_json_object *obj) {
12553a5a1b3Sopenharmony_ci    const char *tmp;
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    tmp = consume_string(str, "true");
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci    if (tmp) {
13053a5a1b3Sopenharmony_ci        obj->type = PA_JSON_TYPE_BOOL;
13153a5a1b3Sopenharmony_ci        obj->bool_value = true;
13253a5a1b3Sopenharmony_ci    } else {
13353a5a1b3Sopenharmony_ci        tmp = consume_string(str, "false");
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci        if (str) {
13653a5a1b3Sopenharmony_ci            obj->type = PA_JSON_TYPE_BOOL;
13753a5a1b3Sopenharmony_ci            obj->bool_value = false;
13853a5a1b3Sopenharmony_ci        }
13953a5a1b3Sopenharmony_ci    }
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci    return tmp;
14253a5a1b3Sopenharmony_ci}
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_cistatic const char* parse_string(const char *str, pa_json_object *obj) {
14553a5a1b3Sopenharmony_ci    pa_strbuf *buf = pa_strbuf_new();
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_ci    str++; /* Consume leading '"' */
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    while (*str && *str != '"') {
15053a5a1b3Sopenharmony_ci        if (*str != '\\') {
15153a5a1b3Sopenharmony_ci            /* We only accept ASCII printable characters. */
15253a5a1b3Sopenharmony_ci            if (*str < 0x20 || *str > 0x7E) {
15353a5a1b3Sopenharmony_ci                pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
15453a5a1b3Sopenharmony_ci                goto error;
15553a5a1b3Sopenharmony_ci            }
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci            /* Normal character, juts consume */
15853a5a1b3Sopenharmony_ci            pa_strbuf_putc(buf, *str);
15953a5a1b3Sopenharmony_ci        } else {
16053a5a1b3Sopenharmony_ci            /* Need to unescape */
16153a5a1b3Sopenharmony_ci            str++;
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci            switch (*str) {
16453a5a1b3Sopenharmony_ci                case '"':
16553a5a1b3Sopenharmony_ci                case '\\':
16653a5a1b3Sopenharmony_ci                case '/':
16753a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, *str);
16853a5a1b3Sopenharmony_ci                    break;
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci                case 'b':
17153a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, '\b' /* backspace */);
17253a5a1b3Sopenharmony_ci                    break;
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci                case 'f':
17553a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, '\f' /* form feed */);
17653a5a1b3Sopenharmony_ci                    break;
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci                case 'n':
17953a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, '\n' /* new line */);
18053a5a1b3Sopenharmony_ci                    break;
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci                case 'r':
18353a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, '\r' /* carriage return */);
18453a5a1b3Sopenharmony_ci                    break;
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_ci                case 't':
18753a5a1b3Sopenharmony_ci                    pa_strbuf_putc(buf, '\t' /* horizontal tab */);
18853a5a1b3Sopenharmony_ci                    break;
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ci                case 'u':
19153a5a1b3Sopenharmony_ci                    pa_log("Unicode code points are currently unsupported");
19253a5a1b3Sopenharmony_ci                    goto error;
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci                default:
19553a5a1b3Sopenharmony_ci                    pa_log("Unexpected escape value: %c", *str);
19653a5a1b3Sopenharmony_ci                    goto error;
19753a5a1b3Sopenharmony_ci            }
19853a5a1b3Sopenharmony_ci        }
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci        str++;
20153a5a1b3Sopenharmony_ci    }
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    if (*str != '"') {
20453a5a1b3Sopenharmony_ci        pa_log("Failed to parse remainder of string: %s", str);
20553a5a1b3Sopenharmony_ci        goto error;
20653a5a1b3Sopenharmony_ci    }
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci    str++;
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci    obj->type = PA_JSON_TYPE_STRING;
21153a5a1b3Sopenharmony_ci    obj->string_value = pa_strbuf_to_string_free(buf);
21253a5a1b3Sopenharmony_ci
21353a5a1b3Sopenharmony_ci    return str;
21453a5a1b3Sopenharmony_ci
21553a5a1b3Sopenharmony_cierror:
21653a5a1b3Sopenharmony_ci    pa_strbuf_free(buf);
21753a5a1b3Sopenharmony_ci    return NULL;
21853a5a1b3Sopenharmony_ci}
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_cistatic const char* parse_number(const char *str, pa_json_object *obj) {
22153a5a1b3Sopenharmony_ci    bool has_fraction = false, has_exponent = false, valid = false;
22253a5a1b3Sopenharmony_ci    char *candidate = NULL;
22353a5a1b3Sopenharmony_ci    const char *s = str;
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci    if (*s == '-')
22653a5a1b3Sopenharmony_ci        s++;
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_ci    if (*s == '0') {
22953a5a1b3Sopenharmony_ci        valid = true;
23053a5a1b3Sopenharmony_ci        s++;
23153a5a1b3Sopenharmony_ci        goto fraction;
23253a5a1b3Sopenharmony_ci    }
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    while (is_digit(*s)) {
23553a5a1b3Sopenharmony_ci        valid = true;
23653a5a1b3Sopenharmony_ci        s++;
23753a5a1b3Sopenharmony_ci    }
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_cifraction:
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    if (!valid) {
24253a5a1b3Sopenharmony_ci        pa_log("Missing digits while parsing number");
24353a5a1b3Sopenharmony_ci        goto error;
24453a5a1b3Sopenharmony_ci    }
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ci    if (*s == '.') {
24753a5a1b3Sopenharmony_ci        has_fraction = true;
24853a5a1b3Sopenharmony_ci        s++;
24953a5a1b3Sopenharmony_ci        valid = false;
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci        while (is_digit(*s)) {
25253a5a1b3Sopenharmony_ci            valid = true;
25353a5a1b3Sopenharmony_ci            s++;
25453a5a1b3Sopenharmony_ci        }
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci        if (!valid) {
25753a5a1b3Sopenharmony_ci            pa_log("No digit after '.' while parsing fraction");
25853a5a1b3Sopenharmony_ci            goto error;
25953a5a1b3Sopenharmony_ci        }
26053a5a1b3Sopenharmony_ci    }
26153a5a1b3Sopenharmony_ci
26253a5a1b3Sopenharmony_ci    if (*s == 'e' || *s == 'E') {
26353a5a1b3Sopenharmony_ci        has_exponent = true;
26453a5a1b3Sopenharmony_ci        s++;
26553a5a1b3Sopenharmony_ci        valid = false;
26653a5a1b3Sopenharmony_ci
26753a5a1b3Sopenharmony_ci        if (*s == '-' || *s == '+')
26853a5a1b3Sopenharmony_ci            s++;
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci        while (is_digit(*s)) {
27153a5a1b3Sopenharmony_ci            valid = true;
27253a5a1b3Sopenharmony_ci            s++;
27353a5a1b3Sopenharmony_ci        }
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci        if (!valid) {
27653a5a1b3Sopenharmony_ci            pa_log("No digit in exponent while parsing fraction");
27753a5a1b3Sopenharmony_ci            goto error;
27853a5a1b3Sopenharmony_ci        }
27953a5a1b3Sopenharmony_ci    }
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    /* Number format looks good, now try to extract the value.
28253a5a1b3Sopenharmony_ci     * Here 's' points just after the string which will be consumed. */
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci    candidate = pa_xstrndup(str, s - str);
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci    if (has_fraction || has_exponent) {
28753a5a1b3Sopenharmony_ci        if (pa_atod(candidate, &obj->double_value) < 0) {
28853a5a1b3Sopenharmony_ci            pa_log("Cannot convert string '%s' to double value", str);
28953a5a1b3Sopenharmony_ci            goto error;
29053a5a1b3Sopenharmony_ci        }
29153a5a1b3Sopenharmony_ci        obj->type = PA_JSON_TYPE_DOUBLE;
29253a5a1b3Sopenharmony_ci    } else {
29353a5a1b3Sopenharmony_ci        if (pa_atoi64(candidate, &obj->int_value) < 0) {
29453a5a1b3Sopenharmony_ci            pa_log("Cannot convert string '%s' to int64_t value", str);
29553a5a1b3Sopenharmony_ci            goto error;
29653a5a1b3Sopenharmony_ci        }
29753a5a1b3Sopenharmony_ci        obj->type = PA_JSON_TYPE_INT;
29853a5a1b3Sopenharmony_ci    }
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_ci    pa_xfree(candidate);
30153a5a1b3Sopenharmony_ci
30253a5a1b3Sopenharmony_ci    return s;
30353a5a1b3Sopenharmony_ci
30453a5a1b3Sopenharmony_cierror:
30553a5a1b3Sopenharmony_ci    pa_xfree(candidate);
30653a5a1b3Sopenharmony_ci    return NULL;
30753a5a1b3Sopenharmony_ci}
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_cistatic const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
31053a5a1b3Sopenharmony_ci    pa_json_object *name = NULL, *value = NULL;
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    obj->object_values = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
31353a5a1b3Sopenharmony_ci                                             pa_xfree, (pa_free_cb_t) pa_json_object_free);
31453a5a1b3Sopenharmony_ci
31553a5a1b3Sopenharmony_ci    while (*str != '}') {
31653a5a1b3Sopenharmony_ci        str++; /* Consume leading '{' or ',' */
31753a5a1b3Sopenharmony_ci
31853a5a1b3Sopenharmony_ci        str = parse_value(str, ":", &name, depth + 1);
31953a5a1b3Sopenharmony_ci        if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
32053a5a1b3Sopenharmony_ci            pa_log("Could not parse key for object");
32153a5a1b3Sopenharmony_ci            goto error;
32253a5a1b3Sopenharmony_ci        }
32353a5a1b3Sopenharmony_ci
32453a5a1b3Sopenharmony_ci        /* Consume the ':' */
32553a5a1b3Sopenharmony_ci        str++;
32653a5a1b3Sopenharmony_ci
32753a5a1b3Sopenharmony_ci        str = parse_value(str, ",}", &value, depth + 1);
32853a5a1b3Sopenharmony_ci        if (!str) {
32953a5a1b3Sopenharmony_ci            pa_log("Could not parse value for object");
33053a5a1b3Sopenharmony_ci            goto error;
33153a5a1b3Sopenharmony_ci        }
33253a5a1b3Sopenharmony_ci
33353a5a1b3Sopenharmony_ci        pa_hashmap_put(obj->object_values, pa_xstrdup(pa_json_object_get_string(name)), value);
33453a5a1b3Sopenharmony_ci        pa_json_object_free(name);
33553a5a1b3Sopenharmony_ci
33653a5a1b3Sopenharmony_ci        name = NULL;
33753a5a1b3Sopenharmony_ci        value = NULL;
33853a5a1b3Sopenharmony_ci    }
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci    /* Drop trailing '}' */
34153a5a1b3Sopenharmony_ci    str++;
34253a5a1b3Sopenharmony_ci
34353a5a1b3Sopenharmony_ci    /* We now know the value was correctly parsed */
34453a5a1b3Sopenharmony_ci    obj->type = PA_JSON_TYPE_OBJECT;
34553a5a1b3Sopenharmony_ci
34653a5a1b3Sopenharmony_ci    return str;
34753a5a1b3Sopenharmony_ci
34853a5a1b3Sopenharmony_cierror:
34953a5a1b3Sopenharmony_ci    pa_hashmap_free(obj->object_values);
35053a5a1b3Sopenharmony_ci    obj->object_values = NULL;
35153a5a1b3Sopenharmony_ci
35253a5a1b3Sopenharmony_ci    if (name)
35353a5a1b3Sopenharmony_ci        pa_json_object_free(name);
35453a5a1b3Sopenharmony_ci    if (value)
35553a5a1b3Sopenharmony_ci        pa_json_object_free(value);
35653a5a1b3Sopenharmony_ci
35753a5a1b3Sopenharmony_ci    return NULL;
35853a5a1b3Sopenharmony_ci}
35953a5a1b3Sopenharmony_ci
36053a5a1b3Sopenharmony_cistatic const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
36153a5a1b3Sopenharmony_ci    pa_json_object *value;
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci    obj->array_values = pa_idxset_new(NULL, NULL);
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    while (*str != ']') {
36653a5a1b3Sopenharmony_ci        str++; /* Consume leading '[' or ',' */
36753a5a1b3Sopenharmony_ci
36853a5a1b3Sopenharmony_ci        /* Need to chew up whitespaces as a special case to deal with the
36953a5a1b3Sopenharmony_ci         * possibility of an empty array */
37053a5a1b3Sopenharmony_ci        while (is_whitespace(*str))
37153a5a1b3Sopenharmony_ci            str++;
37253a5a1b3Sopenharmony_ci
37353a5a1b3Sopenharmony_ci        if (*str == ']')
37453a5a1b3Sopenharmony_ci            break;
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_ci        str = parse_value(str, ",]", &value, depth + 1);
37753a5a1b3Sopenharmony_ci        if (!str) {
37853a5a1b3Sopenharmony_ci            pa_log("Could not parse value for array");
37953a5a1b3Sopenharmony_ci            goto error;
38053a5a1b3Sopenharmony_ci        }
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci        pa_idxset_put(obj->array_values, value, NULL);
38353a5a1b3Sopenharmony_ci    }
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci    /* Drop trailing ']' */
38653a5a1b3Sopenharmony_ci    str++;
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    /* We now know the value was correctly parsed */
38953a5a1b3Sopenharmony_ci    obj->type = PA_JSON_TYPE_ARRAY;
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    return str;
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_cierror:
39453a5a1b3Sopenharmony_ci    pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
39553a5a1b3Sopenharmony_ci    obj->array_values = NULL;
39653a5a1b3Sopenharmony_ci    return NULL;
39753a5a1b3Sopenharmony_ci}
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_citypedef enum {
40053a5a1b3Sopenharmony_ci    JSON_PARSER_STATE_INIT,
40153a5a1b3Sopenharmony_ci    JSON_PARSER_STATE_FINISH,
40253a5a1b3Sopenharmony_ci} json_parser_state;
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_cistatic const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
40553a5a1b3Sopenharmony_ci    json_parser_state state = JSON_PARSER_STATE_INIT;
40653a5a1b3Sopenharmony_ci    pa_json_object *o;
40753a5a1b3Sopenharmony_ci
40853a5a1b3Sopenharmony_ci    pa_assert(str != NULL);
40953a5a1b3Sopenharmony_ci
41053a5a1b3Sopenharmony_ci    o = json_object_new();
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci    if (depth > MAX_NESTING_DEPTH) {
41353a5a1b3Sopenharmony_ci        pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
41453a5a1b3Sopenharmony_ci        goto error;
41553a5a1b3Sopenharmony_ci    }
41653a5a1b3Sopenharmony_ci
41753a5a1b3Sopenharmony_ci    while (!is_end(*str, end)) {
41853a5a1b3Sopenharmony_ci        switch (state) {
41953a5a1b3Sopenharmony_ci            case JSON_PARSER_STATE_INIT:
42053a5a1b3Sopenharmony_ci                if (is_whitespace(*str)) {
42153a5a1b3Sopenharmony_ci                    str++;
42253a5a1b3Sopenharmony_ci                } else if (*str == 'n') {
42353a5a1b3Sopenharmony_ci                    str = parse_null(str, o);
42453a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
42553a5a1b3Sopenharmony_ci                } else if (*str == 't' || *str == 'f') {
42653a5a1b3Sopenharmony_ci                    str = parse_boolean(str, o);
42753a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
42853a5a1b3Sopenharmony_ci                } else if (*str == '"') {
42953a5a1b3Sopenharmony_ci                    str = parse_string(str, o);
43053a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
43153a5a1b3Sopenharmony_ci                } else if (is_digit(*str) || *str == '-') {
43253a5a1b3Sopenharmony_ci                    str = parse_number(str, o);
43353a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
43453a5a1b3Sopenharmony_ci                } else if (*str == '{') {
43553a5a1b3Sopenharmony_ci                    str = parse_object(str, o, depth);
43653a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
43753a5a1b3Sopenharmony_ci                } else if (*str == '[') {
43853a5a1b3Sopenharmony_ci                    str = parse_array(str, o, depth);
43953a5a1b3Sopenharmony_ci                    state = JSON_PARSER_STATE_FINISH;
44053a5a1b3Sopenharmony_ci                } else {
44153a5a1b3Sopenharmony_ci                    pa_log("Invalid JSON string: %s", str);
44253a5a1b3Sopenharmony_ci                    goto error;
44353a5a1b3Sopenharmony_ci                }
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci                if (!str)
44653a5a1b3Sopenharmony_ci                    goto error;
44753a5a1b3Sopenharmony_ci
44853a5a1b3Sopenharmony_ci                break;
44953a5a1b3Sopenharmony_ci
45053a5a1b3Sopenharmony_ci            case JSON_PARSER_STATE_FINISH:
45153a5a1b3Sopenharmony_ci                /* Consume trailing whitespaces */
45253a5a1b3Sopenharmony_ci                if (is_whitespace(*str)) {
45353a5a1b3Sopenharmony_ci                    str++;
45453a5a1b3Sopenharmony_ci                } else {
45553a5a1b3Sopenharmony_ci                    goto error;
45653a5a1b3Sopenharmony_ci                }
45753a5a1b3Sopenharmony_ci        }
45853a5a1b3Sopenharmony_ci    }
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
46153a5a1b3Sopenharmony_ci        /* We didn't actually get any data */
46253a5a1b3Sopenharmony_ci        pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
46353a5a1b3Sopenharmony_ci        goto error;
46453a5a1b3Sopenharmony_ci    }
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    *obj = o;
46753a5a1b3Sopenharmony_ci
46853a5a1b3Sopenharmony_ci    return str;
46953a5a1b3Sopenharmony_ci
47053a5a1b3Sopenharmony_cierror:
47153a5a1b3Sopenharmony_ci    pa_json_object_free(o);
47253a5a1b3Sopenharmony_ci    return NULL;
47353a5a1b3Sopenharmony_ci}
47453a5a1b3Sopenharmony_ci
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_cipa_json_object* pa_json_parse(const char *str) {
47753a5a1b3Sopenharmony_ci    pa_json_object *obj;
47853a5a1b3Sopenharmony_ci
47953a5a1b3Sopenharmony_ci    str = parse_value(str, NULL, &obj, 0);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    if (!str) {
48253a5a1b3Sopenharmony_ci        pa_log("JSON parsing failed");
48353a5a1b3Sopenharmony_ci        return NULL;
48453a5a1b3Sopenharmony_ci    }
48553a5a1b3Sopenharmony_ci
48653a5a1b3Sopenharmony_ci    if (*str != '\0') {
48753a5a1b3Sopenharmony_ci        pa_log("Unable to parse complete JSON string, remainder is: %s", str);
48853a5a1b3Sopenharmony_ci        pa_json_object_free(obj);
48953a5a1b3Sopenharmony_ci        return NULL;
49053a5a1b3Sopenharmony_ci    }
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci    return obj;
49353a5a1b3Sopenharmony_ci}
49453a5a1b3Sopenharmony_ci
49553a5a1b3Sopenharmony_cipa_json_type pa_json_object_get_type(const pa_json_object *obj) {
49653a5a1b3Sopenharmony_ci    return obj->type;
49753a5a1b3Sopenharmony_ci}
49853a5a1b3Sopenharmony_ci
49953a5a1b3Sopenharmony_civoid pa_json_object_free(pa_json_object *obj) {
50053a5a1b3Sopenharmony_ci
50153a5a1b3Sopenharmony_ci    switch (pa_json_object_get_type(obj)) {
50253a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_INIT:
50353a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_INT:
50453a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_DOUBLE:
50553a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_BOOL:
50653a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_NULL:
50753a5a1b3Sopenharmony_ci            break;
50853a5a1b3Sopenharmony_ci
50953a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_STRING:
51053a5a1b3Sopenharmony_ci            pa_xfree(obj->string_value);
51153a5a1b3Sopenharmony_ci            break;
51253a5a1b3Sopenharmony_ci
51353a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_OBJECT:
51453a5a1b3Sopenharmony_ci            pa_hashmap_free(obj->object_values);
51553a5a1b3Sopenharmony_ci            break;
51653a5a1b3Sopenharmony_ci
51753a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_ARRAY:
51853a5a1b3Sopenharmony_ci            pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
51953a5a1b3Sopenharmony_ci            break;
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci        default:
52253a5a1b3Sopenharmony_ci            pa_assert_not_reached();
52353a5a1b3Sopenharmony_ci    }
52453a5a1b3Sopenharmony_ci
52553a5a1b3Sopenharmony_ci    pa_xfree(obj);
52653a5a1b3Sopenharmony_ci}
52753a5a1b3Sopenharmony_ci
52853a5a1b3Sopenharmony_ciint64_t pa_json_object_get_int(const pa_json_object *o) {
52953a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
53053a5a1b3Sopenharmony_ci    return o->int_value;
53153a5a1b3Sopenharmony_ci}
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_cidouble pa_json_object_get_double(const pa_json_object *o) {
53453a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
53553a5a1b3Sopenharmony_ci    return o->double_value;
53653a5a1b3Sopenharmony_ci}
53753a5a1b3Sopenharmony_ci
53853a5a1b3Sopenharmony_cibool pa_json_object_get_bool(const pa_json_object *o) {
53953a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
54053a5a1b3Sopenharmony_ci    return o->bool_value;
54153a5a1b3Sopenharmony_ci}
54253a5a1b3Sopenharmony_ci
54353a5a1b3Sopenharmony_ciconst char* pa_json_object_get_string(const pa_json_object *o) {
54453a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
54553a5a1b3Sopenharmony_ci    return o->string_value;
54653a5a1b3Sopenharmony_ci}
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_ciconst pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
54953a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
55053a5a1b3Sopenharmony_ci    return pa_hashmap_get(o->object_values, name);
55153a5a1b3Sopenharmony_ci}
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ciconst pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o) {
55453a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
55553a5a1b3Sopenharmony_ci    return o->object_values;
55653a5a1b3Sopenharmony_ci}
55753a5a1b3Sopenharmony_ci
55853a5a1b3Sopenharmony_ciint pa_json_object_get_array_length(const pa_json_object *o) {
55953a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
56053a5a1b3Sopenharmony_ci    return pa_idxset_size(o->array_values);
56153a5a1b3Sopenharmony_ci}
56253a5a1b3Sopenharmony_ci
56353a5a1b3Sopenharmony_ciconst pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
56453a5a1b3Sopenharmony_ci    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
56553a5a1b3Sopenharmony_ci    return pa_idxset_get_by_index(o->array_values, index);
56653a5a1b3Sopenharmony_ci}
56753a5a1b3Sopenharmony_ci
56853a5a1b3Sopenharmony_cibool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
56953a5a1b3Sopenharmony_ci    int i;
57053a5a1b3Sopenharmony_ci
57153a5a1b3Sopenharmony_ci    if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
57253a5a1b3Sopenharmony_ci        return false;
57353a5a1b3Sopenharmony_ci
57453a5a1b3Sopenharmony_ci    switch (pa_json_object_get_type(o1)) {
57553a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_NULL:
57653a5a1b3Sopenharmony_ci            return true;
57753a5a1b3Sopenharmony_ci
57853a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_BOOL:
57953a5a1b3Sopenharmony_ci            return o1->bool_value == o2->bool_value;
58053a5a1b3Sopenharmony_ci
58153a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_INT:
58253a5a1b3Sopenharmony_ci            return o1->int_value == o2->int_value;
58353a5a1b3Sopenharmony_ci
58453a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_DOUBLE:
58553a5a1b3Sopenharmony_ci            return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
58653a5a1b3Sopenharmony_ci
58753a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_STRING:
58853a5a1b3Sopenharmony_ci            return pa_streq(o1->string_value, o2->string_value);
58953a5a1b3Sopenharmony_ci
59053a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_ARRAY:
59153a5a1b3Sopenharmony_ci            if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
59253a5a1b3Sopenharmony_ci                return false;
59353a5a1b3Sopenharmony_ci
59453a5a1b3Sopenharmony_ci            for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
59553a5a1b3Sopenharmony_ci                if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
59653a5a1b3Sopenharmony_ci                            pa_json_object_get_array_member(o2, i)))
59753a5a1b3Sopenharmony_ci                    return false;
59853a5a1b3Sopenharmony_ci            }
59953a5a1b3Sopenharmony_ci
60053a5a1b3Sopenharmony_ci            return true;
60153a5a1b3Sopenharmony_ci
60253a5a1b3Sopenharmony_ci        case PA_JSON_TYPE_OBJECT: {
60353a5a1b3Sopenharmony_ci            void *state;
60453a5a1b3Sopenharmony_ci            const char *key;
60553a5a1b3Sopenharmony_ci            const pa_json_object *v1, *v2;
60653a5a1b3Sopenharmony_ci
60753a5a1b3Sopenharmony_ci            if (pa_hashmap_size(o1->object_values) != pa_hashmap_size(o2->object_values))
60853a5a1b3Sopenharmony_ci                return false;
60953a5a1b3Sopenharmony_ci
61053a5a1b3Sopenharmony_ci            PA_HASHMAP_FOREACH_KV(key, v1, o1->object_values, state) {
61153a5a1b3Sopenharmony_ci                v2 = pa_json_object_get_object_member(o2, key);
61253a5a1b3Sopenharmony_ci                if (!v2 || !pa_json_object_equal(v1, v2))
61353a5a1b3Sopenharmony_ci                    return false;
61453a5a1b3Sopenharmony_ci            }
61553a5a1b3Sopenharmony_ci
61653a5a1b3Sopenharmony_ci            return true;
61753a5a1b3Sopenharmony_ci        }
61853a5a1b3Sopenharmony_ci
61953a5a1b3Sopenharmony_ci        default:
62053a5a1b3Sopenharmony_ci            pa_assert_not_reached();
62153a5a1b3Sopenharmony_ci    }
62253a5a1b3Sopenharmony_ci}
62353a5a1b3Sopenharmony_ci
62453a5a1b3Sopenharmony_ci/* Write functions. The functions are wrapper functions around pa_strbuf,
62553a5a1b3Sopenharmony_ci * so that the client does not need to use pa_strbuf directly. */
62653a5a1b3Sopenharmony_ci
62753a5a1b3Sopenharmony_cistatic void json_encoder_context_push(pa_json_encoder *encoder, pa_json_context_type_t type) {
62853a5a1b3Sopenharmony_ci    pa_assert(encoder);
62953a5a1b3Sopenharmony_ci
63053a5a1b3Sopenharmony_ci    encoder_context *head = pa_xnew0(encoder_context, 1);
63153a5a1b3Sopenharmony_ci    head->type = type;
63253a5a1b3Sopenharmony_ci    head->next = encoder->context;
63353a5a1b3Sopenharmony_ci    encoder->context = head;
63453a5a1b3Sopenharmony_ci}
63553a5a1b3Sopenharmony_ci
63653a5a1b3Sopenharmony_ci/* Returns type of context popped off encoder context stack. */
63753a5a1b3Sopenharmony_cistatic pa_json_context_type_t json_encoder_context_pop(pa_json_encoder *encoder) {
63853a5a1b3Sopenharmony_ci    encoder_context *head;
63953a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
64053a5a1b3Sopenharmony_ci
64153a5a1b3Sopenharmony_ci    pa_assert(encoder);
64253a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
64353a5a1b3Sopenharmony_ci
64453a5a1b3Sopenharmony_ci    type = encoder->context->type;
64553a5a1b3Sopenharmony_ci
64653a5a1b3Sopenharmony_ci    head = encoder->context->next;
64753a5a1b3Sopenharmony_ci    pa_xfree(encoder->context);
64853a5a1b3Sopenharmony_ci    encoder->context = head;
64953a5a1b3Sopenharmony_ci
65053a5a1b3Sopenharmony_ci    return type;
65153a5a1b3Sopenharmony_ci}
65253a5a1b3Sopenharmony_ci
65353a5a1b3Sopenharmony_cibool pa_json_encoder_is_empty(pa_json_encoder *encoder) {
65453a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
65553a5a1b3Sopenharmony_ci
65653a5a1b3Sopenharmony_ci    pa_assert(encoder);
65753a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
65853a5a1b3Sopenharmony_ci
65953a5a1b3Sopenharmony_ci    type = encoder->context->type;
66053a5a1b3Sopenharmony_ci    return type == PA_JSON_CONTEXT_EMPTY;
66153a5a1b3Sopenharmony_ci}
66253a5a1b3Sopenharmony_ci
66353a5a1b3Sopenharmony_cipa_json_encoder *pa_json_encoder_new(void) {
66453a5a1b3Sopenharmony_ci    pa_json_encoder *encoder;
66553a5a1b3Sopenharmony_ci
66653a5a1b3Sopenharmony_ci    encoder = pa_xnew(pa_json_encoder, 1);
66753a5a1b3Sopenharmony_ci    encoder->buffer = pa_strbuf_new();
66853a5a1b3Sopenharmony_ci
66953a5a1b3Sopenharmony_ci    encoder->context = NULL;
67053a5a1b3Sopenharmony_ci    json_encoder_context_push(encoder, PA_JSON_CONTEXT_EMPTY);
67153a5a1b3Sopenharmony_ci
67253a5a1b3Sopenharmony_ci    return encoder;
67353a5a1b3Sopenharmony_ci}
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_civoid pa_json_encoder_free(pa_json_encoder *encoder) {
67653a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
67753a5a1b3Sopenharmony_ci    pa_assert(encoder);
67853a5a1b3Sopenharmony_ci
67953a5a1b3Sopenharmony_ci    /* should have exactly one encoder context left at this point */
68053a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
68153a5a1b3Sopenharmony_ci    type = json_encoder_context_pop(encoder);
68253a5a1b3Sopenharmony_ci    pa_assert(encoder->context == NULL);
68353a5a1b3Sopenharmony_ci
68453a5a1b3Sopenharmony_ci    pa_assert(type == PA_JSON_CONTEXT_TOP || type == PA_JSON_CONTEXT_EMPTY);
68553a5a1b3Sopenharmony_ci    if (type == PA_JSON_CONTEXT_EMPTY)
68653a5a1b3Sopenharmony_ci        pa_log_warn("JSON encoder is empty.");
68753a5a1b3Sopenharmony_ci
68853a5a1b3Sopenharmony_ci    if (encoder->buffer)
68953a5a1b3Sopenharmony_ci        pa_strbuf_free(encoder->buffer);
69053a5a1b3Sopenharmony_ci
69153a5a1b3Sopenharmony_ci    pa_xfree(encoder);
69253a5a1b3Sopenharmony_ci}
69353a5a1b3Sopenharmony_ci
69453a5a1b3Sopenharmony_cichar *pa_json_encoder_to_string_free(pa_json_encoder *encoder) {
69553a5a1b3Sopenharmony_ci    char *result;
69653a5a1b3Sopenharmony_ci
69753a5a1b3Sopenharmony_ci    pa_assert(encoder);
69853a5a1b3Sopenharmony_ci
69953a5a1b3Sopenharmony_ci    result = pa_strbuf_to_string_free(encoder->buffer);
70053a5a1b3Sopenharmony_ci
70153a5a1b3Sopenharmony_ci    encoder->buffer = NULL;
70253a5a1b3Sopenharmony_ci    pa_json_encoder_free(encoder);
70353a5a1b3Sopenharmony_ci
70453a5a1b3Sopenharmony_ci    return result;
70553a5a1b3Sopenharmony_ci}
70653a5a1b3Sopenharmony_ci
70753a5a1b3Sopenharmony_cistatic void json_encoder_insert_delimiter(pa_json_encoder *encoder) {
70853a5a1b3Sopenharmony_ci    pa_assert(encoder);
70953a5a1b3Sopenharmony_ci
71053a5a1b3Sopenharmony_ci    if (encoder->context->counter++)
71153a5a1b3Sopenharmony_ci        pa_strbuf_putc(encoder->buffer, ',');
71253a5a1b3Sopenharmony_ci}
71353a5a1b3Sopenharmony_ci
71453a5a1b3Sopenharmony_ci/* Escapes p to create valid JSON string.
71553a5a1b3Sopenharmony_ci * The caller has to free the returned string. */
71653a5a1b3Sopenharmony_cistatic char *pa_json_escape(const char *p) {
71753a5a1b3Sopenharmony_ci    const char *s;
71853a5a1b3Sopenharmony_ci    char *out_string, *output;
71953a5a1b3Sopenharmony_ci    int char_count = strlen(p);
72053a5a1b3Sopenharmony_ci
72153a5a1b3Sopenharmony_ci    /* Maximum number of characters in output string
72253a5a1b3Sopenharmony_ci     * including trailing 0. */
72353a5a1b3Sopenharmony_ci    char_count = 2 * char_count + 1;
72453a5a1b3Sopenharmony_ci
72553a5a1b3Sopenharmony_ci    /* allocate output string */
72653a5a1b3Sopenharmony_ci    out_string = pa_xmalloc(char_count);
72753a5a1b3Sopenharmony_ci    output = out_string;
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci    /* write output string */
73053a5a1b3Sopenharmony_ci    for (s = p; *s; ++s) {
73153a5a1b3Sopenharmony_ci        switch (*s) {
73253a5a1b3Sopenharmony_ci            case '"':
73353a5a1b3Sopenharmony_ci                *output++ = '\\';
73453a5a1b3Sopenharmony_ci                *output++ = '"';
73553a5a1b3Sopenharmony_ci                break;
73653a5a1b3Sopenharmony_ci            case '\\':
73753a5a1b3Sopenharmony_ci                *output++ = '\\';
73853a5a1b3Sopenharmony_ci                *output++ = '\\';
73953a5a1b3Sopenharmony_ci                break;
74053a5a1b3Sopenharmony_ci            case '\b':
74153a5a1b3Sopenharmony_ci                *output++ = '\\';
74253a5a1b3Sopenharmony_ci                *output++ = 'b';
74353a5a1b3Sopenharmony_ci                break;
74453a5a1b3Sopenharmony_ci
74553a5a1b3Sopenharmony_ci            case '\f':
74653a5a1b3Sopenharmony_ci                *output++ = '\\';
74753a5a1b3Sopenharmony_ci                *output++ = 'f';
74853a5a1b3Sopenharmony_ci                break;
74953a5a1b3Sopenharmony_ci
75053a5a1b3Sopenharmony_ci            case '\n':
75153a5a1b3Sopenharmony_ci                *output++ = '\\';
75253a5a1b3Sopenharmony_ci                *output++ = 'n';
75353a5a1b3Sopenharmony_ci                break;
75453a5a1b3Sopenharmony_ci
75553a5a1b3Sopenharmony_ci            case '\r':
75653a5a1b3Sopenharmony_ci                *output++ = '\\';
75753a5a1b3Sopenharmony_ci                *output++ = 'r';
75853a5a1b3Sopenharmony_ci                break;
75953a5a1b3Sopenharmony_ci
76053a5a1b3Sopenharmony_ci            case '\t':
76153a5a1b3Sopenharmony_ci                *output++ = '\\';
76253a5a1b3Sopenharmony_ci                *output++ = 't';
76353a5a1b3Sopenharmony_ci                break;
76453a5a1b3Sopenharmony_ci            default:
76553a5a1b3Sopenharmony_ci                if (*s < 0x20 || *s > 0x7E) {
76653a5a1b3Sopenharmony_ci                    pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *s);
76753a5a1b3Sopenharmony_ci                    pa_xfree(out_string);
76853a5a1b3Sopenharmony_ci                    return NULL;
76953a5a1b3Sopenharmony_ci                }
77053a5a1b3Sopenharmony_ci                *output++ = *s;
77153a5a1b3Sopenharmony_ci                break;
77253a5a1b3Sopenharmony_ci        }
77353a5a1b3Sopenharmony_ci    }
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_ci    *output = 0;
77653a5a1b3Sopenharmony_ci
77753a5a1b3Sopenharmony_ci    return out_string;
77853a5a1b3Sopenharmony_ci}
77953a5a1b3Sopenharmony_ci
78053a5a1b3Sopenharmony_cistatic void json_write_string_escaped(pa_json_encoder *encoder, const char *value) {
78153a5a1b3Sopenharmony_ci    char *escaped_value;
78253a5a1b3Sopenharmony_ci
78353a5a1b3Sopenharmony_ci    pa_assert(encoder);
78453a5a1b3Sopenharmony_ci
78553a5a1b3Sopenharmony_ci    escaped_value = pa_json_escape(value);
78653a5a1b3Sopenharmony_ci    pa_strbuf_printf(encoder->buffer, "\"%s\"", escaped_value);
78753a5a1b3Sopenharmony_ci    pa_xfree(escaped_value);
78853a5a1b3Sopenharmony_ci}
78953a5a1b3Sopenharmony_ci
79053a5a1b3Sopenharmony_ci/* Writes an opening curly brace */
79153a5a1b3Sopenharmony_civoid pa_json_encoder_begin_element_object(pa_json_encoder *encoder) {
79253a5a1b3Sopenharmony_ci    pa_assert(encoder);
79353a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
79453a5a1b3Sopenharmony_ci
79553a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
79653a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
79753a5a1b3Sopenharmony_ci
79853a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
79953a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, '{');
80053a5a1b3Sopenharmony_ci
80153a5a1b3Sopenharmony_ci    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
80253a5a1b3Sopenharmony_ci}
80353a5a1b3Sopenharmony_ci
80453a5a1b3Sopenharmony_ci/* Writes an opening curly brace */
80553a5a1b3Sopenharmony_civoid pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name) {
80653a5a1b3Sopenharmony_ci    pa_assert(encoder);
80753a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
80853a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
80953a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
81053a5a1b3Sopenharmony_ci
81153a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
81253a5a1b3Sopenharmony_ci
81353a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
81453a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, '{');
81753a5a1b3Sopenharmony_ci
81853a5a1b3Sopenharmony_ci    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
81953a5a1b3Sopenharmony_ci}
82053a5a1b3Sopenharmony_ci
82153a5a1b3Sopenharmony_ci/* Writes a closing curly brace */
82253a5a1b3Sopenharmony_civoid pa_json_encoder_end_object(pa_json_encoder *encoder) {
82353a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
82453a5a1b3Sopenharmony_ci    pa_assert(encoder);
82553a5a1b3Sopenharmony_ci
82653a5a1b3Sopenharmony_ci    type = json_encoder_context_pop(encoder);
82753a5a1b3Sopenharmony_ci    pa_assert(type == PA_JSON_CONTEXT_OBJECT);
82853a5a1b3Sopenharmony_ci
82953a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, '}');
83053a5a1b3Sopenharmony_ci}
83153a5a1b3Sopenharmony_ci
83253a5a1b3Sopenharmony_ci/* Writes an opening bracket */
83353a5a1b3Sopenharmony_civoid pa_json_encoder_begin_element_array(pa_json_encoder *encoder) {
83453a5a1b3Sopenharmony_ci    pa_assert(encoder);
83553a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
83653a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
83753a5a1b3Sopenharmony_ci
83853a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
83953a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
84053a5a1b3Sopenharmony_ci
84153a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
84253a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, '[');
84353a5a1b3Sopenharmony_ci
84453a5a1b3Sopenharmony_ci    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
84553a5a1b3Sopenharmony_ci}
84653a5a1b3Sopenharmony_ci
84753a5a1b3Sopenharmony_ci/* Writes member name and an opening bracket */
84853a5a1b3Sopenharmony_civoid pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name) {
84953a5a1b3Sopenharmony_ci    pa_assert(encoder);
85053a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
85153a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
85253a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
85353a5a1b3Sopenharmony_ci
85453a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
85553a5a1b3Sopenharmony_ci
85653a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
85753a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
85853a5a1b3Sopenharmony_ci
85953a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, '[');
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
86253a5a1b3Sopenharmony_ci}
86353a5a1b3Sopenharmony_ci
86453a5a1b3Sopenharmony_ci/* Writes a closing bracket */
86553a5a1b3Sopenharmony_civoid pa_json_encoder_end_array(pa_json_encoder *encoder) {
86653a5a1b3Sopenharmony_ci    pa_json_context_type_t type;
86753a5a1b3Sopenharmony_ci    pa_assert(encoder);
86853a5a1b3Sopenharmony_ci
86953a5a1b3Sopenharmony_ci    type = json_encoder_context_pop(encoder);
87053a5a1b3Sopenharmony_ci    pa_assert(type == PA_JSON_CONTEXT_ARRAY);
87153a5a1b3Sopenharmony_ci
87253a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ']');
87353a5a1b3Sopenharmony_ci}
87453a5a1b3Sopenharmony_ci
87553a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value) {
87653a5a1b3Sopenharmony_ci    pa_assert(encoder);
87753a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
87853a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
87953a5a1b3Sopenharmony_ci
88053a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
88153a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
88253a5a1b3Sopenharmony_ci
88353a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
88453a5a1b3Sopenharmony_ci
88553a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, value);
88653a5a1b3Sopenharmony_ci}
88753a5a1b3Sopenharmony_ci
88853a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value) {
88953a5a1b3Sopenharmony_ci    pa_assert(encoder);
89053a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
89153a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
89253a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
89353a5a1b3Sopenharmony_ci
89453a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
89553a5a1b3Sopenharmony_ci
89653a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
89753a5a1b3Sopenharmony_ci
89853a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
89953a5a1b3Sopenharmony_ci
90053a5a1b3Sopenharmony_ci    /* Null value is written as empty element */
90153a5a1b3Sopenharmony_ci    if (!value)
90253a5a1b3Sopenharmony_ci        value = "";
90353a5a1b3Sopenharmony_ci
90453a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, value);
90553a5a1b3Sopenharmony_ci}
90653a5a1b3Sopenharmony_ci
90753a5a1b3Sopenharmony_cistatic void json_write_null(pa_json_encoder *encoder) {
90853a5a1b3Sopenharmony_ci    pa_assert(encoder);
90953a5a1b3Sopenharmony_ci
91053a5a1b3Sopenharmony_ci    pa_strbuf_puts(encoder->buffer, "null");
91153a5a1b3Sopenharmony_ci}
91253a5a1b3Sopenharmony_ci
91353a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_null(pa_json_encoder *encoder) {
91453a5a1b3Sopenharmony_ci    pa_assert(encoder);
91553a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
91653a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
91753a5a1b3Sopenharmony_ci
91853a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
91953a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
92053a5a1b3Sopenharmony_ci
92153a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
92253a5a1b3Sopenharmony_ci
92353a5a1b3Sopenharmony_ci    json_write_null(encoder);
92453a5a1b3Sopenharmony_ci}
92553a5a1b3Sopenharmony_ci
92653a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name) {
92753a5a1b3Sopenharmony_ci    pa_assert(encoder);
92853a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
92953a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
93053a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
93153a5a1b3Sopenharmony_ci
93253a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
93353a5a1b3Sopenharmony_ci
93453a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
93553a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
93653a5a1b3Sopenharmony_ci
93753a5a1b3Sopenharmony_ci    json_write_null(encoder);
93853a5a1b3Sopenharmony_ci}
93953a5a1b3Sopenharmony_ci
94053a5a1b3Sopenharmony_cistatic void json_write_bool(pa_json_encoder *encoder, bool value) {
94153a5a1b3Sopenharmony_ci    pa_assert(encoder);
94253a5a1b3Sopenharmony_ci
94353a5a1b3Sopenharmony_ci    pa_strbuf_puts(encoder->buffer, value ? "true" : "false");
94453a5a1b3Sopenharmony_ci}
94553a5a1b3Sopenharmony_ci
94653a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value) {
94753a5a1b3Sopenharmony_ci    pa_assert(encoder);
94853a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
94953a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
95053a5a1b3Sopenharmony_ci
95153a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
95253a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
95353a5a1b3Sopenharmony_ci
95453a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
95553a5a1b3Sopenharmony_ci
95653a5a1b3Sopenharmony_ci    json_write_bool(encoder, value);
95753a5a1b3Sopenharmony_ci}
95853a5a1b3Sopenharmony_ci
95953a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value) {
96053a5a1b3Sopenharmony_ci    pa_assert(encoder);
96153a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
96253a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
96353a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
96453a5a1b3Sopenharmony_ci
96553a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
96653a5a1b3Sopenharmony_ci
96753a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
96853a5a1b3Sopenharmony_ci
96953a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
97053a5a1b3Sopenharmony_ci
97153a5a1b3Sopenharmony_ci    json_write_bool(encoder, value);
97253a5a1b3Sopenharmony_ci}
97353a5a1b3Sopenharmony_ci
97453a5a1b3Sopenharmony_cistatic void json_write_int(pa_json_encoder *encoder, int64_t value) {
97553a5a1b3Sopenharmony_ci    pa_assert(encoder);
97653a5a1b3Sopenharmony_ci
97753a5a1b3Sopenharmony_ci    pa_strbuf_printf(encoder->buffer, "%"PRId64, value);
97853a5a1b3Sopenharmony_ci}
97953a5a1b3Sopenharmony_ci
98053a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value) {
98153a5a1b3Sopenharmony_ci    pa_assert(encoder);
98253a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
98353a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
98453a5a1b3Sopenharmony_ci
98553a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
98653a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
98753a5a1b3Sopenharmony_ci
98853a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
98953a5a1b3Sopenharmony_ci
99053a5a1b3Sopenharmony_ci    json_write_int(encoder, value);
99153a5a1b3Sopenharmony_ci}
99253a5a1b3Sopenharmony_ci
99353a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value) {
99453a5a1b3Sopenharmony_ci    pa_assert(encoder);
99553a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
99653a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
99753a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
99853a5a1b3Sopenharmony_ci
99953a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
100053a5a1b3Sopenharmony_ci
100153a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
100253a5a1b3Sopenharmony_ci
100353a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
100453a5a1b3Sopenharmony_ci
100553a5a1b3Sopenharmony_ci    json_write_int(encoder, value);
100653a5a1b3Sopenharmony_ci}
100753a5a1b3Sopenharmony_ci
100853a5a1b3Sopenharmony_cistatic void json_write_double(pa_json_encoder *encoder, double value, int precision) {
100953a5a1b3Sopenharmony_ci    pa_assert(encoder);
101053a5a1b3Sopenharmony_ci    pa_strbuf_printf(encoder->buffer, "%.*f",  precision, value);
101153a5a1b3Sopenharmony_ci}
101253a5a1b3Sopenharmony_ci
101353a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision) {
101453a5a1b3Sopenharmony_ci    pa_assert(encoder);
101553a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
101653a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
101753a5a1b3Sopenharmony_ci
101853a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
101953a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
102053a5a1b3Sopenharmony_ci
102153a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
102253a5a1b3Sopenharmony_ci
102353a5a1b3Sopenharmony_ci    json_write_double(encoder, value, precision);
102453a5a1b3Sopenharmony_ci}
102553a5a1b3Sopenharmony_ci
102653a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision) {
102753a5a1b3Sopenharmony_ci    pa_assert(encoder);
102853a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
102953a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
103053a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
103153a5a1b3Sopenharmony_ci
103253a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
103353a5a1b3Sopenharmony_ci
103453a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
103553a5a1b3Sopenharmony_ci
103653a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
103753a5a1b3Sopenharmony_ci
103853a5a1b3Sopenharmony_ci    json_write_double(encoder, value, precision);
103953a5a1b3Sopenharmony_ci}
104053a5a1b3Sopenharmony_ci
104153a5a1b3Sopenharmony_cistatic void json_write_raw(pa_json_encoder *encoder, const char *raw_string) {
104253a5a1b3Sopenharmony_ci    pa_assert(encoder);
104353a5a1b3Sopenharmony_ci    pa_strbuf_puts(encoder->buffer, raw_string);
104453a5a1b3Sopenharmony_ci}
104553a5a1b3Sopenharmony_ci
104653a5a1b3Sopenharmony_civoid pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string) {
104753a5a1b3Sopenharmony_ci    pa_assert(encoder);
104853a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
104953a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
105053a5a1b3Sopenharmony_ci
105153a5a1b3Sopenharmony_ci    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
105253a5a1b3Sopenharmony_ci        encoder->context->type = PA_JSON_CONTEXT_TOP;
105353a5a1b3Sopenharmony_ci
105453a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
105553a5a1b3Sopenharmony_ci
105653a5a1b3Sopenharmony_ci    json_write_raw(encoder, raw_json_string);
105753a5a1b3Sopenharmony_ci}
105853a5a1b3Sopenharmony_ci
105953a5a1b3Sopenharmony_civoid pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string) {
106053a5a1b3Sopenharmony_ci    pa_assert(encoder);
106153a5a1b3Sopenharmony_ci    pa_assert(encoder->context);
106253a5a1b3Sopenharmony_ci    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
106353a5a1b3Sopenharmony_ci    pa_assert(name && name[0]);
106453a5a1b3Sopenharmony_ci
106553a5a1b3Sopenharmony_ci    json_encoder_insert_delimiter(encoder);
106653a5a1b3Sopenharmony_ci
106753a5a1b3Sopenharmony_ci    json_write_string_escaped(encoder, name);
106853a5a1b3Sopenharmony_ci
106953a5a1b3Sopenharmony_ci    pa_strbuf_putc(encoder->buffer, ':');
107053a5a1b3Sopenharmony_ci
107153a5a1b3Sopenharmony_ci    json_write_raw(encoder, raw_json_string);
107253a5a1b3Sopenharmony_ci}
1073