From 6589f4599151201a61d6b1be8450adb63ae81017 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 11 Dec 2020 18:11:35 +0100 Subject: qobject: Make qobject_to_json_pretty() take a pretty argument Signed-off-by: Markus Armbruster Message-Id: <20201211171152.146877-4-armbru@redhat.com> --- qobject/qjson.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'qobject/qjson.c') diff --git a/qobject/qjson.c b/qobject/qjson.c index f1f2c69704..523a4ab8de 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -149,8 +149,6 @@ QDict *qdict_from_jsonf_nofail(const char *string, ...) return qdict; } -static void to_json(const QObject *obj, QString *str, int pretty, int indent); - static void json_pretty_newline(QString *str, bool pretty, int indent) { int i; @@ -163,7 +161,7 @@ static void json_pretty_newline(QString *str, bool pretty, int indent) } } -static void to_json(const QObject *obj, QString *str, int pretty, int indent) +static void to_json(const QObject *obj, QString *str, bool pretty, int indent) { switch (qobject_type(obj)) { case QTYPE_QNULL: @@ -294,20 +292,16 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) } } -QString *qobject_to_json(const QObject *obj) +QString *qobject_to_json_pretty(const QObject *obj, bool pretty) { QString *str = qstring_new(); - to_json(obj, str, 0, 0); + to_json(obj, str, pretty, 0); return str; } -QString *qobject_to_json_pretty(const QObject *obj) +QString *qobject_to_json(const QObject *obj) { - QString *str = qstring_new(); - - to_json(obj, str, 1, 0); - - return str; + return qobject_to_json_pretty(obj, false); } -- cgit v1.2.3 From f1cc129df8341ebb6176363d24b57035bb5dabe4 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 11 Dec 2020 18:11:36 +0100 Subject: qobject: Use GString instead of QString to accumulate JSON QString supports modifying its string, but it's quite limited: you can only append. The remaining callers use it for building an initial string, never for modifying it later. Use of GString for building the initial string is actually more convenient here. Change qobject_to_json() & friends to do that. Once all such uses are replaced this way, QString can become immutable. Signed-off-by: Markus Armbruster Message-Id: <20201211171152.146877-5-armbru@redhat.com> --- qobject/qjson.c | 85 ++++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 47 deletions(-) (limited to 'qobject/qjson.c') diff --git a/qobject/qjson.c b/qobject/qjson.c index 523a4ab8de..e7100a539c 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -149,28 +149,23 @@ QDict *qdict_from_jsonf_nofail(const char *string, ...) return qdict; } -static void json_pretty_newline(QString *str, bool pretty, int indent) +static void json_pretty_newline(GString *accu, bool pretty, int indent) { - int i; - if (pretty) { - qstring_append(str, "\n"); - for (i = 0; i < indent; i++) { - qstring_append(str, " "); - } + g_string_append_printf(accu, "\n%*s", indent * 4, ""); } } -static void to_json(const QObject *obj, QString *str, bool pretty, int indent) +static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) { switch (qobject_type(obj)) { case QTYPE_QNULL: - qstring_append(str, "null"); + g_string_append(accu, "null"); break; case QTYPE_QNUM: { QNum *val = qobject_to(QNum, obj); char *buffer = qnum_to_string(val); - qstring_append(str, buffer); + g_string_append(accu, buffer); g_free(buffer); break; } @@ -178,35 +173,34 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent) QString *val = qobject_to(QString, obj); const char *ptr; int cp; - char buf[16]; char *end; ptr = qstring_get_str(val); - qstring_append(str, "\""); + g_string_append_c(accu, '"'); for (; *ptr; ptr = end) { cp = mod_utf8_codepoint(ptr, 6, &end); switch (cp) { case '\"': - qstring_append(str, "\\\""); + g_string_append(accu, "\\\""); break; case '\\': - qstring_append(str, "\\\\"); + g_string_append(accu, "\\\\"); break; case '\b': - qstring_append(str, "\\b"); + g_string_append(accu, "\\b"); break; case '\f': - qstring_append(str, "\\f"); + g_string_append(accu, "\\f"); break; case '\n': - qstring_append(str, "\\n"); + g_string_append(accu, "\\n"); break; case '\r': - qstring_append(str, "\\r"); + g_string_append(accu, "\\r"); break; case '\t': - qstring_append(str, "\\t"); + g_string_append(accu, "\\t"); break; default: if (cp < 0) { @@ -214,20 +208,18 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent) } if (cp > 0xFFFF) { /* beyond BMP; need a surrogate pair */ - snprintf(buf, sizeof(buf), "\\u%04X\\u%04X", - 0xD800 + ((cp - 0x10000) >> 10), - 0xDC00 + ((cp - 0x10000) & 0x3FF)); + g_string_append_printf(accu, "\\u%04X\\u%04X", + 0xD800 + ((cp - 0x10000) >> 10), + 0xDC00 + ((cp - 0x10000) & 0x3FF)); } else if (cp < 0x20 || cp >= 0x7F) { - snprintf(buf, sizeof(buf), "\\u%04X", cp); + g_string_append_printf(accu, "\\u%04X", cp); } else { - buf[0] = cp; - buf[1] = 0; + g_string_append_c(accu, cp); } - qstring_append(str, buf); } }; - qstring_append(str, "\""); + g_string_append_c(accu, '"'); break; } case QTYPE_QDICT: { @@ -237,25 +229,25 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent) const QDictEntry *entry; QString *qkey; - qstring_append(str, "{"); + g_string_append_c(accu, '{'); for (entry = qdict_first(val); entry; entry = qdict_next(val, entry)) { - qstring_append(str, sep); - json_pretty_newline(str, pretty, indent + 1); + g_string_append(accu, sep); + json_pretty_newline(accu, pretty, indent + 1); qkey = qstring_from_str(qdict_entry_key(entry)); - to_json(QOBJECT(qkey), str, pretty, indent + 1); + to_json(QOBJECT(qkey), accu, pretty, indent + 1); qobject_unref(qkey); - qstring_append(str, ": "); - to_json(qdict_entry_value(entry), str, pretty, indent + 1); + g_string_append(accu, ": "); + to_json(qdict_entry_value(entry), accu, pretty, indent + 1); sep = comma; } - json_pretty_newline(str, pretty, indent); - qstring_append(str, "}"); + json_pretty_newline(accu, pretty, indent); + g_string_append_c(accu, '}'); break; } case QTYPE_QLIST: { @@ -264,26 +256,26 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent) const char *sep = ""; QListEntry *entry; - qstring_append(str, "["); + g_string_append_c(accu, '['); QLIST_FOREACH_ENTRY(val, entry) { - qstring_append(str, sep); - json_pretty_newline(str, pretty, indent + 1); - to_json(qlist_entry_obj(entry), str, pretty, indent + 1); + g_string_append(accu, sep); + json_pretty_newline(accu, pretty, indent + 1); + to_json(qlist_entry_obj(entry), accu, pretty, indent + 1); sep = comma; } - json_pretty_newline(str, pretty, indent); - qstring_append(str, "]"); + json_pretty_newline(accu, pretty, indent); + g_string_append_c(accu, ']'); break; } case QTYPE_QBOOL: { QBool *val = qobject_to(QBool, obj); if (qbool_get_bool(val)) { - qstring_append(str, "true"); + g_string_append(accu, "true"); } else { - qstring_append(str, "false"); + g_string_append(accu, "false"); } break; } @@ -294,11 +286,10 @@ static void to_json(const QObject *obj, QString *str, bool pretty, int indent) QString *qobject_to_json_pretty(const QObject *obj, bool pretty) { - QString *str = qstring_new(); - - to_json(obj, str, pretty, 0); + GString *accu = g_string_new(NULL); - return str; + to_json(obj, accu, pretty, 0); + return qstring_from_gstring(accu); } QString *qobject_to_json(const QObject *obj) -- cgit v1.2.3 From eab3a4678b07267c39e7290a6e9e7690b1d2a521 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 11 Dec 2020 18:11:37 +0100 Subject: qobject: Change qobject_to_json()'s value to GString qobject_to_json() and qobject_to_json_pretty() build a GString, then covert it to QString. Just one of the callers actually needs a QString: qemu_rbd_parse_filename(). A few others need a string they can modify: qmp_send_response(), qga's send_response(), to_json_str(), and qmp_fd_vsend_fds(). The remainder just need a string. Change qobject_to_json() and qobject_to_json_pretty() to return the GString. qemu_rbd_parse_filename() now has to convert to QString. All others save a QString temporary. to_json_str() actually becomes a bit simpler, because GString provides more convenient modification functions. Signed-off-by: Markus Armbruster Message-Id: <20201211171152.146877-6-armbru@redhat.com> --- qobject/qjson.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'qobject/qjson.c') diff --git a/qobject/qjson.c b/qobject/qjson.c index e7100a539c..2f690c1816 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -284,15 +284,15 @@ static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) } } -QString *qobject_to_json_pretty(const QObject *obj, bool pretty) +GString *qobject_to_json_pretty(const QObject *obj, bool pretty) { GString *accu = g_string_new(NULL); to_json(obj, accu, pretty, 0); - return qstring_from_gstring(accu); + return accu; } -QString *qobject_to_json(const QObject *obj) +GString *qobject_to_json(const QObject *obj) { return qobject_to_json_pretty(obj, false); } -- cgit v1.2.3 From 91f54d92c752e392be4903c9ad2846b2cdd0398d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 11 Dec 2020 18:11:46 +0100 Subject: qobject: Factor quoted_str() out of to_json() Signed-off-by: Markus Armbruster Message-Id: <20201211171152.146877-15-armbru@redhat.com> --- qobject/qjson.c | 110 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 54 insertions(+), 56 deletions(-) (limited to 'qobject/qjson.c') diff --git a/qobject/qjson.c b/qobject/qjson.c index 2f690c1816..962214f5a7 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -156,6 +156,58 @@ static void json_pretty_newline(GString *accu, bool pretty, int indent) } } +static void quoted_str(const char *str, GString *accu) +{ + const char *ptr; + int cp; + char *end; + + g_string_append_c(accu, '"'); + + for (ptr = str; *ptr; ptr = end) { + cp = mod_utf8_codepoint(ptr, 6, &end); + switch (cp) { + case '\"': + g_string_append(accu, "\\\""); + break; + case '\\': + g_string_append(accu, "\\\\"); + break; + case '\b': + g_string_append(accu, "\\b"); + break; + case '\f': + g_string_append(accu, "\\f"); + break; + case '\n': + g_string_append(accu, "\\n"); + break; + case '\r': + g_string_append(accu, "\\r"); + break; + case '\t': + g_string_append(accu, "\\t"); + break; + default: + if (cp < 0) { + cp = 0xFFFD; /* replacement character */ + } + if (cp > 0xFFFF) { + /* beyond BMP; need a surrogate pair */ + g_string_append_printf(accu, "\\u%04X\\u%04X", + 0xD800 + ((cp - 0x10000) >> 10), + 0xDC00 + ((cp - 0x10000) & 0x3FF)); + } else if (cp < 0x20 || cp >= 0x7F) { + g_string_append_printf(accu, "\\u%04X", cp); + } else { + g_string_append_c(accu, cp); + } + } + }; + + g_string_append_c(accu, '"'); +} + static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) { switch (qobject_type(obj)) { @@ -170,56 +222,7 @@ static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) break; } case QTYPE_QSTRING: { - QString *val = qobject_to(QString, obj); - const char *ptr; - int cp; - char *end; - - ptr = qstring_get_str(val); - g_string_append_c(accu, '"'); - - for (; *ptr; ptr = end) { - cp = mod_utf8_codepoint(ptr, 6, &end); - switch (cp) { - case '\"': - g_string_append(accu, "\\\""); - break; - case '\\': - g_string_append(accu, "\\\\"); - break; - case '\b': - g_string_append(accu, "\\b"); - break; - case '\f': - g_string_append(accu, "\\f"); - break; - case '\n': - g_string_append(accu, "\\n"); - break; - case '\r': - g_string_append(accu, "\\r"); - break; - case '\t': - g_string_append(accu, "\\t"); - break; - default: - if (cp < 0) { - cp = 0xFFFD; /* replacement character */ - } - if (cp > 0xFFFF) { - /* beyond BMP; need a surrogate pair */ - g_string_append_printf(accu, "\\u%04X\\u%04X", - 0xD800 + ((cp - 0x10000) >> 10), - 0xDC00 + ((cp - 0x10000) & 0x3FF)); - } else if (cp < 0x20 || cp >= 0x7F) { - g_string_append_printf(accu, "\\u%04X", cp); - } else { - g_string_append_c(accu, cp); - } - } - }; - - g_string_append_c(accu, '"'); + quoted_str(qstring_get_str(qobject_to(QString, obj)), accu); break; } case QTYPE_QDICT: { @@ -227,7 +230,6 @@ static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) const char *comma = pretty ? "," : ", "; const char *sep = ""; const QDictEntry *entry; - QString *qkey; g_string_append_c(accu, '{'); @@ -236,11 +238,7 @@ static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) entry = qdict_next(val, entry)) { g_string_append(accu, sep); json_pretty_newline(accu, pretty, indent + 1); - - qkey = qstring_from_str(qdict_entry_key(entry)); - to_json(QOBJECT(qkey), accu, pretty, indent + 1); - qobject_unref(qkey); - + quoted_str(qdict_entry_key(entry), accu); g_string_append(accu, ": "); to_json(qdict_entry_value(entry), accu, pretty, indent + 1); sep = comma; -- cgit v1.2.3 From 998da0b1581bfda6d6d0e82b9e42edfa1bf5cfe5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 11 Dec 2020 18:11:47 +0100 Subject: qobject: Factor JSON writer out of qobject_to_json() We have two JSON writers written in C: qobject/qjson.c provides qobject_to_json(), and migration/qjson.c provides a more low level imperative interface. They don't share code. The latter tacitly limits numbers to int64_t, and strings contents to characters that don't need escaping. Factor out qobject_to_json()'s JSON writer as qobject/json-writer.c. Straightforward, except for numbers: since the writer is to be independent of QObject, it can't use qnum_to_string(). Open-code it instead. This is actually an improvement of sorts, because it liberates qnum_to_string() from JSON's needs: its JSON-related FIXMEs move to the JSON writer, where they belong. The next commit will replace migration/qjson.c. Signed-off-by: Markus Armbruster Message-Id: <20201211171152.146877-16-armbru@redhat.com> --- qobject/qjson.c | 123 ++++++++++++++------------------------------------------ 1 file changed, 30 insertions(+), 93 deletions(-) (limited to 'qobject/qjson.c') diff --git a/qobject/qjson.c b/qobject/qjson.c index 962214f5a7..bcc376e626 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -14,13 +14,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qmp/json-parser.h" +#include "qapi/qmp/json-writer.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "qemu/unicode.h" typedef struct JSONParsingState { @@ -149,132 +149,69 @@ QDict *qdict_from_jsonf_nofail(const char *string, ...) return qdict; } -static void json_pretty_newline(GString *accu, bool pretty, int indent) +static void to_json(JSONWriter *writer, const char *name, + const QObject *obj) { - if (pretty) { - g_string_append_printf(accu, "\n%*s", indent * 4, ""); - } -} - -static void quoted_str(const char *str, GString *accu) -{ - const char *ptr; - int cp; - char *end; - - g_string_append_c(accu, '"'); + switch (qobject_type(obj)) { + case QTYPE_QNULL: + json_writer_null(writer, name); + break; + case QTYPE_QNUM: { + QNum *val = qobject_to(QNum, obj); - for (ptr = str; *ptr; ptr = end) { - cp = mod_utf8_codepoint(ptr, 6, &end); - switch (cp) { - case '\"': - g_string_append(accu, "\\\""); - break; - case '\\': - g_string_append(accu, "\\\\"); - break; - case '\b': - g_string_append(accu, "\\b"); - break; - case '\f': - g_string_append(accu, "\\f"); + switch (val->kind) { + case QNUM_I64: + json_writer_int64(writer, name, val->u.i64); break; - case '\n': - g_string_append(accu, "\\n"); + case QNUM_U64: + json_writer_uint64(writer, name, val->u.u64); break; - case '\r': - g_string_append(accu, "\\r"); - break; - case '\t': - g_string_append(accu, "\\t"); + case QNUM_DOUBLE: + json_writer_double(writer, name, val->u.dbl); break; default: - if (cp < 0) { - cp = 0xFFFD; /* replacement character */ - } - if (cp > 0xFFFF) { - /* beyond BMP; need a surrogate pair */ - g_string_append_printf(accu, "\\u%04X\\u%04X", - 0xD800 + ((cp - 0x10000) >> 10), - 0xDC00 + ((cp - 0x10000) & 0x3FF)); - } else if (cp < 0x20 || cp >= 0x7F) { - g_string_append_printf(accu, "\\u%04X", cp); - } else { - g_string_append_c(accu, cp); - } + abort(); } - }; - - g_string_append_c(accu, '"'); -} - -static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) -{ - switch (qobject_type(obj)) { - case QTYPE_QNULL: - g_string_append(accu, "null"); - break; - case QTYPE_QNUM: { - QNum *val = qobject_to(QNum, obj); - char *buffer = qnum_to_string(val); - g_string_append(accu, buffer); - g_free(buffer); break; } case QTYPE_QSTRING: { - quoted_str(qstring_get_str(qobject_to(QString, obj)), accu); + QString *val = qobject_to(QString, obj); + + json_writer_str(writer, name, qstring_get_str(val)); break; } case QTYPE_QDICT: { QDict *val = qobject_to(QDict, obj); - const char *comma = pretty ? "," : ", "; - const char *sep = ""; const QDictEntry *entry; - g_string_append_c(accu, '{'); + json_writer_start_object(writer, name); for (entry = qdict_first(val); entry; entry = qdict_next(val, entry)) { - g_string_append(accu, sep); - json_pretty_newline(accu, pretty, indent + 1); - quoted_str(qdict_entry_key(entry), accu); - g_string_append(accu, ": "); - to_json(qdict_entry_value(entry), accu, pretty, indent + 1); - sep = comma; + to_json(writer, qdict_entry_key(entry), qdict_entry_value(entry)); } - json_pretty_newline(accu, pretty, indent); - g_string_append_c(accu, '}'); + json_writer_end_object(writer); break; } case QTYPE_QLIST: { QList *val = qobject_to(QList, obj); - const char *comma = pretty ? "," : ", "; - const char *sep = ""; QListEntry *entry; - g_string_append_c(accu, '['); + json_writer_start_array(writer, name); QLIST_FOREACH_ENTRY(val, entry) { - g_string_append(accu, sep); - json_pretty_newline(accu, pretty, indent + 1); - to_json(qlist_entry_obj(entry), accu, pretty, indent + 1); - sep = comma; + to_json(writer, NULL, qlist_entry_obj(entry)); } - json_pretty_newline(accu, pretty, indent); - g_string_append_c(accu, ']'); + json_writer_end_array(writer); break; } case QTYPE_QBOOL: { QBool *val = qobject_to(QBool, obj); - if (qbool_get_bool(val)) { - g_string_append(accu, "true"); - } else { - g_string_append(accu, "false"); - } + json_writer_bool(writer, name, qbool_get_bool(val)); break; } default: @@ -284,10 +221,10 @@ static void to_json(const QObject *obj, GString *accu, bool pretty, int indent) GString *qobject_to_json_pretty(const QObject *obj, bool pretty) { - GString *accu = g_string_new(NULL); + JSONWriter *writer = json_writer_new(pretty); - to_json(obj, accu, pretty, 0); - return accu; + to_json(writer, NULL, obj); + return json_writer_get_and_free(writer); } GString *qobject_to_json(const QObject *obj) -- cgit v1.2.3