@@ -144,14 +144,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
@@ -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
@@ -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
===============
new file mode 100644
@@ -0,0 +1,172 @@
+/*
+ * QBuffer unit-tests.
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * 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;
+}
@@ -2290,7 +2290,7 @@ if test `expr "$target_list" : ".*softmmu.*"` != 0 ; 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
new file mode 100644
@@ -0,0 +1,116 @@
+/*
+ * QBuffer Module
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * 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);
+}
new file mode 100644
@@ -0,0 +1,33 @@
+/*
+ * QBuffer Module
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ * Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * 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 */
@@ -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
{
@@ -235,6 +237,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:
@@ -44,6 +44,7 @@ typedef enum {
QTYPE_QFLOAT,
QTYPE_QBOOL,
QTYPE_QERROR,
+ QTYPE_QBUFFER,
} qtype_code;
struct QObject;