qemu does not have a string buffer object, so I added this capability to QString.
Cc: Luiz Capitulino <lcapitul...@redhat.com> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- 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); + } +} + +/** + * qstring_append_ch(): Append a character to a QString + */ +void qstring_append_ch(QString *qstring, char c) +{ + if (qstring->n == qstring->alloc - 1) { + if (qstring->alloc < 10) { + qstring->alloc = 10; + } else { + qstring->alloc *= 2; + } + qstring->string = qemu_realloc (qstring->string, qstring->alloc); + } + qstring->string[qstring->n++] = c; +} + +/** * qobject_to_qstring(): Convert a QObject to a QString */ QString *qobject_to_qstring(const QObject *obj) @@ -51,10 +150,12 @@ QString *qobject_to_qstring(const QObject *obj) * qstring_get_str(): Return a pointer to the stored string * * NOTE: Should be used with caution, if the object is deallocated - * this pointer becomes invalid. + * or modified this pointer becomes invalid. */ const char *qstring_get_str(const QString *qstring) { + /* NULL-terminate it here. */ + qstring->string[qstring->n] = 0; return qstring->string; } diff --git a/qstring.h b/qstring.h index e012cb7..6e16d58 100644 --- a/qstring.h +++ b/qstring.h @@ -5,11 +5,16 @@ typedef struct QString { QObject_HEAD; + size_t n, alloc; char *string; } QString; +QString *qstring_new(void); QString *qstring_from_str(const char *str); const char *qstring_get_str(const QString *qstring); +void qstring_append(QString *qstring, const char *str); +void qstring_append_escaped(QString *qstring, const char *str); +void qstring_append_ch(QString *qstring, char c); QString *qobject_to_qstring(const QObject *obj); #endif /* QSTRING_H */ -- 1.6.2.5