json-writer.c 6.5 KB

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