From patchwork Fri May 14 13:20:47 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kiszka X-Patchwork-Id: 52615 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E403EB7DC6 for ; Fri, 14 May 2010 23:36:28 +1000 (EST) Received: from localhost ([127.0.0.1]:54722 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OCv3W-0005Tg-Ly for incoming@patchwork.ozlabs.org; Fri, 14 May 2010 09:35:58 -0400 Received: from [140.186.70.92] (port=36501 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OCup7-0003cP-9e for qemu-devel@nongnu.org; Fri, 14 May 2010 09:21:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OCuoy-000292-Ag for qemu-devel@nongnu.org; Fri, 14 May 2010 09:21:05 -0400 Received: from goliath.siemens.de ([192.35.17.28]:18422) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OCuox-00027c-QY for qemu-devel@nongnu.org; Fri, 14 May 2010 09:20:56 -0400 Received: from mail2.siemens.de (localhost [127.0.0.1]) by goliath.siemens.de (8.12.11.20060308/8.12.11) with ESMTP id o4EDKrFO000337; Fri, 14 May 2010 15:20:53 +0200 Received: from localhost.localdomain (mchn012c.ww002.siemens.net [139.25.109.167] (may be forged)) by mail2.siemens.de (8.12.11.20060308/8.12.11) with ESMTP id o4EDKqWI014498; Fri, 14 May 2010 15:20:53 +0200 From: Jan Kiszka To: qemu-devel@nongnu.org, Anthony Liguori Date: Fri, 14 May 2010 15:20:47 +0200 Message-Id: <6e14cbfe3764b46d9bd6d2db61d41fd9c85dd54e.1273843151.git.jan.kiszka@siemens.com> X-Mailer: git-send-email 1.6.0.2 In-Reply-To: References: In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Avi Kivity , Juan Quintela , Markus Armbruster , Luiz Capitulino Subject: [Qemu-devel] [PATCH 3/8] Add QBuffer X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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. To decode this kind of strings back to a QBuffer, the receiving side has to be aware of their semantic, which is normally no problem within QMP. The first use case of this type is pushing the content of buffers that are part of a device state into a qdict. Signed-off-by: Jan Kiszka --- Makefile | 3 +- Makefile.objs | 2 +- check-qbuffer.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure | 2 +- qbuffer.c | 116 +++++++++++++++++++++++++++++++++++++ qbuffer.h | 33 +++++++++++ qjson.c | 16 +++++ qobject.h | 1 + 8 files changed, 342 insertions(+), 3 deletions(-) create mode 100644 check-qbuffer.c create mode 100644 qbuffer.c create mode 100644 qbuffer.h diff --git a/Makefile b/Makefile index eb9e02b..065c1a5 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,8 @@ 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 3d2a27a..52c8ec7 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/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 + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ +#include + +#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 36d028f..9ff9308 100755 --- a/configure +++ b/configure @@ -2280,7 +2280,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 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 + * + * 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 + * + * 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 +#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 483c667..4d1c21a 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 { @@ -235,6 +237,20 @@ 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 + 3); + + buffer[0] = '"'; + base64_encode(qbuffer_get_data(val), data_size, buffer + 1); + buffer[str_len + 1] = '"'; + buffer[str_len + 2] = 0; + qstring_append(str, buffer); + qemu_free(buffer); + break; + } case QTYPE_QERROR: /* XXX: should QError be emitted? */ case QTYPE_NONE: diff --git a/qobject.h b/qobject.h index 07de211..45c4fa0 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;