qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

json-writer.c (6646B)


      1 /*
      2  * JSON Writer
      3  *
      4  * Copyright IBM, Corp. 2009
      5  * Copyright (c) 2010-2020 Red Hat Inc.
      6  *
      7  * Authors:
      8  *  Anthony Liguori   <aliguori@us.ibm.com>
      9  *  Markus Armbruster <armbru@redhat.com>
     10  *
     11  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
     12  * See the COPYING.LIB file in the top-level directory.
     13  *
     14  */
     15 
     16 #include "qemu/osdep.h"
     17 #include "qapi/qmp/json-writer.h"
     18 #include "qemu/unicode.h"
     19 
     20 struct JSONWriter {
     21     bool pretty;
     22     bool need_comma;
     23     GString *contents;
     24     GByteArray *container_is_array;
     25 };
     26 
     27 JSONWriter *json_writer_new(bool pretty)
     28 {
     29     JSONWriter *writer = g_new(JSONWriter, 1);
     30 
     31     writer->pretty = pretty;
     32     writer->need_comma = false;
     33     writer->contents = g_string_new(NULL);
     34     writer->container_is_array = g_byte_array_new();
     35     return writer;
     36 }
     37 
     38 const char *json_writer_get(JSONWriter *writer)
     39 {
     40     g_assert(!writer->container_is_array->len);
     41     return writer->contents->str;
     42 }
     43 
     44 GString *json_writer_get_and_free(JSONWriter *writer)
     45 {
     46     GString *contents = writer->contents;
     47 
     48     writer->contents = NULL;
     49     g_byte_array_free(writer->container_is_array, true);
     50     g_free(writer);
     51     return contents;
     52 }
     53 
     54 void json_writer_free(JSONWriter *writer)
     55 {
     56     if (writer) {
     57         g_string_free(json_writer_get_and_free(writer), true);
     58     }
     59 }
     60 
     61 static void enter_container(JSONWriter *writer, bool is_array)
     62 {
     63     unsigned depth = writer->container_is_array->len;
     64 
     65     g_byte_array_set_size(writer->container_is_array, depth + 1);
     66     writer->container_is_array->data[depth] = is_array;
     67     writer->need_comma = false;
     68 }
     69 
     70 static void leave_container(JSONWriter *writer, bool is_array)
     71 {
     72     unsigned depth = writer->container_is_array->len;
     73 
     74     assert(depth);
     75     assert(writer->container_is_array->data[depth - 1] == is_array);
     76     g_byte_array_set_size(writer->container_is_array, depth - 1);
     77     writer->need_comma = true;
     78 }
     79 
     80 static bool in_object(JSONWriter *writer)
     81 {
     82     unsigned depth = writer->container_is_array->len;
     83 
     84     return depth && !writer->container_is_array->data[depth - 1];
     85 }
     86 
     87 static void pretty_newline(JSONWriter *writer)
     88 {
     89     if (writer->pretty) {
     90         g_string_append_printf(writer->contents, "\n%*s",
     91                                writer->container_is_array->len * 4, "");
     92     }
     93 }
     94 
     95 static void pretty_newline_or_space(JSONWriter *writer)
     96 {
     97     if (writer->pretty) {
     98         g_string_append_printf(writer->contents, "\n%*s",
     99                                writer->container_is_array->len * 4, "");
    100     } else {
    101         g_string_append_c(writer->contents, ' ');
    102     }
    103 }
    104 
    105 static void quoted_str(JSONWriter *writer, const char *str)
    106 {
    107     const char *ptr;
    108     char *end;
    109     int cp;
    110 
    111     g_string_append_c(writer->contents, '"');
    112 
    113     for (ptr = str; *ptr; ptr = end) {
    114         cp = mod_utf8_codepoint(ptr, 6, &end);
    115         switch (cp) {
    116         case '\"':
    117             g_string_append(writer->contents, "\\\"");
    118             break;
    119         case '\\':
    120             g_string_append(writer->contents, "\\\\");
    121             break;
    122         case '\b':
    123             g_string_append(writer->contents, "\\b");
    124             break;
    125         case '\f':
    126             g_string_append(writer->contents, "\\f");
    127             break;
    128         case '\n':
    129             g_string_append(writer->contents, "\\n");
    130             break;
    131         case '\r':
    132             g_string_append(writer->contents, "\\r");
    133             break;
    134         case '\t':
    135             g_string_append(writer->contents, "\\t");
    136             break;
    137         default:
    138             if (cp < 0) {
    139                 cp = 0xFFFD; /* replacement character */
    140             }
    141             if (cp > 0xFFFF) {
    142                 /* beyond BMP; need a surrogate pair */
    143                 g_string_append_printf(writer->contents, "\\u%04X\\u%04X",
    144                                        0xD800 + ((cp - 0x10000) >> 10),
    145                                        0xDC00 + ((cp - 0x10000) & 0x3FF));
    146             } else if (cp < 0x20 || cp >= 0x7F) {
    147                 g_string_append_printf(writer->contents, "\\u%04X", cp);
    148             } else {
    149                 g_string_append_c(writer->contents, cp);
    150             }
    151         }
    152     };
    153 
    154     g_string_append_c(writer->contents, '"');
    155 }
    156 
    157 static void maybe_comma_name(JSONWriter *writer, const char *name)
    158 {
    159     if (writer->need_comma) {
    160         g_string_append_c(writer->contents, ',');
    161         pretty_newline_or_space(writer);
    162     } else {
    163         if (writer->contents->len) {
    164             pretty_newline(writer);
    165         }
    166         writer->need_comma = true;
    167     }
    168 
    169     if (in_object(writer)) {
    170         quoted_str(writer, name);
    171         g_string_append(writer->contents, ": ");
    172     }
    173 }
    174 
    175 void json_writer_start_object(JSONWriter *writer, const char *name)
    176 {
    177     maybe_comma_name(writer, name);
    178     g_string_append_c(writer->contents, '{');
    179     enter_container(writer, false);
    180 }
    181 
    182 void json_writer_end_object(JSONWriter *writer)
    183 {
    184     leave_container(writer, false);
    185     pretty_newline(writer);
    186     g_string_append_c(writer->contents, '}');
    187 }
    188 
    189 void json_writer_start_array(JSONWriter *writer, const char *name)
    190 {
    191     maybe_comma_name(writer, name);
    192     g_string_append_c(writer->contents, '[');
    193     enter_container(writer, true);
    194 }
    195 
    196 void json_writer_end_array(JSONWriter *writer)
    197 {
    198     leave_container(writer, true);
    199     pretty_newline(writer);
    200     g_string_append_c(writer->contents, ']');
    201 }
    202 
    203 void json_writer_bool(JSONWriter *writer, const char *name, bool val)
    204 {
    205     maybe_comma_name(writer, name);
    206     g_string_append(writer->contents, val ? "true" : "false");
    207 }
    208 
    209 void json_writer_null(JSONWriter *writer, const char *name)
    210 {
    211     maybe_comma_name(writer, name);
    212     g_string_append(writer->contents, "null");
    213 }
    214 
    215 void json_writer_int64(JSONWriter *writer, const char *name, int64_t val)
    216 {
    217     maybe_comma_name(writer, name);
    218     g_string_append_printf(writer->contents, "%" PRId64, val);
    219 }
    220 
    221 void json_writer_uint64(JSONWriter *writer, const char *name, uint64_t val)
    222 {
    223     maybe_comma_name(writer, name);
    224     g_string_append_printf(writer->contents, "%" PRIu64, val);
    225 }
    226 
    227 void json_writer_double(JSONWriter *writer, const char *name, double val)
    228 {
    229     maybe_comma_name(writer, name);
    230 
    231     /*
    232      * FIXME: g_string_append_printf() is locale dependent; but JSON
    233      * requires numbers to be formatted as if in the C locale.
    234      * Dependence on C locale is a pervasive issue in QEMU.
    235      */
    236     /*
    237      * FIXME: This risks printing Inf or NaN, which are not valid
    238      * JSON values.
    239      */
    240     g_string_append_printf(writer->contents, "%.17g", val);
    241 }
    242 
    243 void json_writer_str(JSONWriter *writer, const char *name, const char *str)
    244 {
    245     maybe_comma_name(writer, name);
    246     quoted_str(writer, str);
    247 }