Patchwork [4/7] add mutable qstring functions

login
register
mail settings
Submitter Paolo Bonzini
Date Oct. 17, 2009, 7:55 a.m.
Message ID <1255766136-3028-5-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/36293/
State New
Headers show

Comments

Paolo Bonzini - Oct. 17, 2009, 7:55 a.m.
qemu does not have a string buffer object, so I added this capability
to QString.

Cc: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 check-qstring.c |   79 ++++++++++++++++++++++++++++++++++++++++-
 qstring.c       |  105 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 qstring.h       |    5 +++
 3 files changed, 186 insertions(+), 3 deletions(-)
Anthony Liguori - Oct. 17, 2009, 1:03 p.m.
Paolo Bonzini wrote:
> qemu does not have a string buffer object, so I added this capability
> to QString.
>
> Cc: Luiz Capitulino <lcapitulino@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@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);
> +    }
> +}
>
>   

The json parser should do the escaping.

Regards,

Anthony Liguori

Patch

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 */