xref: /third_party/pulseaudio/src/pulsecore/json.c (revision 53a5a1b3)
1/***
2  This file is part of PulseAudio.
3
4  Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <math.h>
25
26#include <pulse/xmalloc.h>
27#include <pulsecore/core-util.h>
28#include <pulsecore/hashmap.h>
29#include <pulsecore/json.h>
30#include <pulsecore/strbuf.h>
31
32#define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
33
34struct pa_json_object {
35    pa_json_type type;
36
37    union {
38        int64_t int_value;
39        double double_value;
40        bool bool_value;
41        char *string_value;
42        pa_hashmap *object_values; /* name -> object */
43        pa_idxset *array_values; /* objects */
44    };
45};
46
47/* JSON encoder context type */
48typedef enum pa_json_context_type {
49    /* Top-level context of empty encoder. JSON element can be added. */
50    PA_JSON_CONTEXT_EMPTY  = 0,
51    /* Top-level context of encoder with an element. JSON element cannot be added. */
52    PA_JSON_CONTEXT_TOP    = 1,
53    /* JSON array context. JSON elements can be added. */
54    PA_JSON_CONTEXT_ARRAY  = 2,
55    /* JSON object context. JSON object members can be added. */
56    PA_JSON_CONTEXT_OBJECT = 3,
57} pa_json_context_type_t;
58
59typedef struct encoder_context {
60    pa_json_context_type_t type;
61    int counter;
62    struct encoder_context *next;
63} encoder_context;
64
65/* JSON encoder structure, a wrapper for pa_strbuf and encoder context */
66struct pa_json_encoder {
67    pa_strbuf *buffer;
68    encoder_context *context;
69};
70
71static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
72
73static pa_json_object* json_object_new(void) {
74    pa_json_object *obj;
75
76    obj = pa_xnew0(pa_json_object, 1);
77
78    return obj;
79}
80
81static bool is_whitespace(char c) {
82    return c == '\t' || c == '\n' || c == '\r' || c == ' ';
83}
84
85static bool is_digit(char c) {
86    return c >= '0' && c <= '9';
87}
88
89static bool is_end(const char c, const char *end) {
90    if (!end)
91        return c == '\0';
92    else  {
93        while (*end) {
94            if (c == *end)
95                return true;
96            end++;
97        }
98    }
99
100    return false;
101}
102
103static const char* consume_string(const char *str, const char *expect) {
104    while (*expect) {
105        if (*str != *expect)
106            return NULL;
107
108        str++;
109        expect++;
110    }
111
112    return str;
113}
114
115static const char* parse_null(const char *str, pa_json_object *obj) {
116    str = consume_string(str, "null");
117
118    if (str)
119        obj->type = PA_JSON_TYPE_NULL;
120
121    return str;
122}
123
124static const char* parse_boolean(const char *str, pa_json_object *obj) {
125    const char *tmp;
126
127    tmp = consume_string(str, "true");
128
129    if (tmp) {
130        obj->type = PA_JSON_TYPE_BOOL;
131        obj->bool_value = true;
132    } else {
133        tmp = consume_string(str, "false");
134
135        if (str) {
136            obj->type = PA_JSON_TYPE_BOOL;
137            obj->bool_value = false;
138        }
139    }
140
141    return tmp;
142}
143
144static const char* parse_string(const char *str, pa_json_object *obj) {
145    pa_strbuf *buf = pa_strbuf_new();
146
147    str++; /* Consume leading '"' */
148
149    while (*str && *str != '"') {
150        if (*str != '\\') {
151            /* We only accept ASCII printable characters. */
152            if (*str < 0x20 || *str > 0x7E) {
153                pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
154                goto error;
155            }
156
157            /* Normal character, juts consume */
158            pa_strbuf_putc(buf, *str);
159        } else {
160            /* Need to unescape */
161            str++;
162
163            switch (*str) {
164                case '"':
165                case '\\':
166                case '/':
167                    pa_strbuf_putc(buf, *str);
168                    break;
169
170                case 'b':
171                    pa_strbuf_putc(buf, '\b' /* backspace */);
172                    break;
173
174                case 'f':
175                    pa_strbuf_putc(buf, '\f' /* form feed */);
176                    break;
177
178                case 'n':
179                    pa_strbuf_putc(buf, '\n' /* new line */);
180                    break;
181
182                case 'r':
183                    pa_strbuf_putc(buf, '\r' /* carriage return */);
184                    break;
185
186                case 't':
187                    pa_strbuf_putc(buf, '\t' /* horizontal tab */);
188                    break;
189
190                case 'u':
191                    pa_log("Unicode code points are currently unsupported");
192                    goto error;
193
194                default:
195                    pa_log("Unexpected escape value: %c", *str);
196                    goto error;
197            }
198        }
199
200        str++;
201    }
202
203    if (*str != '"') {
204        pa_log("Failed to parse remainder of string: %s", str);
205        goto error;
206    }
207
208    str++;
209
210    obj->type = PA_JSON_TYPE_STRING;
211    obj->string_value = pa_strbuf_to_string_free(buf);
212
213    return str;
214
215error:
216    pa_strbuf_free(buf);
217    return NULL;
218}
219
220static const char* parse_number(const char *str, pa_json_object *obj) {
221    bool has_fraction = false, has_exponent = false, valid = false;
222    char *candidate = NULL;
223    const char *s = str;
224
225    if (*s == '-')
226        s++;
227
228    if (*s == '0') {
229        valid = true;
230        s++;
231        goto fraction;
232    }
233
234    while (is_digit(*s)) {
235        valid = true;
236        s++;
237    }
238
239fraction:
240
241    if (!valid) {
242        pa_log("Missing digits while parsing number");
243        goto error;
244    }
245
246    if (*s == '.') {
247        has_fraction = true;
248        s++;
249        valid = false;
250
251        while (is_digit(*s)) {
252            valid = true;
253            s++;
254        }
255
256        if (!valid) {
257            pa_log("No digit after '.' while parsing fraction");
258            goto error;
259        }
260    }
261
262    if (*s == 'e' || *s == 'E') {
263        has_exponent = true;
264        s++;
265        valid = false;
266
267        if (*s == '-' || *s == '+')
268            s++;
269
270        while (is_digit(*s)) {
271            valid = true;
272            s++;
273        }
274
275        if (!valid) {
276            pa_log("No digit in exponent while parsing fraction");
277            goto error;
278        }
279    }
280
281    /* Number format looks good, now try to extract the value.
282     * Here 's' points just after the string which will be consumed. */
283
284    candidate = pa_xstrndup(str, s - str);
285
286    if (has_fraction || has_exponent) {
287        if (pa_atod(candidate, &obj->double_value) < 0) {
288            pa_log("Cannot convert string '%s' to double value", str);
289            goto error;
290        }
291        obj->type = PA_JSON_TYPE_DOUBLE;
292    } else {
293        if (pa_atoi64(candidate, &obj->int_value) < 0) {
294            pa_log("Cannot convert string '%s' to int64_t value", str);
295            goto error;
296        }
297        obj->type = PA_JSON_TYPE_INT;
298    }
299
300    pa_xfree(candidate);
301
302    return s;
303
304error:
305    pa_xfree(candidate);
306    return NULL;
307}
308
309static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
310    pa_json_object *name = NULL, *value = NULL;
311
312    obj->object_values = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
313                                             pa_xfree, (pa_free_cb_t) pa_json_object_free);
314
315    while (*str != '}') {
316        str++; /* Consume leading '{' or ',' */
317
318        str = parse_value(str, ":", &name, depth + 1);
319        if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
320            pa_log("Could not parse key for object");
321            goto error;
322        }
323
324        /* Consume the ':' */
325        str++;
326
327        str = parse_value(str, ",}", &value, depth + 1);
328        if (!str) {
329            pa_log("Could not parse value for object");
330            goto error;
331        }
332
333        pa_hashmap_put(obj->object_values, pa_xstrdup(pa_json_object_get_string(name)), value);
334        pa_json_object_free(name);
335
336        name = NULL;
337        value = NULL;
338    }
339
340    /* Drop trailing '}' */
341    str++;
342
343    /* We now know the value was correctly parsed */
344    obj->type = PA_JSON_TYPE_OBJECT;
345
346    return str;
347
348error:
349    pa_hashmap_free(obj->object_values);
350    obj->object_values = NULL;
351
352    if (name)
353        pa_json_object_free(name);
354    if (value)
355        pa_json_object_free(value);
356
357    return NULL;
358}
359
360static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
361    pa_json_object *value;
362
363    obj->array_values = pa_idxset_new(NULL, NULL);
364
365    while (*str != ']') {
366        str++; /* Consume leading '[' or ',' */
367
368        /* Need to chew up whitespaces as a special case to deal with the
369         * possibility of an empty array */
370        while (is_whitespace(*str))
371            str++;
372
373        if (*str == ']')
374            break;
375
376        str = parse_value(str, ",]", &value, depth + 1);
377        if (!str) {
378            pa_log("Could not parse value for array");
379            goto error;
380        }
381
382        pa_idxset_put(obj->array_values, value, NULL);
383    }
384
385    /* Drop trailing ']' */
386    str++;
387
388    /* We now know the value was correctly parsed */
389    obj->type = PA_JSON_TYPE_ARRAY;
390
391    return str;
392
393error:
394    pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
395    obj->array_values = NULL;
396    return NULL;
397}
398
399typedef enum {
400    JSON_PARSER_STATE_INIT,
401    JSON_PARSER_STATE_FINISH,
402} json_parser_state;
403
404static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
405    json_parser_state state = JSON_PARSER_STATE_INIT;
406    pa_json_object *o;
407
408    pa_assert(str != NULL);
409
410    o = json_object_new();
411
412    if (depth > MAX_NESTING_DEPTH) {
413        pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
414        goto error;
415    }
416
417    while (!is_end(*str, end)) {
418        switch (state) {
419            case JSON_PARSER_STATE_INIT:
420                if (is_whitespace(*str)) {
421                    str++;
422                } else if (*str == 'n') {
423                    str = parse_null(str, o);
424                    state = JSON_PARSER_STATE_FINISH;
425                } else if (*str == 't' || *str == 'f') {
426                    str = parse_boolean(str, o);
427                    state = JSON_PARSER_STATE_FINISH;
428                } else if (*str == '"') {
429                    str = parse_string(str, o);
430                    state = JSON_PARSER_STATE_FINISH;
431                } else if (is_digit(*str) || *str == '-') {
432                    str = parse_number(str, o);
433                    state = JSON_PARSER_STATE_FINISH;
434                } else if (*str == '{') {
435                    str = parse_object(str, o, depth);
436                    state = JSON_PARSER_STATE_FINISH;
437                } else if (*str == '[') {
438                    str = parse_array(str, o, depth);
439                    state = JSON_PARSER_STATE_FINISH;
440                } else {
441                    pa_log("Invalid JSON string: %s", str);
442                    goto error;
443                }
444
445                if (!str)
446                    goto error;
447
448                break;
449
450            case JSON_PARSER_STATE_FINISH:
451                /* Consume trailing whitespaces */
452                if (is_whitespace(*str)) {
453                    str++;
454                } else {
455                    goto error;
456                }
457        }
458    }
459
460    if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
461        /* We didn't actually get any data */
462        pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
463        goto error;
464    }
465
466    *obj = o;
467
468    return str;
469
470error:
471    pa_json_object_free(o);
472    return NULL;
473}
474
475
476pa_json_object* pa_json_parse(const char *str) {
477    pa_json_object *obj;
478
479    str = parse_value(str, NULL, &obj, 0);
480
481    if (!str) {
482        pa_log("JSON parsing failed");
483        return NULL;
484    }
485
486    if (*str != '\0') {
487        pa_log("Unable to parse complete JSON string, remainder is: %s", str);
488        pa_json_object_free(obj);
489        return NULL;
490    }
491
492    return obj;
493}
494
495pa_json_type pa_json_object_get_type(const pa_json_object *obj) {
496    return obj->type;
497}
498
499void pa_json_object_free(pa_json_object *obj) {
500
501    switch (pa_json_object_get_type(obj)) {
502        case PA_JSON_TYPE_INIT:
503        case PA_JSON_TYPE_INT:
504        case PA_JSON_TYPE_DOUBLE:
505        case PA_JSON_TYPE_BOOL:
506        case PA_JSON_TYPE_NULL:
507            break;
508
509        case PA_JSON_TYPE_STRING:
510            pa_xfree(obj->string_value);
511            break;
512
513        case PA_JSON_TYPE_OBJECT:
514            pa_hashmap_free(obj->object_values);
515            break;
516
517        case PA_JSON_TYPE_ARRAY:
518            pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
519            break;
520
521        default:
522            pa_assert_not_reached();
523    }
524
525    pa_xfree(obj);
526}
527
528int64_t pa_json_object_get_int(const pa_json_object *o) {
529    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
530    return o->int_value;
531}
532
533double pa_json_object_get_double(const pa_json_object *o) {
534    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
535    return o->double_value;
536}
537
538bool pa_json_object_get_bool(const pa_json_object *o) {
539    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
540    return o->bool_value;
541}
542
543const char* pa_json_object_get_string(const pa_json_object *o) {
544    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
545    return o->string_value;
546}
547
548const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
549    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
550    return pa_hashmap_get(o->object_values, name);
551}
552
553const pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o) {
554    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
555    return o->object_values;
556}
557
558int pa_json_object_get_array_length(const pa_json_object *o) {
559    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
560    return pa_idxset_size(o->array_values);
561}
562
563const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
564    pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
565    return pa_idxset_get_by_index(o->array_values, index);
566}
567
568bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
569    int i;
570
571    if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
572        return false;
573
574    switch (pa_json_object_get_type(o1)) {
575        case PA_JSON_TYPE_NULL:
576            return true;
577
578        case PA_JSON_TYPE_BOOL:
579            return o1->bool_value == o2->bool_value;
580
581        case PA_JSON_TYPE_INT:
582            return o1->int_value == o2->int_value;
583
584        case PA_JSON_TYPE_DOUBLE:
585            return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
586
587        case PA_JSON_TYPE_STRING:
588            return pa_streq(o1->string_value, o2->string_value);
589
590        case PA_JSON_TYPE_ARRAY:
591            if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
592                return false;
593
594            for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
595                if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
596                            pa_json_object_get_array_member(o2, i)))
597                    return false;
598            }
599
600            return true;
601
602        case PA_JSON_TYPE_OBJECT: {
603            void *state;
604            const char *key;
605            const pa_json_object *v1, *v2;
606
607            if (pa_hashmap_size(o1->object_values) != pa_hashmap_size(o2->object_values))
608                return false;
609
610            PA_HASHMAP_FOREACH_KV(key, v1, o1->object_values, state) {
611                v2 = pa_json_object_get_object_member(o2, key);
612                if (!v2 || !pa_json_object_equal(v1, v2))
613                    return false;
614            }
615
616            return true;
617        }
618
619        default:
620            pa_assert_not_reached();
621    }
622}
623
624/* Write functions. The functions are wrapper functions around pa_strbuf,
625 * so that the client does not need to use pa_strbuf directly. */
626
627static void json_encoder_context_push(pa_json_encoder *encoder, pa_json_context_type_t type) {
628    pa_assert(encoder);
629
630    encoder_context *head = pa_xnew0(encoder_context, 1);
631    head->type = type;
632    head->next = encoder->context;
633    encoder->context = head;
634}
635
636/* Returns type of context popped off encoder context stack. */
637static pa_json_context_type_t json_encoder_context_pop(pa_json_encoder *encoder) {
638    encoder_context *head;
639    pa_json_context_type_t type;
640
641    pa_assert(encoder);
642    pa_assert(encoder->context);
643
644    type = encoder->context->type;
645
646    head = encoder->context->next;
647    pa_xfree(encoder->context);
648    encoder->context = head;
649
650    return type;
651}
652
653bool pa_json_encoder_is_empty(pa_json_encoder *encoder) {
654    pa_json_context_type_t type;
655
656    pa_assert(encoder);
657    pa_assert(encoder->context);
658
659    type = encoder->context->type;
660    return type == PA_JSON_CONTEXT_EMPTY;
661}
662
663pa_json_encoder *pa_json_encoder_new(void) {
664    pa_json_encoder *encoder;
665
666    encoder = pa_xnew(pa_json_encoder, 1);
667    encoder->buffer = pa_strbuf_new();
668
669    encoder->context = NULL;
670    json_encoder_context_push(encoder, PA_JSON_CONTEXT_EMPTY);
671
672    return encoder;
673}
674
675void pa_json_encoder_free(pa_json_encoder *encoder) {
676    pa_json_context_type_t type;
677    pa_assert(encoder);
678
679    /* should have exactly one encoder context left at this point */
680    pa_assert(encoder->context);
681    type = json_encoder_context_pop(encoder);
682    pa_assert(encoder->context == NULL);
683
684    pa_assert(type == PA_JSON_CONTEXT_TOP || type == PA_JSON_CONTEXT_EMPTY);
685    if (type == PA_JSON_CONTEXT_EMPTY)
686        pa_log_warn("JSON encoder is empty.");
687
688    if (encoder->buffer)
689        pa_strbuf_free(encoder->buffer);
690
691    pa_xfree(encoder);
692}
693
694char *pa_json_encoder_to_string_free(pa_json_encoder *encoder) {
695    char *result;
696
697    pa_assert(encoder);
698
699    result = pa_strbuf_to_string_free(encoder->buffer);
700
701    encoder->buffer = NULL;
702    pa_json_encoder_free(encoder);
703
704    return result;
705}
706
707static void json_encoder_insert_delimiter(pa_json_encoder *encoder) {
708    pa_assert(encoder);
709
710    if (encoder->context->counter++)
711        pa_strbuf_putc(encoder->buffer, ',');
712}
713
714/* Escapes p to create valid JSON string.
715 * The caller has to free the returned string. */
716static char *pa_json_escape(const char *p) {
717    const char *s;
718    char *out_string, *output;
719    int char_count = strlen(p);
720
721    /* Maximum number of characters in output string
722     * including trailing 0. */
723    char_count = 2 * char_count + 1;
724
725    /* allocate output string */
726    out_string = pa_xmalloc(char_count);
727    output = out_string;
728
729    /* write output string */
730    for (s = p; *s; ++s) {
731        switch (*s) {
732            case '"':
733                *output++ = '\\';
734                *output++ = '"';
735                break;
736            case '\\':
737                *output++ = '\\';
738                *output++ = '\\';
739                break;
740            case '\b':
741                *output++ = '\\';
742                *output++ = 'b';
743                break;
744
745            case '\f':
746                *output++ = '\\';
747                *output++ = 'f';
748                break;
749
750            case '\n':
751                *output++ = '\\';
752                *output++ = 'n';
753                break;
754
755            case '\r':
756                *output++ = '\\';
757                *output++ = 'r';
758                break;
759
760            case '\t':
761                *output++ = '\\';
762                *output++ = 't';
763                break;
764            default:
765                if (*s < 0x20 || *s > 0x7E) {
766                    pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *s);
767                    pa_xfree(out_string);
768                    return NULL;
769                }
770                *output++ = *s;
771                break;
772        }
773    }
774
775    *output = 0;
776
777    return out_string;
778}
779
780static void json_write_string_escaped(pa_json_encoder *encoder, const char *value) {
781    char *escaped_value;
782
783    pa_assert(encoder);
784
785    escaped_value = pa_json_escape(value);
786    pa_strbuf_printf(encoder->buffer, "\"%s\"", escaped_value);
787    pa_xfree(escaped_value);
788}
789
790/* Writes an opening curly brace */
791void pa_json_encoder_begin_element_object(pa_json_encoder *encoder) {
792    pa_assert(encoder);
793    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
794
795    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
796        encoder->context->type = PA_JSON_CONTEXT_TOP;
797
798    json_encoder_insert_delimiter(encoder);
799    pa_strbuf_putc(encoder->buffer, '{');
800
801    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
802}
803
804/* Writes an opening curly brace */
805void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name) {
806    pa_assert(encoder);
807    pa_assert(encoder->context);
808    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
809    pa_assert(name && name[0]);
810
811    json_encoder_insert_delimiter(encoder);
812
813    json_write_string_escaped(encoder, name);
814    pa_strbuf_putc(encoder->buffer, ':');
815
816    pa_strbuf_putc(encoder->buffer, '{');
817
818    json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
819}
820
821/* Writes a closing curly brace */
822void pa_json_encoder_end_object(pa_json_encoder *encoder) {
823    pa_json_context_type_t type;
824    pa_assert(encoder);
825
826    type = json_encoder_context_pop(encoder);
827    pa_assert(type == PA_JSON_CONTEXT_OBJECT);
828
829    pa_strbuf_putc(encoder->buffer, '}');
830}
831
832/* Writes an opening bracket */
833void pa_json_encoder_begin_element_array(pa_json_encoder *encoder) {
834    pa_assert(encoder);
835    pa_assert(encoder->context);
836    pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
837
838    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
839        encoder->context->type = PA_JSON_CONTEXT_TOP;
840
841    json_encoder_insert_delimiter(encoder);
842    pa_strbuf_putc(encoder->buffer, '[');
843
844    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
845}
846
847/* Writes member name and an opening bracket */
848void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name) {
849    pa_assert(encoder);
850    pa_assert(encoder->context);
851    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
852    pa_assert(name && name[0]);
853
854    json_encoder_insert_delimiter(encoder);
855
856    json_write_string_escaped(encoder, name);
857    pa_strbuf_putc(encoder->buffer, ':');
858
859    pa_strbuf_putc(encoder->buffer, '[');
860
861    json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
862}
863
864/* Writes a closing bracket */
865void pa_json_encoder_end_array(pa_json_encoder *encoder) {
866    pa_json_context_type_t type;
867    pa_assert(encoder);
868
869    type = json_encoder_context_pop(encoder);
870    pa_assert(type == PA_JSON_CONTEXT_ARRAY);
871
872    pa_strbuf_putc(encoder->buffer, ']');
873}
874
875void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value) {
876    pa_assert(encoder);
877    pa_assert(encoder->context);
878    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
879
880    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
881        encoder->context->type = PA_JSON_CONTEXT_TOP;
882
883    json_encoder_insert_delimiter(encoder);
884
885    json_write_string_escaped(encoder, value);
886}
887
888void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value) {
889    pa_assert(encoder);
890    pa_assert(encoder->context);
891    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
892    pa_assert(name && name[0]);
893
894    json_encoder_insert_delimiter(encoder);
895
896    json_write_string_escaped(encoder, name);
897
898    pa_strbuf_putc(encoder->buffer, ':');
899
900    /* Null value is written as empty element */
901    if (!value)
902        value = "";
903
904    json_write_string_escaped(encoder, value);
905}
906
907static void json_write_null(pa_json_encoder *encoder) {
908    pa_assert(encoder);
909
910    pa_strbuf_puts(encoder->buffer, "null");
911}
912
913void pa_json_encoder_add_element_null(pa_json_encoder *encoder) {
914    pa_assert(encoder);
915    pa_assert(encoder->context);
916    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
917
918    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
919        encoder->context->type = PA_JSON_CONTEXT_TOP;
920
921    json_encoder_insert_delimiter(encoder);
922
923    json_write_null(encoder);
924}
925
926void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name) {
927    pa_assert(encoder);
928    pa_assert(encoder->context);
929    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
930    pa_assert(name && name[0]);
931
932    json_encoder_insert_delimiter(encoder);
933
934    json_write_string_escaped(encoder, name);
935    pa_strbuf_putc(encoder->buffer, ':');
936
937    json_write_null(encoder);
938}
939
940static void json_write_bool(pa_json_encoder *encoder, bool value) {
941    pa_assert(encoder);
942
943    pa_strbuf_puts(encoder->buffer, value ? "true" : "false");
944}
945
946void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value) {
947    pa_assert(encoder);
948    pa_assert(encoder->context);
949    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
950
951    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
952        encoder->context->type = PA_JSON_CONTEXT_TOP;
953
954    json_encoder_insert_delimiter(encoder);
955
956    json_write_bool(encoder, value);
957}
958
959void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value) {
960    pa_assert(encoder);
961    pa_assert(encoder->context);
962    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
963    pa_assert(name && name[0]);
964
965    json_encoder_insert_delimiter(encoder);
966
967    json_write_string_escaped(encoder, name);
968
969    pa_strbuf_putc(encoder->buffer, ':');
970
971    json_write_bool(encoder, value);
972}
973
974static void json_write_int(pa_json_encoder *encoder, int64_t value) {
975    pa_assert(encoder);
976
977    pa_strbuf_printf(encoder->buffer, "%"PRId64, value);
978}
979
980void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value) {
981    pa_assert(encoder);
982    pa_assert(encoder->context);
983    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
984
985    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
986        encoder->context->type = PA_JSON_CONTEXT_TOP;
987
988    json_encoder_insert_delimiter(encoder);
989
990    json_write_int(encoder, value);
991}
992
993void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value) {
994    pa_assert(encoder);
995    pa_assert(encoder->context);
996    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
997    pa_assert(name && name[0]);
998
999    json_encoder_insert_delimiter(encoder);
1000
1001    json_write_string_escaped(encoder, name);
1002
1003    pa_strbuf_putc(encoder->buffer, ':');
1004
1005    json_write_int(encoder, value);
1006}
1007
1008static void json_write_double(pa_json_encoder *encoder, double value, int precision) {
1009    pa_assert(encoder);
1010    pa_strbuf_printf(encoder->buffer, "%.*f",  precision, value);
1011}
1012
1013void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision) {
1014    pa_assert(encoder);
1015    pa_assert(encoder->context);
1016    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
1017
1018    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
1019        encoder->context->type = PA_JSON_CONTEXT_TOP;
1020
1021    json_encoder_insert_delimiter(encoder);
1022
1023    json_write_double(encoder, value, precision);
1024}
1025
1026void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision) {
1027    pa_assert(encoder);
1028    pa_assert(encoder->context);
1029    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
1030    pa_assert(name && name[0]);
1031
1032    json_encoder_insert_delimiter(encoder);
1033
1034    json_write_string_escaped(encoder, name);
1035
1036    pa_strbuf_putc(encoder->buffer, ':');
1037
1038    json_write_double(encoder, value, precision);
1039}
1040
1041static void json_write_raw(pa_json_encoder *encoder, const char *raw_string) {
1042    pa_assert(encoder);
1043    pa_strbuf_puts(encoder->buffer, raw_string);
1044}
1045
1046void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string) {
1047    pa_assert(encoder);
1048    pa_assert(encoder->context);
1049    pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
1050
1051    if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
1052        encoder->context->type = PA_JSON_CONTEXT_TOP;
1053
1054    json_encoder_insert_delimiter(encoder);
1055
1056    json_write_raw(encoder, raw_json_string);
1057}
1058
1059void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string) {
1060    pa_assert(encoder);
1061    pa_assert(encoder->context);
1062    pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
1063    pa_assert(name && name[0]);
1064
1065    json_encoder_insert_delimiter(encoder);
1066
1067    json_write_string_escaped(encoder, name);
1068
1069    pa_strbuf_putc(encoder->buffer, ':');
1070
1071    json_write_raw(encoder, raw_json_string);
1072}
1073