qemu does not have a string buffer object, so I added this capability
to QString.
Cc: Luiz Capitulino <address@hidden>
Signed-off-by: Paolo Bonzini <address@hidden>
---
check-qstring.c | 79 ++++++++++++++++++++++++++++++++++++++++-
qstring.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
qstring.h | 5 +++
3 files changed, 186 insertions(+), 3 deletions(-)
diff --git a/check-qstring.c b/check-qstring.c
index ea4dfd0..842cd7f 100644
--- a/check-qstring.c
+++ b/check-qstring.c
@@ -17,6 +17,23 @@
* (with some violations to access 'private' data)
*/
+START_TEST(qstring_new_test)
+{
+ QString *qstring;
+
+ qstring = qstring_new();
+ fail_unless(qstring != NULL);
+ fail_unless(qstring->base.refcnt == 1);
+ fail_unless(qstring->n == 0);
+ fail_unless(qstring->alloc < 1000);
+ fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
+
+ // destroy doesn't exit yet
+ qemu_free(qstring->string);
+ qemu_free(qstring);
+}
+END_TEST
+
START_TEST(qstring_from_str_test)
{
QString *qstring;
@@ -25,7 +42,9 @@ START_TEST(qstring_from_str_test)
qstring = qstring_from_str(str);
fail_unless(qstring != NULL);
fail_unless(qstring->base.refcnt == 1);
- fail_unless(strcmp(str, qstring->string) == 0);
+ fail_unless(qstring->n == 4);
+ fail_unless(qstring->alloc >= 4);
+ fail_unless(memcmp(str, qstring->string, 4) == 0);
fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
// destroy doesn't exit yet
@@ -55,6 +74,60 @@ START_TEST(qstring_get_str_test)
}
END_TEST
+START_TEST(qstring_append_test)
+{
+ QString *qstring;
+ const char *str = "QEM";
+ const char *longstr = "QEMUQEMUQEMUQEMUQEMUQEMUQEMUQEMUQEMUQEMU";
+
+ qstring = qstring_from_str(str);
+ qstring_append(qstring, "U");
+ fail_unless(qstring->n == 4);
+ fail_unless(qstring->alloc >= 4);
+ fail_unless(memcmp(longstr, qstring->string, 4) == 0);
+
+ qstring_append(qstring, "Q");
+ fail_unless(qstring->n == 5);
+ fail_unless(qstring->alloc >= 5);
+ fail_unless(memcmp(longstr, qstring->string, 5) == 0);
+
+ qstring_append(qstring, longstr + 5);
+ fail_unless(qstring->n == strlen (longstr));
+ fail_unless(qstring->alloc >= qstring->n);
+ fail_unless(memcmp(longstr, qstring->string, qstring->n) == 0);
+ QDECREF(qstring);
+}
+END_TEST
+
+START_TEST(qstring_append_ch_test)
+{
+ QString *qstring;
+ const char *str = "QEM";
+
+ qstring = qstring_from_str(str);
+ qstring_append_ch(qstring, 'U');
+ fail_unless(qstring->n == 4);
+ fail_unless(qstring->alloc >= 4);
+ fail_unless(memcmp("QEMU", qstring->string, 4) == 0);
+ QDECREF(qstring);
+}
+END_TEST
+
+START_TEST(qstring_append_escaped_test)
+{
+ QString *qstring;
+ const char *str = "\"Q\x0EMU\t";
+ const char *result = "\\\"Q\\u000eMU\\t";
+
+ qstring = qstring_new();
+ qstring_append_escaped(qstring, str);
+ fail_unless(qstring->n == strlen (result));
+ fail_unless(qstring->alloc >= qstring->n);
+ fail_unless(memcmp(result, qstring->string, strlen (result)) == 0);
+ QDECREF(qstring);
+}
+END_TEST
+
START_TEST(qobject_to_qstring_test)
{
QString *qstring;
@@ -75,9 +148,13 @@ static Suite *qstring_suite(void)
qstring_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qstring_public_tcase);
+ tcase_add_test(qstring_public_tcase, qstring_new_test);
tcase_add_test(qstring_public_tcase, qstring_from_str_test);
tcase_add_test(qstring_public_tcase, qstring_destroy_test);
tcase_add_test(qstring_public_tcase, qstring_get_str_test);
+ tcase_add_test(qstring_public_tcase, qstring_append_test);
+ tcase_add_test(qstring_public_tcase, qstring_append_ch_test);
+ tcase_add_test(qstring_public_tcase, qstring_append_escaped_test);
tcase_add_test(qstring_public_tcase, qobject_to_qstring_test);
return s;
diff --git a/qstring.c b/qstring.c
index 6d411da..ab77fba 100644
--- a/qstring.c
+++ b/qstring.c
@@ -21,6 +21,29 @@ static const QType qstring_type = {
};
/**
+ * Invariant: all strings have an empty byte at the end so that
+ * it is easy to convert them to C strings.
+ */
+
+
+/**
+ * qstring_new(): Create a new empty QString
+ *
+ * Return strong reference.
+ */
+QString *qstring_new(void)
+{
+ QString *qstring;
+ qstring = qemu_malloc(sizeof(*qstring));
+ qstring->n = 0;
+ qstring->alloc = 16;
+ qstring->string = qemu_malloc(qstring->alloc);
+ QOBJECT_INIT(qstring, &qstring_type);
+
+ return qstring;
+}
+
+/**
* qstring_from_str(): Create a new QString from a regular C string
*
* Return strong reference.
@@ -28,15 +51,91 @@ static const QType qstring_type = {
QString *qstring_from_str(const char *str)
{
QString *qstring;
+ size_t n = strlen(str);
qstring = qemu_malloc(sizeof(*qstring));
- qstring->string = qemu_strdup(str);
+ qstring->n = n;
+ qstring->alloc = n + 1;
+ qstring->string = qemu_memdup(str, qstring->alloc);
QOBJECT_INIT(qstring, &qstring_type);
return qstring;
}
/**
+ * qstring_append(): Append a regular C string to a QString
+ */
+void qstring_append(QString *qstring, const char *str)
+{
+ size_t n = strlen(str);
+ size_t total = qstring->n + n + 1;
+
+ if (total > qstring->alloc) {
+ if (qstring->alloc * 2 < total) {
+ qstring->alloc = total;
+ } else {
+ qstring->alloc *= 2;
+ }
+ qstring->string = qemu_realloc (qstring->string, qstring->alloc);
+ }
+ memcpy (qstring->string + qstring->n, str, n + 1);
+ qstring->n += n;
+}
+
+/**
+ * qstring_append(): Append a regular C string to a QString, escaping it
+ * according to JSON syntax.
+ */
+void qstring_append_escaped(QString *qstring, const char *str)
+{
+ for (; *str; str++) {
+ unsigned char ch = *str;
+ switch (*str) {
+ case '\f': ch = 'f'; goto backslash;
+ case '\n': ch = 'n'; goto backslash;
+ case '\r': ch = 'r'; goto backslash;
+ case '\t': ch = 't'; goto backslash;
+ case '\b': ch = 'b'; goto backslash;
+
+ backslash:
+ case '\\':
+ case '\"':
+ qstring_append_ch (qstring, '\\');
+ break;
+
+ default:
+ if (ch < 0x20) {
+ qstring_append_ch (qstring, '\\');
+ qstring_append_ch (qstring, 'u');
+ qstring_append_ch (qstring, '0');
+ qstring_append_ch (qstring, '0');
+ qstring_append_ch (qstring, '0' + (ch >> 4));
+ ch = (ch & 15) + ((ch & 15) > 9 ? 'a' - 10 : '0');
+ }
+ break;
+ }
+
+ qstring_append_ch (qstring, ch);
+ }
+}