[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v2 4/6] QMP: Add QBuffer
From: |
Jan Kiszka |
Subject: |
[Qemu-devel] [PATCH v2 4/6] QMP: Add QBuffer |
Date: |
Fri, 26 Aug 2011 20:23:32 +0200 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686 (x86_64); de; rv:1.8.1.12) Gecko/20080226 SUSE/2.0.0.12-1.1 Thunderbird/2.0.0.12 Mnenhy/0.7.5.666 |
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.
CC: Luiz Capitulino <address@hidden>
Signed-off-by: Jan Kiszka <address@hidden>
---
Changes in v2:
- fixed unit tests
Makefile | 5 +-
Makefile.objs | 2 +-
QMP/qmp-spec.txt | 10 +++-
check-qbuffer.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 2 +-
qbuffer.c | 117 ++++++++++++++++++++++++++++++++++++
qbuffer.h | 33 ++++++++++
qjson.c | 15 +++++
qobject.h | 1 +
9 files changed, 352 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 8606849..9dc2cf4 100644
--- a/Makefile
+++ b/Makefile
@@ -151,7 +151,7 @@ qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o
$(oslib-obj-y) $(trac
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN
$@")
-check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o
check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
+check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o
check-qjson.o test-coroutine.o check-qbuffer.o: $(GENERATED_HEADERS)
CHECK_PROG_DEPS = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o
@@ -160,7 +160,8 @@ check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o
$(CHECK_PROG_DEPS)
check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
-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 error.o qerror.o
qemu-error.o $(CHECK_PROG_DEPS)
+check-qbuffer: check-qbuffer.o qbuffer.o qstring.o base64.o $(CHECK_PROG_DEPS)
+check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o
qjson.o qbuffer.o base64.o json-streamer.o json-lexer.o json-parser.o error.o
qerror.o qemu-error.o $(CHECK_PROG_DEPS)
test-coroutine: test-coroutine.o qemu-timer-common.o async.o
$(coroutine-obj-y) $(CHECK_PROG_DEPS)
$(qapi-obj-y): $(GENERATED_HEADERS)
diff --git a/Makefile.objs b/Makefile.objs
index 41febf6..dc79057 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 error.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..0876e9f
--- /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 */
+ g_free(qbuffer->data);
+ g_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 1340c33..3521898 100755
--- a/configure
+++ b/configure
@@ -2630,7 +2630,7 @@ if test "$softmmu" = yes ; then
tools="qemu-ga\$(EXESUF) $tools"
fi
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..4252a1e
--- /dev/null
+++ b/qbuffer.c
@@ -0,0 +1,117 @@
+/*
+ * 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 = g_malloc(sizeof(*qb));
+ qb->data = g_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 = g_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 = g_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);
+ g_free(qb->data);
+ g_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 f9c8e77..d415f83 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
{
@@ -268,6 +270,19 @@ static void to_json(const QObject *obj, QString *str, int
pretty, int indent)
}
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 = g_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, "\"}");
+ g_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;
[Qemu-devel] [PATCH 5/6] monitor: Add basic device state visualization, Jan Kiszka, 2011/08/26
[Qemu-devel] [PATCH 6/6] qdev: Generate IDs for anonymous devices, Jan Kiszka, 2011/08/26