[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v4 19/23] QMP: Add QBuffer
From: |
Jan Kiszka |
Subject: |
[Qemu-devel] [PATCH v4 19/23] QMP: Add QBuffer |
Date: |
Wed, 16 Jun 2010 00:38:43 +0200 |
From: Jan Kiszka <address@hidden>
This introduces a buffer object for use with QMP. As a buffer is not
natively encodable in JSON, we encode it as a base64 string and
encapsulate the result in the new QMP object class "buffer".
The first use case for this is pushing the content of buffers that are
part of a device state into a qdict.
Signed-off-by: Jan Kiszka <address@hidden>
---
Makefile | 5 +-
Makefile.objs | 2 +-
QMP/qmp-spec.txt | 10 +++-
check-qbuffer.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 2 +-
qbuffer.c | 116 ++++++++++++++++++++++++++++++++++++
qbuffer.h | 33 ++++++++++
qjson.c | 15 +++++
qobject.h | 1 +
9 files changed, 351 insertions(+), 5 deletions(-)
create mode 100644 check-qbuffer.c
create mode 100644 qbuffer.c
create mode 100644 qbuffer.h
diff --git a/Makefile b/Makefile
index 221fbd8..2267261 100644
--- a/Makefile
+++ b/Makefile
@@ -146,14 +146,15 @@ qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o
qemu-error.o $(block-obj-y) $(qobj
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
-check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o
check-qjson.o: $(GENERATED_HEADERS)
+check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o
check-qjson.o check-qbuffer: $(GENERATED_HEADERS)
check-qint: check-qint.o qint.o qemu-malloc.o
check-qstring: check-qstring.o qstring.o qemu-malloc.o
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o
qemu-malloc.o qlist.o
check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o
check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o
-check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o
qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o
+check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o
qbuffer.o base64.o qjson.o json-streamer.o json-lexer.o json-parser.o
qemu-malloc.o
+check-qbuffer: check-qbuffer.o qbuffer.o base64.o qstring.o qemu-malloc.o
clean:
# avoid old build problems by removing potentially incorrect old files
diff --git a/Makefile.objs b/Makefile.objs
index 670b8a8..82bc63f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,6 +1,6 @@
#######################################################################
# QObject
-qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
+qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o qbuffer.o
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
qobject-obj-y += qerror.o base64.o
diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt
index fa1dd62..820e39d 100644
--- a/QMP/qmp-spec.txt
+++ b/QMP/qmp-spec.txt
@@ -153,7 +153,15 @@ JSON objects that contain the key-value pair '"__class__":
json-string' are
reserved for QMP-specific complex object classes that. QMP specifies which
further keys each of these objects include and how they are encoded.
-So far, no complex object class is specified.
+2.6.1 Buffer class
+------------------
+
+This QMP object class allows to transport binary data. A buffer object
+consists of the following keys:
+
+{ "__class__": "buffer", "data": json-string }
+
+The data string is base64 encoded according to RFC 4648.
3. QMP Examples
===============
diff --git a/check-qbuffer.c b/check-qbuffer.c
new file mode 100644
index 0000000..b490230
--- /dev/null
+++ b/check-qbuffer.c
@@ -0,0 +1,172 @@
+/*
+ * QBuffer unit-tests.
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include <check.h>
+
+#include "qbuffer.h"
+#include "qemu-common.h"
+
+const char data[] = "some data";
+
+START_TEST(qbuffer_from_data_test)
+{
+ QBuffer *qbuffer;
+
+ qbuffer = qbuffer_from_data(data, sizeof(data));
+ fail_unless(qbuffer != NULL);
+ fail_unless(qbuffer->base.refcnt == 1);
+ fail_unless(memcmp(data, qbuffer->data, sizeof(data)) == 0);
+ fail_unless(qbuffer->size == sizeof(data));
+ fail_unless(qobject_type(QOBJECT(qbuffer)) == QTYPE_QBUFFER);
+
+ /* destroy doesn't exit yet */
+ qemu_free(qbuffer->data);
+ qemu_free(qbuffer);
+}
+END_TEST
+
+START_TEST(qbuffer_destroy_test)
+{
+ QBuffer *qbuffer = qbuffer_from_data(data, sizeof(data));
+
+ QDECREF(qbuffer);
+}
+END_TEST
+
+START_TEST(qbuffer_get_data_test)
+{
+ QBuffer *qbuffer;
+ const void *ret_data;
+
+ qbuffer = qbuffer_from_data(data, sizeof(data));
+ ret_data = qbuffer_get_data(qbuffer);
+ fail_unless(memcmp(ret_data, data, sizeof(data)) == 0);
+
+ QDECREF(qbuffer);
+}
+END_TEST
+
+START_TEST(qbuffer_get_size_test)
+{
+ QBuffer *qbuffer;
+
+ qbuffer = qbuffer_from_data(data, sizeof(data));
+ fail_unless(qbuffer_get_size(qbuffer) == sizeof(data));
+
+ QDECREF(qbuffer);
+}
+END_TEST
+
+START_TEST(qbuffer_from_qstring_test)
+{
+ const struct {
+ const char *encoded;
+ const char *decoded;
+ } pattern[3] = {
+ {
+ .encoded = "SGVsbG8sIFFCdWZmZXIhCg==",
+ .decoded = "Hello, QBuffer!",
+ },
+ {
+ .encoded = "SGVsbG8gUUJ1ZmZlcgo=",
+ .decoded = "Hello QBuffer",
+ },
+ {
+ .encoded = "SGVsbG8gUUJ1ZmZlciEK===",
+ .decoded = "Hello QBuffer!",
+ },
+ };
+ QBuffer *qbuffer;
+ QString *qstring;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pattern); i++) {
+ qstring = qstring_from_str(pattern[i].encoded);
+ qbuffer = qbuffer_from_qstring(qstring);
+ QDECREF(qstring);
+
+ fail_unless(qbuffer != NULL);
+ fail_unless(memcmp(qbuffer_get_data(qbuffer), pattern[i].decoded,
+ sizeof(pattern[i].decoded)) == 0);
+
+ QDECREF(qbuffer);
+ }
+}
+END_TEST
+
+START_TEST(qbuffer_from_invalid_qstring_test)
+{
+ const char *pattern[] = {
+ "SGVsbG8sIFFCdWZmZXIhC",
+ "SGVsbG8gU=UJ1ZmZlcgo",
+ "SGVsbG8gUUJ1*ZmZlciEK",
+ };
+ QBuffer *qbuffer;
+ QString *qstring;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pattern); i++) {
+ qstring = qstring_from_str(pattern[i]);
+ qbuffer = qbuffer_from_qstring(qstring);
+ QDECREF(qstring);
+
+ fail_unless(qbuffer == NULL);
+ }
+}
+END_TEST
+
+START_TEST(qobject_to_qbuffer_test)
+{
+ QBuffer *qbuffer;
+
+ qbuffer = qbuffer_from_data(data, sizeof(data));
+ fail_unless(qobject_to_qbuffer(QOBJECT(qbuffer)) == qbuffer);
+
+ QDECREF(qbuffer);
+}
+END_TEST
+
+static Suite *qbuffer_suite(void)
+{
+ Suite *s;
+ TCase *qbuffer_public_tcase;
+
+ s = suite_create("QBuffer test-suite");
+
+ qbuffer_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qbuffer_public_tcase);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_from_data_test);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_destroy_test);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_get_data_test);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_get_size_test);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_from_qstring_test);
+ tcase_add_test(qbuffer_public_tcase, qbuffer_from_invalid_qstring_test);
+ tcase_add_test(qbuffer_public_tcase, qobject_to_qbuffer_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qbuffer_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/configure b/configure
index c0d8aa5..6957593 100755
--- a/configure
+++ b/configure
@@ -2008,7 +2008,7 @@ if test "$softmmu" = yes ; then
if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
tools="qemu-nbd\$(EXESUF) $tools"
if [ "$check_utests" = "yes" ]; then
- tools="check-qint check-qstring check-qdict check-qlist $tools"
+ tools="check-qint check-qstring check-qdict check-qlist check-qbuffer
$tools"
tools="check-qfloat check-qjson $tools"
fi
fi
diff --git a/qbuffer.c b/qbuffer.c
new file mode 100644
index 0000000..704d170
--- /dev/null
+++ b/qbuffer.c
@@ -0,0 +1,116 @@
+/*
+ * QBuffer Module
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qbuffer.h"
+#include "qobject.h"
+#include "qemu-common.h"
+#include "base64.h"
+
+static void qbuffer_destroy_obj(QObject *obj);
+
+static const QType qbuffer_type = {
+ .code = QTYPE_QBUFFER,
+ .destroy = qbuffer_destroy_obj,
+};
+
+/**
+ * qbuffer_from_data(): Create a new QBuffer from an existing data blob
+ *
+ * Returns strong reference.
+ */
+QBuffer *qbuffer_from_data(const void *data, size_t size)
+{
+ QBuffer *qb;
+
+ qb = qemu_malloc(sizeof(*qb));
+ qb->data = qemu_malloc(size);
+ memcpy(qb->data, data, size);
+ qb->size = size;
+ QOBJECT_INIT(qb, &qbuffer_type);
+
+ return qb;
+}
+
+/**
+ * qbuffer_from_qstring(): Create a new QBuffer from a QString object that
+ * contains the data as a stream of hex-encoded bytes
+ *
+ * Returns strong reference.
+ */
+QBuffer *qbuffer_from_qstring(const QString *string)
+{
+ const char *str = qstring_get_str(string);
+ size_t str_len;
+ QBuffer *qb;
+
+ qb = qemu_malloc(sizeof(*qb));
+
+ str_len = strlen(str);
+ while (str_len > 0 && str[str_len - 1] == '=') {
+ str_len--;
+ }
+ qb->size = (str_len / 4) * 3 + ((str_len % 4) * 3) / 4;
+ qb->data = qemu_malloc(qb->size);
+
+ QOBJECT_INIT(qb, &qbuffer_type);
+
+ if (base64_decode(str, str_len, qb->data) < 0) {
+ qbuffer_destroy_obj(QOBJECT(qb));
+ return NULL;
+ }
+
+ return qb;
+}
+
+/**
+ * qbuffer_get_data(): Return pointer to stored data
+ *
+ * NOTE: Should be used with caution, if the object is deallocated
+ * this pointer becomes invalid.
+ */
+const void *qbuffer_get_data(const QBuffer *qb)
+{
+ return qb->data;
+}
+
+/**
+ * qbuffer_get_size(): Return size of stored data
+ */
+size_t qbuffer_get_size(const QBuffer *qb)
+{
+ return qb->size;
+}
+
+/**
+ * qobject_to_qbool(): Convert a QObject into a QBuffer
+ */
+QBuffer *qobject_to_qbuffer(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QBUFFER)
+ return NULL;
+
+ return container_of(obj, QBuffer, base);
+}
+
+/**
+ * qbuffer_destroy_obj(): Free all memory allocated by a QBuffer object
+ */
+static void qbuffer_destroy_obj(QObject *obj)
+{
+ QBuffer *qb;
+
+ assert(obj != NULL);
+ qb = qobject_to_qbuffer(obj);
+ qemu_free(qb->data);
+ qemu_free(qb);
+}
diff --git a/qbuffer.h b/qbuffer.h
new file mode 100644
index 0000000..2e01078
--- /dev/null
+++ b/qbuffer.h
@@ -0,0 +1,33 @@
+/*
+ * QBuffer Module
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QBUFFER_H
+#define QBUFFER_H
+
+#include <stdint.h>
+#include "qobject.h"
+#include "qstring.h"
+
+typedef struct QBuffer {
+ QObject_HEAD;
+ void *data;
+ size_t size;
+} QBuffer;
+
+QBuffer *qbuffer_from_data(const void *data, size_t size);
+QBuffer *qbuffer_from_qstring(const QString *string);
+const void *qbuffer_get_data(const QBuffer *qb);
+size_t qbuffer_get_size(const QBuffer *qb);
+QBuffer *qobject_to_qbuffer(const QObject *obj);
+
+#endif /* QBUFFER_H */
diff --git a/qjson.c b/qjson.c
index e4ee433..2996707 100644
--- a/qjson.c
+++ b/qjson.c
@@ -19,7 +19,9 @@
#include "qlist.h"
#include "qbool.h"
#include "qfloat.h"
+#include "qbuffer.h"
#include "qdict.h"
+#include "base64.h"
typedef struct JSONParsingState
{
@@ -238,6 +240,19 @@ static void to_json(const QObject *obj, QString *str)
}
break;
}
+ case QTYPE_QBUFFER: {
+ QBuffer *val = qobject_to_qbuffer(obj);
+ size_t data_size = qbuffer_get_size(val);
+ size_t str_len = ((data_size + 2) / 3) * 4;
+ char *buffer = qemu_malloc(str_len + 1);
+
+ base64_encode(qbuffer_get_data(val), data_size, buffer);
+ qstring_append(str, "{\"__class__\": \"buffer\", \"data\": \"");
+ qstring_append(str, buffer);
+ qstring_append(str, "\"}");
+ qemu_free(buffer);
+ break;
+ }
case QTYPE_QERROR:
/* XXX: should QError be emitted? */
case QTYPE_NONE:
diff --git a/qobject.h b/qobject.h
index d42386d..4ec932b 100644
--- a/qobject.h
+++ b/qobject.h
@@ -44,6 +44,7 @@ typedef enum {
QTYPE_QFLOAT,
QTYPE_QBOOL,
QTYPE_QERROR,
+ QTYPE_QBUFFER,
} qtype_code;
struct QObject;
--
1.6.0.2
- Re: [Qemu-devel] [PATCH v4 11/23] monitor: Add completion support for option lists, (continued)
[Qemu-devel] [PATCH v4 15/23] monitor: Establish cmd flags and convert the async tag, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 16/23] monitor: Allow to exclude commands from QMP, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 14/23] monitor: return length of printed string via monitor_[v]printf, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 17/23] Add base64 encoder/decoder, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 21/23] QMP: Teach basic capability negotiation to python example, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 12/23] monitor: Add completion for qdev paths, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 19/23] QMP: Add QBuffer,
Jan Kiszka <=
[Qemu-devel] [PATCH v4 18/23] QMP: Reserve namespace for complex object classes, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 22/23] QMP: Fix python helper /wrt long return strings, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 23/23] QMP: Add support for buffer class to qmp python helper, Jan Kiszka, 2010/06/15
[Qemu-devel] [PATCH v4 20/23] monitor: Add basic device state visualization, Jan Kiszka, 2010/06/15
Re: [Qemu-devel] [PATCH v4 00/23] qdev path reworks & basic device state visualization, Markus Armbruster, 2010/06/23