Skip to content

Commit

Permalink
qobject: Use GString instead of QString to accumulate JSON
Browse files Browse the repository at this point in the history
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 <armbru@redhat.com>
Message-Id: <20201211171152.146877-5-armbru@redhat.com>
  • Loading branch information
Markus Armbruster committed Dec 19, 2020
1 parent 6589f45 commit f1cc129
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 47 deletions.
1 change: 1 addition & 0 deletions include/qapi/qmp/qstring.h
Expand Up @@ -25,6 +25,7 @@ struct QString {
QString *qstring_new(void);
QString *qstring_from_str(const char *str);
QString *qstring_from_substr(const char *str, size_t start, size_t end);
QString *qstring_from_gstring(GString *gstr);
size_t qstring_get_length(const QString *qstring);
const char *qstring_get_str(const QString *qstring);
const char *qstring_get_try_str(const QString *qstring);
Expand Down
85 changes: 38 additions & 47 deletions qobject/qjson.c
Expand Up @@ -149,85 +149,77 @@ 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;
}
case QTYPE_QSTRING: {
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) {
cp = 0xFFFD; /* replacement character */
}
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: {
Expand All @@ -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: {
Expand All @@ -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;
}
Expand All @@ -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)
Expand Down
19 changes: 19 additions & 0 deletions qobject/qstring.c
Expand Up @@ -66,6 +66,25 @@ QString *qstring_from_str(const char *str)
return qstring_from_substr(str, 0, strlen(str));
}

/**
* qstring_from_gstring(): Convert a GString to a QString
*
* Return strong reference.
*/

QString *qstring_from_gstring(GString *gstr)
{
QString *qstring;

qstring = g_malloc(sizeof(*qstring));
qobject_init(QOBJECT(qstring), QTYPE_QSTRING);
qstring->length = gstr->len;
qstring->capacity = gstr->allocated_len;
qstring->string = g_string_free(gstr, false);
return qstring;
}


static void capacity_increase(QString *qstring, size_t len)
{
if (qstring->capacity < (qstring->length + len)) {
Expand Down

0 comments on commit f1cc129

Please sign in to comment.