qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors
@ 2013-03-28 11:38 Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 1/8] Move some contents of savevm.c to qemu-file.c Stefan Berger
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

This patch series implements ASN.1 BER visitors for encoding and decoding
of data into byte streams.

This is the last post of this series with some issues fixed, the visitor_optional
implemented and all possible tests in test-visitor-serialization passing.

   Stefan


Stefan Berger (8):
  Move some contents of savevm.c to qemu-file.c
  3 new file wrappers
  QEMUSizedBuffer
  QAPI: add type_sized_buffer
  ASN.1 output visitor
  ASN.1 input visitor
  Extend test-visitor-serialization with ASN.1 visitor(s)
  ASN.1 specific test cases

 configure                          |    2 +-
 include/migration/qemu-file.h      |   22 +
 include/qapi/ber-input-visitor.h   |   30 +
 include/qapi/ber-output-visitor.h  |   32 +
 include/qapi/ber.h                 |  113 ++++
 include/qapi/visitor-impl.h        |    3 +
 include/qapi/visitor.h             |    3 +
 include/qemu-common.h              |   14 +
 qapi/Makefile.objs                 |    1 +
 qapi/ber-common.c                  |   86 +++
 qapi/ber-common.h                  |   29 +
 qapi/ber-input-visitor.c           | 1153 ++++++++++++++++++++++++++++++++++
 qapi/ber-output-visitor.c          |  705 +++++++++++++++++++++
 qapi/qapi-visit-core.c             |    8 +
 savevm.c                           |  690 --------------------
 tests/Makefile                     |   15 +-
 tests/test-ber-visitor.c           |  894 ++++++++++++++++++++++++++
 tests/test-visitor-serialization.c |   78 +++
 util/Makefile.objs                 |    1 +
 util/qemu-file.c                   | 1214 ++++++++++++++++++++++++++++++++++++
 20 files changed, 4401 insertions(+), 692 deletions(-)
 create mode 100644 include/qapi/ber-input-visitor.h
 create mode 100644 include/qapi/ber-output-visitor.h
 create mode 100644 include/qapi/ber.h
 create mode 100644 qapi/ber-common.c
 create mode 100644 qapi/ber-common.h
 create mode 100644 qapi/ber-input-visitor.c
 create mode 100644 qapi/ber-output-visitor.c
 create mode 100644 tests/test-ber-visitor.c
 create mode 100644 util/qemu-file.c

-- 
1.7.11.7

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 1/8] Move some contents of savevm.c to qemu-file.c
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 2/8] 3 new file wrappers Stefan Berger
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, pbonzini, mst

This patch reorganizes qemu file operations to be in their own source file
instead of being lumped in savevm.c.  Besides being more logical for maintenance
it also makes it easier for future users of the file functions to add tests.

Cc: pbonzini@redhat.com
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 include/migration/qemu-file.h |   6 +
 savevm.c                      | 690 ----------------------------------------
 util/Makefile.objs            |   1 +
 util/qemu-file.c              | 715 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 722 insertions(+), 690 deletions(-)
 create mode 100644 util/qemu-file.c

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index df81261..7194b84 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -98,6 +98,12 @@ void qemu_file_reset_rate_limit(QEMUFile *f);
 void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
 int64_t qemu_file_get_rate_limit(QEMUFile *f);
 int qemu_file_get_error(QEMUFile *f);
+void qemu_file_set_error(QEMUFile *f, int ret);
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable);
+int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset);
+int qemu_peek_byte(QEMUFile *f, int offset);
+void qemu_file_skip(QEMUFile *f, int size);
+void qemu_fflush(QEMUFile *f);
 
 static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
 {
diff --git a/savevm.c b/savevm.c
index 35c8d1e..8d78c3c 100644
--- a/savevm.c
+++ b/savevm.c
@@ -109,696 +109,6 @@ void qemu_announce_self(void)
 	qemu_announce_self_once(&timer);
 }
 
-/***********************************************************/
-/* savevm/loadvm support */
-
-#define IO_BUF_SIZE 32768
-
-struct QEMUFile {
-    const QEMUFileOps *ops;
-    void *opaque;
-    int is_write;
-
-    int64_t bytes_xfer;
-    int64_t xfer_limit;
-
-    int64_t pos; /* start of buffer when writing, end of buffer
-                    when reading */
-    int buf_index;
-    int buf_size; /* 0 when writing */
-    uint8_t buf[IO_BUF_SIZE];
-
-    int last_error;
-};
-
-typedef struct QEMUFileStdio
-{
-    FILE *stdio_file;
-    QEMUFile *file;
-} QEMUFileStdio;
-
-typedef struct QEMUFileSocket
-{
-    int fd;
-    QEMUFile *file;
-} QEMUFileSocket;
-
-typedef struct {
-    Coroutine *co;
-    int fd;
-} FDYieldUntilData;
-
-static void fd_coroutine_enter(void *opaque)
-{
-    FDYieldUntilData *data = opaque;
-    qemu_set_fd_handler(data->fd, NULL, NULL, NULL);
-    qemu_coroutine_enter(data->co, NULL);
-}
-
-/**
- * Yield until a file descriptor becomes readable
- *
- * Note that this function clobbers the handlers for the file descriptor.
- */
-static void coroutine_fn yield_until_fd_readable(int fd)
-{
-    FDYieldUntilData data;
-
-    assert(qemu_in_coroutine());
-    data.co = qemu_coroutine_self();
-    data.fd = fd;
-    qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data);
-    qemu_coroutine_yield();
-}
-
-static int socket_get_fd(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-
-    return s->fd;
-}
-
-static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-
-    for (;;) {
-        len = qemu_recv(s->fd, buf, size, 0);
-        if (len != -1) {
-            break;
-        }
-        if (socket_error() == EAGAIN) {
-            yield_until_fd_readable(s->fd);
-        } else if (socket_error() != EINTR) {
-            break;
-        }
-    }
-
-    if (len == -1) {
-        len = -socket_error();
-    }
-    return len;
-}
-
-static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-
-    len = qemu_send_full(s->fd, buf, size, 0);
-    if (len < size) {
-        len = -socket_error();
-    }
-    return len;
-}
-
-static int socket_close(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-    closesocket(s->fd);
-    g_free(s);
-    return 0;
-}
-
-static int stdio_get_fd(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-
-    return fileno(s->stdio_file);
-}
-
-static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    FILE *fp = s->stdio_file;
-    int bytes;
-
-    for (;;) {
-        clearerr(fp);
-        bytes = fread(buf, 1, size, fp);
-        if (bytes != 0 || !ferror(fp)) {
-            break;
-        }
-        if (errno == EAGAIN) {
-            yield_until_fd_readable(fileno(fp));
-        } else if (errno != EINTR) {
-            break;
-        }
-    }
-    return bytes;
-}
-
-static int stdio_pclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    int ret;
-    ret = pclose(s->stdio_file);
-    if (ret == -1) {
-        ret = -errno;
-    } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
-        /* close succeeded, but non-zero exit code: */
-        ret = -EIO; /* fake errno value */
-    }
-    g_free(s);
-    return ret;
-}
-
-static int stdio_fclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    int ret = 0;
-
-    if (s->file->ops->put_buffer) {
-        int fd = fileno(s->stdio_file);
-        struct stat st;
-
-        ret = fstat(fd, &st);
-        if (ret == 0 && S_ISREG(st.st_mode)) {
-            /*
-             * If the file handle is a regular file make sure the
-             * data is flushed to disk before signaling success.
-             */
-            ret = fsync(fd);
-            if (ret != 0) {
-                ret = -errno;
-                return ret;
-            }
-        }
-    }
-    if (fclose(s->stdio_file) == EOF) {
-        ret = -errno;
-    }
-    g_free(s);
-    return ret;
-}
-
-static const QEMUFileOps stdio_pipe_read_ops = {
-    .get_fd =     stdio_get_fd,
-    .get_buffer = stdio_get_buffer,
-    .close =      stdio_pclose
-};
-
-static const QEMUFileOps stdio_pipe_write_ops = {
-    .get_fd =     stdio_get_fd,
-    .put_buffer = stdio_put_buffer,
-    .close =      stdio_pclose
-};
-
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
-    FILE *stdio_file;
-    QEMUFileStdio *s;
-
-    stdio_file = popen(command, mode);
-    if (stdio_file == NULL) {
-        return NULL;
-    }
-
-    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
-        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = stdio_file;
-
-    if(mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
-    }
-    return s->file;
-}
-
-static const QEMUFileOps stdio_file_read_ops = {
-    .get_fd =     stdio_get_fd,
-    .get_buffer = stdio_get_buffer,
-    .close =      stdio_fclose
-};
-
-static const QEMUFileOps stdio_file_write_ops = {
-    .get_fd =     stdio_get_fd,
-    .put_buffer = stdio_put_buffer,
-    .close =      stdio_fclose
-};
-
-QEMUFile *qemu_fdopen(int fd, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (mode == NULL ||
-	(mode[0] != 'r' && mode[0] != 'w') ||
-	mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-    s->stdio_file = fdopen(fd, mode);
-    if (!s->stdio_file)
-        goto fail;
-
-    if(mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
-    }
-    return s->file;
-
-fail:
-    g_free(s);
-    return NULL;
-}
-
-static const QEMUFileOps socket_read_ops = {
-    .get_fd =     socket_get_fd,
-    .get_buffer = socket_get_buffer,
-    .close =      socket_close
-};
-
-static const QEMUFileOps socket_write_ops = {
-    .get_fd =     socket_get_fd,
-    .put_buffer = socket_put_buffer,
-    .close =      socket_close
-};
-
-QEMUFile *qemu_fopen_socket(int fd, const char *mode)
-{
-    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
-
-    if (mode == NULL ||
-        (mode[0] != 'r' && mode[0] != 'w') ||
-        mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s->fd = fd;
-    if (mode[0] == 'w') {
-        socket_set_block(s->fd);
-        s->file = qemu_fopen_ops(s, &socket_write_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &socket_read_ops);
-    }
-    return s->file;
-}
-
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (mode == NULL ||
-	(mode[0] != 'r' && mode[0] != 'w') ||
-	mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = fopen(filename, mode);
-    if (!s->stdio_file)
-        goto fail;
-    
-    if(mode[0] == 'w') {
-        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
-    }
-    return s->file;
-fail:
-    g_free(s);
-    return NULL;
-}
-
-static int block_put_buffer(void *opaque, const uint8_t *buf,
-                           int64_t pos, int size)
-{
-    bdrv_save_vmstate(opaque, buf, pos, size);
-    return size;
-}
-
-static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    return bdrv_load_vmstate(opaque, buf, pos, size);
-}
-
-static int bdrv_fclose(void *opaque)
-{
-    return bdrv_flush(opaque);
-}
-
-static const QEMUFileOps bdrv_read_ops = {
-    .get_buffer = block_get_buffer,
-    .close =      bdrv_fclose
-};
-
-static const QEMUFileOps bdrv_write_ops = {
-    .put_buffer = block_put_buffer,
-    .close =      bdrv_fclose
-};
-
-static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
-{
-    if (is_writable)
-        return qemu_fopen_ops(bs, &bdrv_write_ops);
-    return qemu_fopen_ops(bs, &bdrv_read_ops);
-}
-
-QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
-{
-    QEMUFile *f;
-
-    f = g_malloc0(sizeof(QEMUFile));
-
-    f->opaque = opaque;
-    f->ops = ops;
-    f->is_write = 0;
-    return f;
-}
-
-int qemu_file_get_error(QEMUFile *f)
-{
-    return f->last_error;
-}
-
-static void qemu_file_set_error(QEMUFile *f, int ret)
-{
-    if (f->last_error == 0) {
-        f->last_error = ret;
-    }
-}
-
-/** Flushes QEMUFile buffer
- *
- */
-static void qemu_fflush(QEMUFile *f)
-{
-    int ret = 0;
-
-    if (!f->ops->put_buffer) {
-        return;
-    }
-    if (f->is_write && f->buf_index > 0) {
-        ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
-        if (ret >= 0) {
-            f->pos += f->buf_index;
-        }
-        f->buf_index = 0;
-    }
-    if (ret < 0) {
-        qemu_file_set_error(f, ret);
-    }
-}
-
-static void qemu_fill_buffer(QEMUFile *f)
-{
-    int len;
-    int pending;
-
-    if (!f->ops->get_buffer)
-        return;
-
-    if (f->is_write)
-        abort();
-
-    pending = f->buf_size - f->buf_index;
-    if (pending > 0) {
-        memmove(f->buf, f->buf + f->buf_index, pending);
-    }
-    f->buf_index = 0;
-    f->buf_size = pending;
-
-    len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
-                        IO_BUF_SIZE - pending);
-    if (len > 0) {
-        f->buf_size += len;
-        f->pos += len;
-    } else if (len == 0) {
-        qemu_file_set_error(f, -EIO);
-    } else if (len != -EAGAIN)
-        qemu_file_set_error(f, len);
-}
-
-int qemu_get_fd(QEMUFile *f)
-{
-    if (f->ops->get_fd) {
-        return f->ops->get_fd(f->opaque);
-    }
-    return -1;
-}
-
-/** Closes the file
- *
- * Returns negative error value if any error happened on previous operations or
- * while closing the file. Returns 0 or positive number on success.
- *
- * The meaning of return value on success depends on the specific backend
- * being used.
- */
-int qemu_fclose(QEMUFile *f)
-{
-    int ret;
-    qemu_fflush(f);
-    ret = qemu_file_get_error(f);
-
-    if (f->ops->close) {
-        int ret2 = f->ops->close(f->opaque);
-        if (ret >= 0) {
-            ret = ret2;
-        }
-    }
-    /* If any error was spotted before closing, we should report it
-     * instead of the close() return value.
-     */
-    if (f->last_error) {
-        ret = f->last_error;
-    }
-    g_free(f);
-    return ret;
-}
-
-void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
-{
-    int l;
-
-    if (f->last_error) {
-        return;
-    }
-
-    if (f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
-    }
-
-    while (size > 0) {
-        l = IO_BUF_SIZE - f->buf_index;
-        if (l > size)
-            l = size;
-        memcpy(f->buf + f->buf_index, buf, l);
-        f->is_write = 1;
-        f->buf_index += l;
-        f->bytes_xfer += l;
-        buf += l;
-        size -= l;
-        if (f->buf_index >= IO_BUF_SIZE) {
-            qemu_fflush(f);
-            if (qemu_file_get_error(f)) {
-                break;
-            }
-        }
-    }
-}
-
-void qemu_put_byte(QEMUFile *f, int v)
-{
-    if (f->last_error) {
-        return;
-    }
-
-    if (f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
-    }
-
-    f->buf[f->buf_index++] = v;
-    f->is_write = 1;
-    if (f->buf_index >= IO_BUF_SIZE) {
-        qemu_fflush(f);
-    }
-}
-
-static void qemu_file_skip(QEMUFile *f, int size)
-{
-    if (f->buf_index + size <= f->buf_size) {
-        f->buf_index += size;
-    }
-}
-
-static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
-{
-    int pending;
-    int index;
-
-    if (f->is_write) {
-        abort();
-    }
-
-    index = f->buf_index + offset;
-    pending = f->buf_size - index;
-    if (pending < size) {
-        qemu_fill_buffer(f);
-        index = f->buf_index + offset;
-        pending = f->buf_size - index;
-    }
-
-    if (pending <= 0) {
-        return 0;
-    }
-    if (size > pending) {
-        size = pending;
-    }
-
-    memcpy(buf, f->buf + index, size);
-    return size;
-}
-
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
-{
-    int pending = size;
-    int done = 0;
-
-    while (pending > 0) {
-        int res;
-
-        res = qemu_peek_buffer(f, buf, pending, 0);
-        if (res == 0) {
-            return done;
-        }
-        qemu_file_skip(f, res);
-        buf += res;
-        pending -= res;
-        done += res;
-    }
-    return done;
-}
-
-static int qemu_peek_byte(QEMUFile *f, int offset)
-{
-    int index = f->buf_index + offset;
-
-    if (f->is_write) {
-        abort();
-    }
-
-    if (index >= f->buf_size) {
-        qemu_fill_buffer(f);
-        index = f->buf_index + offset;
-        if (index >= f->buf_size) {
-            return 0;
-        }
-    }
-    return f->buf[index];
-}
-
-int qemu_get_byte(QEMUFile *f)
-{
-    int result;
-
-    result = qemu_peek_byte(f, 0);
-    qemu_file_skip(f, 1);
-    return result;
-}
-
-int64_t qemu_ftell(QEMUFile *f)
-{
-    qemu_fflush(f);
-    return f->pos;
-}
-
-int qemu_file_rate_limit(QEMUFile *f)
-{
-    if (qemu_file_get_error(f)) {
-        return 1;
-    }
-    if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
-        return 1;
-    }
-    return 0;
-}
-
-int64_t qemu_file_get_rate_limit(QEMUFile *f)
-{
-    return f->xfer_limit;
-}
-
-void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
-{
-    f->xfer_limit = limit;
-}
-
-void qemu_file_reset_rate_limit(QEMUFile *f)
-{
-    f->bytes_xfer = 0;
-}
-
-void qemu_put_be16(QEMUFile *f, unsigned int v)
-{
-    qemu_put_byte(f, v >> 8);
-    qemu_put_byte(f, v);
-}
-
-void qemu_put_be32(QEMUFile *f, unsigned int v)
-{
-    qemu_put_byte(f, v >> 24);
-    qemu_put_byte(f, v >> 16);
-    qemu_put_byte(f, v >> 8);
-    qemu_put_byte(f, v);
-}
-
-void qemu_put_be64(QEMUFile *f, uint64_t v)
-{
-    qemu_put_be32(f, v >> 32);
-    qemu_put_be32(f, v);
-}
-
-unsigned int qemu_get_be16(QEMUFile *f)
-{
-    unsigned int v;
-    v = qemu_get_byte(f) << 8;
-    v |= qemu_get_byte(f);
-    return v;
-}
-
-unsigned int qemu_get_be32(QEMUFile *f)
-{
-    unsigned int v;
-    v = qemu_get_byte(f) << 24;
-    v |= qemu_get_byte(f) << 16;
-    v |= qemu_get_byte(f) << 8;
-    v |= qemu_get_byte(f);
-    return v;
-}
-
-uint64_t qemu_get_be64(QEMUFile *f)
-{
-    uint64_t v;
-    v = (uint64_t)qemu_get_be32(f) << 32;
-    v |= qemu_get_be32(f);
-    return v;
-}
-
-
 /* timer */
 
 void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 557bda7..df02fba 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -10,3 +10,4 @@ util-obj-$(CONFIG_POSIX) += compatfd.o
 util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o
 util-obj-y += qemu-option.o qemu-progress.o
 util-obj-y += hexdump.o
+util-obj-y += qemu-file.o
diff --git a/util/qemu-file.c b/util/qemu-file.c
new file mode 100644
index 0000000..4fed6d5
--- /dev/null
+++ b/util/qemu-file.c
@@ -0,0 +1,715 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "block/block.h"
+#include "qemu/sockets.h"
+
+
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+    const QEMUFileOps *ops;
+    void *opaque;
+    int is_write;
+
+    int64_t bytes_xfer;
+    int64_t xfer_limit;
+
+    int64_t pos; /* start of buffer when writing, end of buffer
+                    when reading */
+    int buf_index;
+    int buf_size; /* 0 when writing */
+    uint8_t buf[IO_BUF_SIZE];
+
+    int last_error;
+};
+
+typedef struct QEMUFileStdio
+{
+    FILE *stdio_file;
+    QEMUFile *file;
+} QEMUFileStdio;
+
+typedef struct QEMUFileSocket
+{
+    int fd;
+    QEMUFile *file;
+} QEMUFileSocket;
+
+typedef struct {
+    Coroutine *co;
+    int fd;
+} FDYieldUntilData;
+
+static void fd_coroutine_enter(void *opaque)
+{
+    FDYieldUntilData *data = opaque;
+    qemu_set_fd_handler(data->fd, NULL, NULL, NULL);
+    qemu_coroutine_enter(data->co, NULL);
+}
+
+/**
+ * Yield until a file descriptor becomes readable
+ *
+ * Note that this function clobbers the handlers for the file descriptor.
+ */
+static void coroutine_fn yield_until_fd_readable(int fd)
+{
+    FDYieldUntilData data;
+
+    assert(qemu_in_coroutine());
+    data.co = qemu_coroutine_self();
+    data.fd = fd;
+    qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data);
+    qemu_coroutine_yield();
+}
+
+static int socket_get_fd(void *opaque)
+{
+    QEMUFileSocket *s = opaque;
+
+    return s->fd;
+}
+
+static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len;
+
+    for (;;) {
+        len = qemu_recv(s->fd, buf, size, 0);
+        if (len != -1) {
+            break;
+        }
+        if (socket_error() == EAGAIN) {
+            yield_until_fd_readable(s->fd);
+        } else if (socket_error() != EINTR) {
+            break;
+        }
+    }
+
+    if (len == -1) {
+        len = -socket_error();
+    }
+    return len;
+}
+
+static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len;
+
+    len = qemu_send_full(s->fd, buf, size, 0);
+    if (len < size) {
+        len = -socket_error();
+    }
+    return len;
+}
+
+static int socket_close(void *opaque)
+{
+    QEMUFileSocket *s = opaque;
+    closesocket(s->fd);
+    g_free(s);
+    return 0;
+}
+
+static int stdio_get_fd(void *opaque)
+{
+    QEMUFileStdio *s = opaque;
+
+    return fileno(s->stdio_file);
+}
+
+static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    FILE *fp = s->stdio_file;
+    int bytes;
+
+    for (;;) {
+        clearerr(fp);
+        bytes = fread(buf, 1, size, fp);
+        if (bytes != 0 || !ferror(fp)) {
+            break;
+        }
+        if (errno == EAGAIN) {
+            yield_until_fd_readable(fileno(fp));
+        } else if (errno != EINTR) {
+            break;
+        }
+    }
+    return bytes;
+}
+
+static int stdio_pclose(void *opaque)
+{
+    QEMUFileStdio *s = opaque;
+    int ret;
+    ret = pclose(s->stdio_file);
+    if (ret == -1) {
+        ret = -errno;
+    } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+        /* close succeeded, but non-zero exit code: */
+        ret = -EIO; /* fake errno value */
+    }
+    g_free(s);
+    return ret;
+}
+
+static int stdio_fclose(void *opaque)
+{
+    QEMUFileStdio *s = opaque;
+    int ret = 0;
+
+    if (s->file->ops->put_buffer) {
+        int fd = fileno(s->stdio_file);
+        struct stat st;
+
+        ret = fstat(fd, &st);
+        if (ret == 0 && S_ISREG(st.st_mode)) {
+            /*
+             * If the file handle is a regular file make sure the
+             * data is flushed to disk before signaling success.
+             */
+            ret = fsync(fd);
+            if (ret != 0) {
+                ret = -errno;
+                return ret;
+            }
+        }
+    }
+    if (fclose(s->stdio_file) == EOF) {
+        ret = -errno;
+    }
+    g_free(s);
+    return ret;
+}
+
+static const QEMUFileOps stdio_pipe_read_ops = {
+    .get_fd =     stdio_get_fd,
+    .get_buffer = stdio_get_buffer,
+    .close =      stdio_pclose
+};
+
+static const QEMUFileOps stdio_pipe_write_ops = {
+    .get_fd =     stdio_get_fd,
+    .put_buffer = stdio_put_buffer,
+    .close =      stdio_pclose
+};
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+    FILE *stdio_file;
+    QEMUFileStdio *s;
+
+    stdio_file = popen(command, mode);
+    if (stdio_file == NULL) {
+        return NULL;
+    }
+
+    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+
+    s->stdio_file = stdio_file;
+
+    if(mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
+    }
+    return s->file;
+}
+
+static const QEMUFileOps stdio_file_read_ops = {
+    .get_fd =     stdio_get_fd,
+    .get_buffer = stdio_get_buffer,
+    .close =      stdio_fclose
+};
+
+static const QEMUFileOps stdio_file_write_ops = {
+    .get_fd =     stdio_get_fd,
+    .put_buffer = stdio_put_buffer,
+    .close =      stdio_fclose
+};
+
+QEMUFile *qemu_fdopen(int fd, const char *mode)
+{
+    QEMUFileStdio *s;
+
+    if (mode == NULL ||
+	(mode[0] != 'r' && mode[0] != 'w') ||
+	mode[1] != 'b' || mode[2] != 0) {
+        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+    s->stdio_file = fdopen(fd, mode);
+    if (!s->stdio_file)
+        goto fail;
+
+    if(mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+    }
+    return s->file;
+
+fail:
+    g_free(s);
+    return NULL;
+}
+
+static const QEMUFileOps socket_read_ops = {
+    .get_fd =     socket_get_fd,
+    .get_buffer = socket_get_buffer,
+    .close =      socket_close
+};
+
+static const QEMUFileOps socket_write_ops = {
+    .get_fd =     socket_get_fd,
+    .put_buffer = socket_put_buffer,
+    .close =      socket_close
+};
+
+QEMUFile *qemu_fopen_socket(int fd, const char *mode)
+{
+    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
+
+    if (mode == NULL ||
+        (mode[0] != 'r' && mode[0] != 'w') ||
+        mode[1] != 'b' || mode[2] != 0) {
+        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s->fd = fd;
+    if (mode[0] == 'w') {
+        socket_set_block(s->fd);
+        s->file = qemu_fopen_ops(s, &socket_write_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &socket_read_ops);
+    }
+    return s->file;
+}
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+    QEMUFileStdio *s;
+
+    if (mode == NULL ||
+	(mode[0] != 'r' && mode[0] != 'w') ||
+	mode[1] != 'b' || mode[2] != 0) {
+        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+
+    s->stdio_file = fopen(filename, mode);
+    if (!s->stdio_file)
+        goto fail;
+
+    if(mode[0] == 'w') {
+        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+    }
+    return s->file;
+fail:
+    g_free(s);
+    return NULL;
+}
+
+static int block_put_buffer(void *opaque, const uint8_t *buf,
+                           int64_t pos, int size)
+{
+    bdrv_save_vmstate(opaque, buf, pos, size);
+    return size;
+}
+
+static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    return bdrv_load_vmstate(opaque, buf, pos, size);
+}
+
+static int bdrv_fclose(void *opaque)
+{
+    return bdrv_flush(opaque);
+}
+
+static const QEMUFileOps bdrv_read_ops = {
+    .get_buffer = block_get_buffer,
+    .close =      bdrv_fclose
+};
+
+static const QEMUFileOps bdrv_write_ops = {
+    .put_buffer = block_put_buffer,
+    .close =      bdrv_fclose
+};
+
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
+{
+    if (is_writable)
+        return qemu_fopen_ops(bs, &bdrv_write_ops);
+    return qemu_fopen_ops(bs, &bdrv_read_ops);
+}
+
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
+{
+    QEMUFile *f;
+
+    f = g_malloc0(sizeof(QEMUFile));
+
+    f->opaque = opaque;
+    f->ops = ops;
+    f->is_write = 0;
+    return f;
+}
+
+int qemu_file_get_error(QEMUFile *f)
+{
+    return f->last_error;
+}
+
+void qemu_file_set_error(QEMUFile *f, int ret)
+{
+    if (f->last_error == 0) {
+        f->last_error = ret;
+    }
+}
+
+/** Flushes QEMUFile buffer
+ *
+ */
+void qemu_fflush(QEMUFile *f)
+{
+    int ret = 0;
+
+    if (!f->ops->put_buffer) {
+        return;
+    }
+    if (f->is_write && f->buf_index > 0) {
+        ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
+        if (ret >= 0) {
+            f->pos += f->buf_index;
+        }
+        f->buf_index = 0;
+    }
+    if (ret < 0) {
+        qemu_file_set_error(f, ret);
+    }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+    int len;
+    int pending;
+
+    if (!f->ops->get_buffer)
+        return;
+
+    if (f->is_write)
+        abort();
+
+    pending = f->buf_size - f->buf_index;
+    if (pending > 0) {
+        memmove(f->buf, f->buf + f->buf_index, pending);
+    }
+    f->buf_index = 0;
+    f->buf_size = pending;
+
+    len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
+                        IO_BUF_SIZE - pending);
+    if (len > 0) {
+        f->buf_size += len;
+        f->pos += len;
+    } else if (len == 0) {
+        qemu_file_set_error(f, -EIO);
+    } else if (len != -EAGAIN)
+        qemu_file_set_error(f, len);
+}
+
+int qemu_get_fd(QEMUFile *f)
+{
+    if (f->ops->get_fd) {
+        return f->ops->get_fd(f->opaque);
+    }
+    return -1;
+}
+
+/** Closes the file
+ *
+ * Returns negative error value if any error happened on previous operations or
+ * while closing the file. Returns 0 or positive number on success.
+ *
+ * The meaning of return value on success depends on the specific backend
+ * being used.
+ */
+int qemu_fclose(QEMUFile *f)
+{
+    int ret;
+    qemu_fflush(f);
+    ret = qemu_file_get_error(f);
+
+    if (f->ops->close) {
+        int ret2 = f->ops->close(f->opaque);
+        if (ret >= 0) {
+            ret = ret2;
+        }
+    }
+    /* If any error was spotted before closing, we should report it
+     * instead of the close() return value.
+     */
+    if (f->last_error) {
+        ret = f->last_error;
+    }
+    g_free(f);
+    return ret;
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+    int l;
+
+    if (f->last_error) {
+        return;
+    }
+
+    if (f->is_write == 0 && f->buf_index > 0) {
+        fprintf(stderr,
+                "Attempted to write to buffer while read buffer is not empty\n");
+        abort();
+    }
+
+    while (size > 0) {
+        l = IO_BUF_SIZE - f->buf_index;
+        if (l > size)
+            l = size;
+        memcpy(f->buf + f->buf_index, buf, l);
+        f->is_write = 1;
+        f->buf_index += l;
+        f->bytes_xfer += l;
+        buf += l;
+        size -= l;
+        if (f->buf_index >= IO_BUF_SIZE) {
+            qemu_fflush(f);
+            if (qemu_file_get_error(f)) {
+                break;
+            }
+        }
+    }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+    if (f->last_error) {
+        return;
+    }
+
+    if (f->is_write == 0 && f->buf_index > 0) {
+        fprintf(stderr,
+                "Attempted to write to buffer while read buffer is not empty\n");
+        abort();
+    }
+
+    f->buf[f->buf_index++] = v;
+    f->is_write = 1;
+    if (f->buf_index >= IO_BUF_SIZE) {
+        qemu_fflush(f);
+    }
+}
+
+void qemu_file_skip(QEMUFile *f, int size)
+{
+    if (f->buf_index + size <= f->buf_size) {
+        f->buf_index += size;
+    }
+}
+
+int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+    int pending;
+    int index;
+
+    if (f->is_write) {
+        abort();
+    }
+
+    index = f->buf_index + offset;
+    pending = f->buf_size - index;
+    if (pending < size) {
+        qemu_fill_buffer(f);
+        index = f->buf_index + offset;
+        pending = f->buf_size - index;
+    }
+
+    if (pending <= 0) {
+        return 0;
+    }
+    if (size > pending) {
+        size = pending;
+    }
+
+    memcpy(buf, f->buf + index, size);
+    return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+    int pending = size;
+    int done = 0;
+
+    while (pending > 0) {
+        int res;
+
+        res = qemu_peek_buffer(f, buf, pending, 0);
+        if (res == 0) {
+            return done;
+        }
+        qemu_file_skip(f, res);
+        buf += res;
+        pending -= res;
+        done += res;
+    }
+    return done;
+}
+
+int qemu_peek_byte(QEMUFile *f, int offset)
+{
+    int index = f->buf_index + offset;
+
+    if (f->is_write) {
+        abort();
+    }
+
+    if (index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        index = f->buf_index + offset;
+        if (index >= f->buf_size) {
+            return 0;
+        }
+    }
+    return f->buf[index];
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+    int result;
+
+    result = qemu_peek_byte(f, 0);
+    qemu_file_skip(f, 1);
+    return result;
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+    qemu_fflush(f);
+    return f->pos;
+}
+
+int qemu_file_rate_limit(QEMUFile *f)
+{
+    if (qemu_file_get_error(f)) {
+        return 1;
+    }
+    if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
+        return 1;
+    }
+    return 0;
+}
+
+int64_t qemu_file_get_rate_limit(QEMUFile *f)
+{
+    return f->xfer_limit;
+}
+
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
+{
+    f->xfer_limit = limit;
+}
+
+void qemu_file_reset_rate_limit(QEMUFile *f)
+{
+    f->bytes_xfer = 0;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 24);
+    qemu_put_byte(f, v >> 16);
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+    qemu_put_be32(f, v >> 32);
+    qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 24;
+    v |= qemu_get_byte(f) << 16;
+    v |= qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+    uint64_t v;
+    v = (uint64_t)qemu_get_be32(f) << 32;
+    v |= qemu_get_be32(f);
+    return v;
+}
+
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 2/8] 3 new file wrappers
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 1/8] Move some contents of savevm.c to qemu-file.c Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 3/8] QEMUSizedBuffer Stefan Berger
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Add 3 very short file wrapper functions to make code that follows more
readable.

Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 include/migration/qemu-file.h |  3 +++
 util/qemu-file.c              | 30 ++++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 7194b84..728b6e2 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -68,6 +68,9 @@ int qemu_fclose(QEMUFile *f);
 int64_t qemu_ftell(QEMUFile *f);
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
 void qemu_put_byte(QEMUFile *f, int v);
+int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size);
+int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset);
+int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size);
 
 static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
 {
diff --git a/util/qemu-file.c b/util/qemu-file.c
index 4fed6d5..f8a54e7 100644
--- a/util/qemu-file.c
+++ b/util/qemu-file.c
@@ -713,3 +713,33 @@ uint64_t qemu_get_be64(QEMUFile *f)
     return v;
 }
 
+int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size)
+{
+    if (qemu_file_get_error(f)) {
+        return -1;
+    }
+    return qemu_get_buffer(f, buf, size);
+}
+
+int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+    if (qemu_file_get_error(f)) {
+        return -1;
+    }
+    return qemu_peek_buffer(f, buf, size, offset);
+}
+
+int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size)
+{
+    if (qemu_file_get_error(f)) {
+        return -1;
+    }
+
+    qemu_put_buffer(f, buf, size);
+
+    if (qemu_file_get_error(f)) {
+        return -1;
+    }
+
+    return size;
+}
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 3/8] QEMUSizedBuffer
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 1/8] Move some contents of savevm.c to qemu-file.c Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 2/8] 3 new file wrappers Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 4/8] QAPI: add type_sized_buffer Stefan Berger
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Using the QEMUFile interface, this patch adds support functions for operating
on in-memory sized buffers that can be written to or read from.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 include/migration/qemu-file.h |  13 ++
 include/qemu-common.h         |  12 ++
 util/qemu-file.c              | 411 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 436 insertions(+)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 728b6e2..0a297fc 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -24,6 +24,8 @@
 #ifndef QEMU_FILE_H
 #define QEMU_FILE_H 1
 
+#include <stdint.h>
+
 /* This function writes a chunk of data to a file at the given position.
  * The pos argument can be ignored if the file is only being used for
  * streaming.  The handler should try to write all of the data it can.
@@ -58,6 +60,15 @@ typedef struct QEMUFileOps {
     QEMUFileGetFD *get_fd;
 } QEMUFileOps;
 
+struct QEMUSizedBuffer {
+    struct iovec *iov;
+    size_t n_iov;
+    size_t size; /* total allocated size in all iov's */
+    size_t used; /* number of used bytes */
+};
+
+typedef struct QEMUSizedBuffer QEMUSizedBuffer;
+
 QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
 QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fdopen(int fd, const char *mode);
@@ -71,6 +82,8 @@ void qemu_put_byte(QEMUFile *f, int v);
 int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size);
 int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset);
 int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size);
+QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
+const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f);
 
 static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
 {
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 7754ee2..7794fa7 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -448,4 +448,16 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n);
 
 void hexdump(const char *buf, FILE *fp, const char *prefix, size_t size);
 
+/* QEMU Sized Buffer */
+#include "include/migration/qemu-file.h"
+QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len);
+QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *);
+void qsb_free(QEMUSizedBuffer *);
+size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length);
+size_t qsb_get_length(const QEMUSizedBuffer *qsb);
+ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count,
+                       uint8_t **buf);
+ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
+                     off_t pos, size_t count);
+
 #endif
diff --git a/util/qemu-file.c b/util/qemu-file.c
index f8a54e7..89b0614 100644
--- a/util/qemu-file.c
+++ b/util/qemu-file.c
@@ -743,3 +743,414 @@ int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size)
 
     return size;
 }
+
+
+#define QSB_CHUNK_SIZE      (1 << 10)
+#define QSB_MAX_CHUNK_SIZE  (10 * QSB_CHUNK_SIZE)
+
+/**
+ * Create a QEMUSizedBuffer
+ * This type of buffer uses scatter-gather lists internally and
+ * can grow to any size. Any data array in the scatter-gather list
+ * can hold different amount of bytes.
+ *
+ * @buffer: Optional buffer to copy into the QSB
+ * @len: size of initial buffer; if @buffer is given, buffer must
+ *       hold at least len bytes
+ *
+ * Returns a pointer to a QEMUSizedBuffer
+ */
+QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len)
+{
+    QEMUSizedBuffer *qsb;
+    size_t alloc_len, num_chunks, i, to_copy;
+    size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE)
+                        ? QSB_MAX_CHUNK_SIZE
+                        : QSB_CHUNK_SIZE;
+
+    if (len == 0) {
+        /* we want to allocate at least one chunk */
+        len = QSB_CHUNK_SIZE;
+    }
+
+    num_chunks = DIV_ROUND_UP(len, chunk_size);
+    alloc_len = num_chunks * chunk_size;
+
+    qsb = g_new0(QEMUSizedBuffer, 1);
+    qsb->iov = g_new0(struct iovec, num_chunks);
+    qsb->n_iov = num_chunks;
+
+    for (i = 0; i < num_chunks; i++) {
+        qsb->iov[i].iov_base = g_malloc0(chunk_size);
+        qsb->iov[i].iov_len = chunk_size;
+        if (buffer) {
+            to_copy = (len - qsb->used) > chunk_size
+                      ? chunk_size : (len - qsb->used);
+            memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy);
+            qsb->used += to_copy;
+        }
+    }
+
+    qsb->size = alloc_len;
+
+    return qsb;
+}
+
+/**
+ * Free the QEMUSizedBuffer
+ *
+ * @qsb: The QEMUSizedBuffer to free
+ */
+void qsb_free(QEMUSizedBuffer *qsb)
+{
+    size_t i;
+
+    if (!qsb) {
+        return;
+    }
+
+    for (i = 0; i < qsb->n_iov; i++) {
+        g_free(qsb->iov[i].iov_base);
+    }
+    g_free(qsb->iov);
+    g_free(qsb);
+}
+
+/**
+ * Get the number of of used bytes in the QEMUSizedBuffer
+ *
+ * @qsb: A QEMUSizedBuffer
+ *
+ * Returns the number of bytes currently used in this buffer
+ */
+size_t qsb_get_length(const QEMUSizedBuffer *qsb)
+{
+    return qsb->used;
+}
+
+/**
+ * Set the length of the buffer; The primary usage of this
+ * function is to truncate the number of used bytes in the buffer.
+ * The size will not be extended beyond the current  number of
+ * allocated bytes in the QEMUSizedBuffer.
+ *
+ * @qsb: A QEMUSizedBuffer
+ * @new_len : The new length of bytes in the buffer
+ *
+ * Returns the number of bytes the buffer was trucated or extended
+ * to.
+ */
+size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len)
+{
+    if (new_len <= qsb->size) {
+        qsb->used = new_len;
+    } else {
+        qsb->used = qsb->size;
+    }
+    return qsb->used;
+}
+
+/**
+ * Get the iovec that holds the data for a given position @pos.
+ *
+ * @qsb: A QEMUSizedBuffer
+ * @pos: The index of a byte in the buffer
+ * @d_off: Pointer to an offset that this function will indicate
+ *         at what position within the returned iovec the byte
+ *         is to be found
+ *
+ * Returns the index of the iovec that holds the byte at the given
+ * index @pos in the byte stream; a negative number if the iovec
+ * for the given position @pos does not exist.
+ */
+static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb,
+                             off_t pos, off_t *d_off)
+{
+    ssize_t i;
+    off_t curr = 0;
+
+    if (pos > qsb->used) {
+        return -1;
+    }
+
+    for (i = 0; i < qsb->n_iov; i++) {
+        if (curr + qsb->iov[i].iov_len > pos) {
+            *d_off = pos - curr;
+            return i;
+        }
+        curr += qsb->iov[i].iov_len;
+    }
+    return -1;
+}
+
+/*
+ * Convert the QEMUSizedBuffer into a flat buffer.
+ *
+ * Note: If at all possible, try to avoid this function since it
+ *       may unnecessarily copy memory around.
+ *
+ * @qsb: pointer to QEMUSizedBuffer
+ * @start : offset to start at
+ * @count: number of bytes to copy
+ * @buf: a pointer to an optional buffer to write into; the pointer may
+ *       point to NULL in which case the buffer will be allocated;
+ *       if buffer is provided, it must be large enough to hold @count bytes
+ *
+ * Returns the number of bytes  copied into the output buffer
+ */
+ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start,
+                       size_t count, uint8_t **buf)
+{
+    uint8_t *buffer;
+    const struct iovec *iov;
+    size_t to_copy, all_copy;
+    ssize_t index;
+    off_t s_off;
+    off_t d_off = 0;
+    char *s;
+
+    if (start > qsb->used) {
+        return 0;
+    }
+
+    all_copy = qsb->used - start;
+    if (all_copy > count) {
+        all_copy = count;
+    } else {
+        count = all_copy;
+    }
+
+    if (*buf == NULL) {
+        *buf = g_malloc(all_copy);
+    }
+    buffer = *buf;
+
+    index = qsb_get_iovec(qsb, start, &s_off);
+    if (index < 0) {
+        return 0;
+    }
+
+    while (all_copy > 0) {
+        iov = &qsb->iov[index];
+
+        s = iov->iov_base;
+
+        to_copy = iov->iov_len - s_off;
+        if (to_copy > all_copy) {
+            to_copy = all_copy;
+        }
+        memcpy(&buffer[d_off], &s[s_off], to_copy);
+
+        d_off += to_copy;
+        all_copy -= to_copy;
+
+        s_off = 0;
+        index++;
+    }
+
+    return count;
+}
+
+/**
+ * Grow the QEMUSizedBuffer to the given size and allocated
+ * memory for it.
+ *
+ * @qsb: A QEMUSizedBuffer
+ * @new_size: The new size of the buffer
+ *
+ * Returns an error code in case of memory allocation failure
+ * or the new size of the buffer otherwise. The returned size
+ * may be greater or equal to @new_size.
+ */
+static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size)
+{
+    size_t needed_chunks, i;
+    size_t chunk_size = QSB_CHUNK_SIZE;
+
+    if (qsb->size < new_size) {
+        needed_chunks = DIV_ROUND_UP(new_size - qsb->size,
+                                     chunk_size);
+
+        qsb->iov = g_realloc_n(qsb->iov, qsb->n_iov + needed_chunks,
+                               sizeof(struct iovec));
+        if (qsb->iov == NULL) {
+            return -ENOMEM;
+        }
+
+        for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) {
+            qsb->iov[i].iov_base = g_malloc0(chunk_size);
+            qsb->iov[i].iov_len = chunk_size;
+        }
+
+        qsb->n_iov += needed_chunks;
+        qsb->size += (needed_chunks * chunk_size);
+    }
+
+    return qsb->size;
+}
+
+/**
+ * Write into the QEMUSizedBuffer at a given position and a given
+ * number of bytes. This function will automatically grow the
+ * QEMUSizedBuffer.
+ *
+ * @qsb: A QEMUSizedBuffer
+ * @source: A byte array to copy data from
+ * @pos: The position withing the @qsb to write data to
+ * @size: The number of bytes to copy into the @qsb
+ *
+ * Returns an error code in case of memory allocation failure,
+ * @size otherwise.
+ */
+ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source,
+                     off_t pos, size_t count)
+{
+    ssize_t rc = qsb_grow(qsb, pos + count);
+    size_t to_copy;
+    size_t all_copy = count;
+    const struct iovec *iov;
+    ssize_t index;
+    char *dest;
+    off_t d_off, s_off = 0;
+
+    if (rc < 0) {
+        return rc;
+    }
+
+    if (pos + count > qsb->used) {
+        qsb->used = pos + count;
+    }
+
+    index = qsb_get_iovec(qsb, pos, &d_off);
+    if (index < 0) {
+        return 0;
+    }
+
+    while (all_copy > 0) {
+        iov = &qsb->iov[index];
+
+        dest = iov->iov_base;
+
+        to_copy = iov->iov_len - d_off;
+        if (to_copy > all_copy) {
+            to_copy = all_copy;
+        }
+
+        memcpy(&dest[d_off], &source[s_off], to_copy);
+
+        s_off += to_copy;
+        all_copy -= to_copy;
+
+        d_off = 0;
+        index++;
+    }
+
+    return count;
+}
+
+/**
+ * Create an exact copy of the given QEMUSizedBuffer.
+ *
+ * @qsb : A QEMUSizedBuffer
+ *
+ * Returns a clone of @qsb
+ */
+QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
+{
+    QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb));
+    size_t i;
+    off_t pos = 0;
+
+    for (i = 0; i < qsb->n_iov; i++) {
+        pos += qsb_write_at(out, qsb->iov[i].iov_base,
+                            pos, qsb->iov[i].iov_len);
+    }
+
+    return out;
+}
+
+typedef struct QEMUBuffer {
+    QEMUSizedBuffer *qsb;
+    QEMUFile *file;
+} QEMUBuffer;
+
+static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUBuffer *s = opaque;
+    ssize_t len = qsb_get_length(s->qsb) - pos;
+
+    if (len <= 0) {
+        return 0;
+    }
+
+    if (len > size) {
+        len = size;
+    }
+    return qsb_get_buffer(s->qsb, pos, len, &buf);
+}
+
+static int buf_put_buffer(void *opaque, const uint8_t *buf,
+                          int64_t pos, int size)
+{
+    QEMUBuffer *s = opaque;
+
+    return qsb_write_at(s->qsb, buf, pos, size);
+}
+
+static int buf_close(void *opaque)
+{
+    QEMUBuffer *s = opaque;
+
+    qsb_free(s->qsb);
+
+    g_free(s);
+
+    return 0;
+}
+
+const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f)
+{
+    QEMUBuffer *p;
+
+    qemu_fflush(f);
+
+    p = (QEMUBuffer *)f->opaque;
+
+    return p->qsb;
+}
+
+static const QEMUFileOps buf_read_ops = {
+    .get_buffer = buf_get_buffer,
+    .close =      buf_close
+};
+
+static const QEMUFileOps buf_write_ops = {
+    .put_buffer = buf_put_buffer,
+    .close =      buf_close
+};
+
+QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
+{
+    QEMUBuffer *s;
+
+    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+        fprintf(stderr, "qemu_bufopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUBuffer));
+    if (mode[0] == 'r') {
+        s->qsb = input;
+    }
+
+    if (s->qsb == NULL) {
+        s->qsb = qsb_create(NULL, 0);
+    }
+
+    if (mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, &buf_read_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &buf_write_ops);
+    }
+    return s->file;
+}
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 4/8] QAPI: add type_sized_buffer
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (2 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 3/8] QEMUSizedBuffer Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 5/8] ASN.1 output visitor Stefan Berger
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Add a sized buffer interface to qapi for serializing and
deserializing of u8[], u16[], u32[] and u64[] with proper
handling of endianess.

Cc: Michael Roth <mdroth@linux.vnet.ibm.com>
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 include/qapi/visitor-impl.h | 3 +++
 include/qapi/visitor.h      | 3 +++
 qapi/qapi-visit-core.c      | 8 ++++++++
 3 files changed, 14 insertions(+)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 5159964..be4e5ab 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -34,6 +34,9 @@ struct Visitor
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
                         Error **errp);
+    void (*type_sized_buffer)(Visitor *v, void **obj, const char *name,
+                              size_t elem_count, size_t elem_size,
+                              Error **errp);
 
     /* May be NULL */
     void (*start_optional)(Visitor *v, bool *present, const char *name,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 1fef18c..66ba4bf 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -51,5 +51,8 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+void visit_type_sized_buffer(Visitor *v, void **obj, const char *name,
+                             size_t elem_counter, size_t elem_size,
+                             Error **errp);
 
 #endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 401ee6e..374c0ff 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -313,3 +313,11 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[],
     g_free(enum_str);
     *obj = value;
 }
+
+void visit_type_sized_buffer(Visitor *v, void **obj, const char *name,
+                             size_t elem_count, size_t elem_size, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_sized_buffer(v, obj, name, elem_count, elem_size, errp);
+    }
+}
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 5/8] ASN.1 output visitor
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (3 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 4/8] QAPI: add type_sized_buffer Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 6/8] ASN.1 input visitor Stefan Berger
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Implement an output visitor for ASN.1 BER and CER encoding.

Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 configure                         |   2 +-
 include/qapi/ber-output-visitor.h |  32 ++
 include/qapi/ber.h                | 113 ++++++
 include/qemu-common.h             |   2 +
 qapi/Makefile.objs                |   1 +
 qapi/ber-common.c                 |  86 +++++
 qapi/ber-common.h                 |  29 ++
 qapi/ber-output-visitor.c         | 705 ++++++++++++++++++++++++++++++++++++++
 util/qemu-file.c                  |  58 ++++
 9 files changed, 1027 insertions(+), 1 deletion(-)
 create mode 100644 include/qapi/ber-output-visitor.h
 create mode 100644 include/qapi/ber.h
 create mode 100644 qapi/ber-common.c
 create mode 100644 qapi/ber-common.h
 create mode 100644 qapi/ber-output-visitor.c

diff --git a/configure b/configure
index 46a7594..5e1d69f 100755
--- a/configure
+++ b/configure
@@ -2844,7 +2844,7 @@ fi
 # Do we need libm
 cat > $TMPC << EOF
 #include <math.h>
-int main(void) { return isnan(sin(0.0)); }
+int main(void) { return isnan(nan("NAN")); }
 EOF
 if compile_prog "" "" ; then
   :
diff --git a/include/qapi/ber-output-visitor.h b/include/qapi/ber-output-visitor.h
new file mode 100644
index 0000000..d93490b
--- /dev/null
+++ b/include/qapi/ber-output-visitor.h
@@ -0,0 +1,32 @@
+/*
+ * BER Output Visitor header
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.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 BER_OUTPUT_VISITOR_H
+#define BER_OUTPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+#include "qapi/ber.h"
+
+typedef struct BEROutputVisitor BEROutputVisitor;
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *,
+                                         BERLengthEncoding le);
+void ber_output_visitor_cleanup(BEROutputVisitor *v);
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v);
+
+BERLengthEncoding ber_output_set_length_encoding(BEROutputVisitor *v,
+                                                 BERLengthEncoding le);
+
+#endif
diff --git a/include/qapi/ber.h b/include/qapi/ber.h
new file mode 100644
index 0000000..4c835c1
--- /dev/null
+++ b/include/qapi/ber.h
@@ -0,0 +1,113 @@
+/*
+ * ASN.1 Basic Encoding Rules Common functions
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *  Michael Tsirkin   <mst@redhat.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 QAPI_BER_H
+#define QAPI_BER_H
+
+/*
+ * This is a subset of BER for QEMU use.
+ * QEMU will use the DER encoding always with one extension from
+ * CER: SET and SEQUENCE types can have indefinite-length encoding
+ * if the encoding is not all immediately available.
+ *
+ * We assume that SET encodings can be available or not available,
+ * and that SEQUENCE encodings are available unless a SEQUENCE includes
+ * a non-available SET.
+ *
+ * The last is an extension to allow an arbitrarily large SET
+ * to be produced online without knowing the length in advance.
+ *
+ * All types used shall be universal, with explicit tagging, to simplify
+ * use by external tools.
+ */
+
+
+#define BER_TYPE_CLASS_SHIFT  6
+#define BER_TYPE_PC_SHIFT     5
+
+typedef enum ber_type_class {
+    BER_TYPE_CLASS_UNIVERSAL = 0x0 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_APPLICATION = 0x1 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_PRIVATE = 0x3 << BER_TYPE_CLASS_SHIFT,
+    BER_TYPE_CLASS_MASK = 0x3 << BER_TYPE_CLASS_SHIFT /* Mask to get class */
+} BERTypeClass;
+
+/* P/C bit */
+typedef enum ber_type_p_c {
+    BER_TYPE_PRIMITIVE = 0x0 << BER_TYPE_PC_SHIFT,
+    BER_TYPE_CONSTRUCTED = 0x1 << BER_TYPE_PC_SHIFT,
+    BER_TYPE_P_C_MASK = 0x1 << BER_TYPE_PC_SHIFT /* Mask to get P/C bit */
+} BERTypePC;
+
+typedef enum ber_type_tag {
+    BER_TYPE_EOC              /*  P        0       0*/,
+    BER_TYPE_BOOLEAN          /*  P        1       1*/,
+    BER_TYPE_INTEGER          /*  P        2       2*/,
+    BER_TYPE_BIT_STRING       /*  P/C      3       3*/,
+    BER_TYPE_OCTET_STRING     /*  P/C      4       4*/,
+    BER_TYPE_NULL             /*  P        5       5*/,
+    BER_TYPE_OBJECT_ID        /*  P        6       6*/,
+    BER_TYPE_OBJECT_DESC      /*  P        7       7*/,
+    BER_TYPE_EXTERNAL         /*  C        8       8*/,
+    BER_TYPE_REAL             /*  P        9       9*/,
+    BER_TYPE_ENUMERATED       /*  P        10      A*/,
+    BER_TYPE_EMBEDDED         /*  C        11      B*/,
+    BER_TYPE_UTF8_STRING      /*  P/C      12      C*/,
+    BER_TYPE_RELATIVE_OID     /*  P        13      D*/,
+    BER_TYPE_UNUSED_0xE       /*                    */,
+    BER_TYPE_UNUSED_0xF       /*                    */,
+    BER_TYPE_SEQUENCE         /*  C        16      10*/,
+    BER_TYPE_SET              /*  C        17      11*/,
+    BER_TYPE_NUMERIC_STRING   /*  P/C      18      12*/,
+    BER_TYPE_PRINTABLE_STRING /*  P/C      19      13*/,
+    BER_TYPE_T61STRING        /*  P/C      20      14*/,
+    BER_TYPE_VIDEOTEX_STRING  /*  P/C      21      15*/,
+    BER_TYPE_IA5_STRING       /*  P/C      22      16*/,
+    BER_TYPE_UTCTIME          /*  P/C      23      17*/,
+    BER_TYPE_GENERALIZED_TIME /*  P/C      24      18*/,
+    BER_TYPE_GRAPHIC_STRING   /*  P/C      25      19*/,
+    BER_TYPE_VISIBLE_STRING   /*  P/C      26      1A*/,
+    BER_TYPE_GENERAL_STRING   /*  P/C      27      1B*/,
+    BER_TYPE_UNIVERSAL_STRING /*  P/C      28      1C*/,
+    BER_TYPE_CHARACTER_STRING /*  P/C      29      1D*/,
+    BER_TYPE_BMP_STRING       /*  P/C      30      1E*/,
+    BER_TYPE_LONG_FORM        /*  -        31      1F*/,
+    BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */,
+    BER_TYPE_CUSTOM_LIST = 0x20,
+    BER_TYPE_CUSTOM_OPTIONAL = 0x21,
+} BERTypeTag;
+
+typedef enum ber_length {
+    /* Special length values */
+    BER_LENGTH_INDEFINITE = 0x1 << 7,
+    BER_LENGTH_RESERVED = 0xFF,
+    /* Anything else is either short or long */
+    BER_LENGTH_SHORT = 0x0 << 7,
+    BER_LENGTH_LONG = 0x1 << 7,
+    BER_LENGTH_SHORT_LONG_MASK = 0x1 << 7,
+    BER_LENGTH_MASK = 0x7F,
+} BERLength;
+
+typedef enum ber_length_encoding {
+    BER_LENGTH_ENCODING_DEFINITE = 0, /* used by DER */
+    BER_LENGTH_ENCODING_INDEFINITE = 1, /* used by CER */
+} BERLengthEncoding;
+
+const char *ber_type_to_str(uint8_t ber_type);
+const char *ber_type_pc_to_str(enum ber_type_class ber_type_flags);
+const char *ber_type_class_to_str(enum ber_type_class ber_type_flags);
+
+#endif /* QAPI_BER_H */
+
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 7794fa7..31ce759 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -459,5 +459,7 @@ ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count,
                        uint8_t **buf);
 ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
                      off_t pos, size_t count);
+ssize_t qsb_qfile_write(const QEMUSizedBuffer *qsb, QEMUFile *file,
+                        off_t start, size_t count);
 
 #endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 1f9c973..519e3ee 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
 
 util-obj-y += opts-visitor.o
+util-obj-y += ber-common.o ber-output-visitor.o
diff --git a/qapi/ber-common.c b/qapi/ber-common.c
new file mode 100644
index 0000000..1053c41
--- /dev/null
+++ b/qapi/ber-common.c
@@ -0,0 +1,86 @@
+/*
+ * ASN.1 Basic Encoding Rules Common functions
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *  Michael Tsirkin   <mst@redhat.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 <stdint.h>
+
+#include "qapi/ber.h"
+
+static const char *ber_type_names[] = {
+    "BER_TYPE_EOC",
+    "BER_TYPE_BOOLEAN",
+    "BER_TYPE_INTEGER",
+    "BER_TYPE_BIT_STRING",
+    "BER_TYPE_OCTET_STRING",
+    "BER_TYPE_NULL",
+    "BER_TYPE_OBJECT_ID",
+    "BER_TYPE_OBJECT_DESC",
+    "BER_TYPE_EXTERNAL",
+    "BER_TYPE_REAL",
+    "BER_TYPE_ENUMERATED",
+    "BER_TYPE_EMBEDDED",
+    "BER_TYPE_UTF8_STRING",
+    "BER_TYPE_RELATIVE_OID",
+    "BER_TYPE_UNUSED_0xE",
+    "BER_TYPE_UNUSED_0xF",
+    "BER_TYPE_SEQUENCE",
+    "BER_TYPE_SET",
+    "BER_TYPE_NUMERIC_STRING",
+    "BER_TYPE_PRINTABLE_STRING",
+    "BER_TYPE_T61STRING",
+    "BER_TYPE_VIDEOTEX_STRING",
+    "BER_TYPE_IA5_STRING",
+    "BER_TYPE_UTCTIME",
+    "BER_TYPE_GENERALIZED_TIME",
+    "BER_TYPE_GRAPHIC_STRING",
+    "BER_TYPE_VISIBLE_STRING",
+    "BER_TYPE_GENERAL_STRING",
+    "BER_TYPE_UNIVERSAL_STRING",
+    "BER_TYPE_CHARACTER_STRING"
+    "BER_TYPE_BMP_STRING",
+    "BER_TYPE_LONG_FORM",
+};
+
+const char *ber_type_to_str(uint8_t ber_type)
+{
+    return ber_type_names[ber_type & BER_TYPE_TAG_MASK];
+}
+
+static const char *ber_pc_names[] = {
+    "BER_PRIMITIVE",
+    "BER_CONSTRUCTED"
+};
+
+const char *ber_type_pc_to_str(enum ber_type_class ber_type_flags)
+{
+    int idx = (ber_type_flags & BER_TYPE_P_C_MASK) >>
+               BER_TYPE_PC_SHIFT;
+
+    return ber_pc_names[idx];
+}
+
+static const char *ber_class_names[] = {
+    "BER_CLASS_UNIVERSAL",
+    "BER_CLASS_APPLICATION",
+    "BER_CLASS_CONTEXT",
+    "BER_CLASS_PRIVATE"
+};
+
+const char *ber_type_class_to_str(enum ber_type_class ber_type_flags)
+{
+    int idx = (ber_type_flags & BER_TYPE_CLASS_MASK) >>
+               BER_TYPE_CLASS_SHIFT;
+
+    return ber_class_names[idx];
+}
diff --git a/qapi/ber-common.h b/qapi/ber-common.h
new file mode 100644
index 0000000..c9fd2dd
--- /dev/null
+++ b/qapi/ber-common.h
@@ -0,0 +1,29 @@
+/*
+ * BER Visitor -- common code
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ *  Stefan Berger     <stefanb@us.ibm.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 __QAPI_BER_COMMON_H__
+#define __QAPI_BER_COMMON_H__
+
+#include <stdint.h>
+
+#include "qemu/compiler.h"
+
+struct ieee754_buffer {
+    uint8_t type;
+    uint8_t length;
+    uint8_t first;
+    uint16_t exponent;
+    uint32_t mant_hi;
+    uint32_t mant_lo;
+} QEMU_PACKED;
+
+#endif /* __QAPI_BER_COMMON_H__ */
diff --git a/qapi/ber-output-visitor.c b/qapi/ber-output-visitor.c
new file mode 100644
index 0000000..db00ebb
--- /dev/null
+++ b/qapi/ber-output-visitor.c
@@ -0,0 +1,705 @@
+/*
+ * BER Output Visitor
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *  Michael Tsirkin   <mst@redhat.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 <math.h>
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/ber-common.h"
+#include "qapi/ber-output-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "include/qapi/qmp/qerror.h"
+#include "hw/hw.h"
+#include "qapi/ber.h"
+#include "qapi/visitor-impl.h"
+
+
+#define CER_FRAGMENT_CHUNK_SIZE  1000
+
+/*#define DEBUG_BER */
+
+#ifdef DEBUG_BER
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+
+typedef struct QStackEntry {
+    QEMUFile *qfile;
+    bool is_list_head;
+    BERLengthEncoding length_encoding;
+    QTAILQ_ENTRY(QStackEntry) node;
+} QStackEntry;
+
+typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
+
+struct BEROutputVisitor {
+    Visitor visitor;
+    QStack stack;
+    QEMUFile *qfile;
+
+    BERLengthEncoding length_encoding;
+};
+
+static void ber_output_type_bool(Visitor *v, bool *obj, const char *name,
+                                 Error **errp);
+
+
+static BEROutputVisitor *to_aov(Visitor *v)
+{
+    return container_of(v, BEROutputVisitor, visitor);
+}
+
+static void ber_output_push(BEROutputVisitor *qov, QEMUFile *qfile,
+                            Error **errp)
+{
+    QStackEntry *e = g_malloc0(sizeof(*e));
+
+    e->qfile = qfile;
+    e->is_list_head = true;
+    /* remember length encoding used at this point */
+    e->length_encoding = qov->length_encoding;
+    QTAILQ_INSERT_HEAD(&qov->stack, e, node);
+}
+
+static QEMUFile *ber_output_pop(BEROutputVisitor *qov)
+{
+    QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+    QEMUFile *qfile;
+
+    QTAILQ_REMOVE(&qov->stack, e, node);
+    qfile = e->qfile;
+    /* switch back to length encoding used at this point */
+    qov->length_encoding = e->length_encoding;
+    g_free(e);
+
+    return qfile;
+}
+
+static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen,
+                                    enum ber_type_tag ber_type,
+                                    uint8_t ber_type_flags,
+                                    Error **errp)
+{
+    unsigned int idx = 0;
+
+    if (buflen < 1) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return 0;
+    }
+
+    if (ber_type > BER_TYPE_LONG_FORM) {
+        int byte = sizeof(uint32_t);
+        uint32_t mask = 0x7f << (7 * byte);
+        bool do_write = false;
+
+        buffer[0] = ber_type_flags | BER_TYPE_LONG_FORM;
+
+        while (byte >= 0) {
+            if (!do_write) {
+                if ((mask & ber_type)) {
+                    do_write = true;
+                    if (1 + byte + 1 > buflen) {
+                        error_set(errp, QERR_BUFFER_OVERRUN);
+                        return 0;
+                    }
+                }
+            }
+            if (do_write) {
+                buffer[1 + idx] = (ber_type >> (7 * byte)) & 0x7f;
+                if (byte > 0) {
+                    buffer[1 + idx] |= 0x80;
+                }
+                idx++;
+            }
+            byte--;
+            mask = 0x7f << (7 * byte);
+        }
+    } else {
+        buffer[0] = ber_type | ber_type_flags;
+    }
+    return 1 + idx;
+}
+
+static unsigned int ber_encode_len(uint8_t *buffer, uint32_t buflen,
+                                   uint64_t len, Error **errp)
+{
+    uint64_t mask = 0xFF00000000000000ULL;
+    int shift =  64 - 8;
+    int c = 0;
+
+    if (len <= 0x7f && buflen >= 1) {
+        buffer[0] = len;
+        return 1;
+    }
+
+    while (mask && (mask & len) == 0) {
+        mask >>= 8;
+        shift -= 8;
+    }
+
+    while (shift >= 0) {
+        if (1 + c + 1 > buflen) {
+            error_set(errp, QERR_BUFFER_OVERRUN);
+            return 0;
+        }
+        buffer[1+c] = len >> shift;
+        c++;
+        shift -= 8;
+    }
+
+    buffer[0] = BER_LENGTH_LONG | c;
+
+    return 1 + c;
+}
+
+static void ber_output_start_constructed(Visitor *v, uint32_t ber_type,
+                                         Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    uint8_t buf[20];
+    unsigned int tag_bytes_written;
+
+    switch (aov->length_encoding) {
+    case BER_LENGTH_ENCODING_DEFINITE:
+        ber_output_push(aov, aov->qfile, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        aov->qfile = qemu_bufopen("w", NULL);
+        break;
+    case BER_LENGTH_ENCODING_INDEFINITE:
+        ber_output_push(aov, aov->qfile, errp); /* needed for list support */
+        if (error_is_set(errp)) {
+            return;
+        }
+        tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+                                            ber_type, BER_TYPE_CONSTRUCTED,
+                                            errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        buf[tag_bytes_written] = BER_LENGTH_INDEFINITE;
+        if (qemu_write_bytes(aov->qfile, buf, 1 + tag_bytes_written) !=
+            1 + tag_bytes_written) {
+            error_setg(errp, "QEMUFile error: Error while writing "
+                       "constructed type");
+            return;
+        }
+    }
+}
+
+static void ber_output_constructed_ber_close(BEROutputVisitor *aov,
+                                             QEMUFile *qfile,
+                                             uint32_t ber_type,
+                                             Error **errp)
+{
+    uint8_t buf[20];
+    const QEMUSizedBuffer *qsb;
+    uint64_t len;
+    unsigned int num_bytes, tag_bytes_written;
+
+    tag_bytes_written = ber_encode_type(buf, sizeof(buf),
+                                        ber_type, BER_TYPE_CONSTRUCTED,
+                                        errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    qsb = qemu_buf_get(aov->qfile);
+    len = qsb_get_length(qsb);
+    DPRINTF("%s:constructed type (0x%02x, %p) has length %ld bytes\n",
+            __func__, ber_type, aov->qfile, len);
+
+    num_bytes = ber_encode_len(&buf[tag_bytes_written],
+                               sizeof(buf) - tag_bytes_written,
+                               len, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (qemu_write_bytes(qfile, buf, tag_bytes_written + num_bytes) !=
+            tag_bytes_written + num_bytes ||
+        qsb_qfile_write(qsb, qfile, 0, qsb_get_length(qsb)) !=
+            qsb_get_length(qsb)) {
+        error_setg(errp, "QEMUFile error: Error while writing buffer");
+        return;
+    }
+
+    qemu_fclose(aov->qfile);
+    aov->qfile = qfile;
+    qemu_fflush(qfile);
+}
+
+static void ber_output_end_constructed(Visitor *v, uint32_t ber_type,
+                                       Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    uint8_t buf[20];
+    QEMUFile *qfile = ber_output_pop(aov);
+
+    DPRINTF("%s: end set/struct:\n", __func__);
+
+    switch (aov->length_encoding) {
+    case BER_LENGTH_ENCODING_DEFINITE:
+        ber_output_constructed_ber_close(aov, qfile, ber_type, errp);
+        break;
+
+    case BER_LENGTH_ENCODING_INDEFINITE:
+        buf[0] = BER_TYPE_EOC;
+        buf[1] = 0;
+        if (qemu_write_bytes(aov->qfile, buf, 2) != 2) {
+            error_setg(errp, "QEMUFile error: Error while writing buffer "
+                       "with BER_TYPE_EOC");
+            return;
+        }
+        break;
+    }
+}
+
+static void ber_output_start_struct(Visitor *v, void **obj, const char *kind,
+                                    const char *name, size_t unused,
+                                    Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_end_struct(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_SEQUENCE, errp);
+}
+
+static void ber_output_start_optional(Visitor *v, bool *present,
+                                      const char *name, Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_CUSTOM_OPTIONAL, errp);
+
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    ber_output_type_bool(v, present, name, errp);
+}
+
+static void ber_output_end_optional(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_CUSTOM_OPTIONAL, errp);
+}
+
+static void ber_output_start_list(Visitor *v, const char *name,
+                                  Error **errp)
+{
+    ber_output_start_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static GenericList *ber_output_next_list(Visitor *v, GenericList **listp,
+                                         Error **errp)
+{
+    GenericList *list = *listp;
+    BEROutputVisitor *bov = to_aov(v);
+    QStackEntry *e = QTAILQ_FIRST(&bov->stack);
+
+    assert(e);
+    if (e->is_list_head) {
+        e->is_list_head = false;
+        return list;
+    }
+
+    return list ? list->next : NULL;
+}
+
+static void ber_output_end_list(Visitor *v, Error **errp)
+{
+    ber_output_end_constructed(v, BER_TYPE_CUSTOM_LIST, errp);
+}
+
+static void ber_output_fragment(Visitor *v, uint32_t ber_type,
+                                uint8_t *buffer, size_t elem_count,
+                                size_t elem_size, Error **errp)
+{
+    uint32_t offset = 0;
+    bool fragmented = false;
+    uint32_t chunk;
+    unsigned int num_bytes, type_bytes;
+    uint8_t buf[20];
+    uint32_t chunk_size;
+    BEROutputVisitor *aov = to_aov(v);
+    size_t buflen = elem_count * elem_size;
+    size_t n_elms;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    size_t i;
+    uint16_t *d16, *s16;
+    uint32_t *d32, *s32;
+    uint64_t *d64, *s64;
+#endif
+    uint8_t *conv_buffer = NULL;
+    bool is_bigendian;
+#if __BYTE_ORDER == __BIG_ENDIAN
+    is_bigendian = true;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+    is_bigendian = false;
+#else
+# error Unsupported endianess
+#endif
+
+    switch (aov->length_encoding) {
+    case BER_LENGTH_ENCODING_INDEFINITE:
+        /* X.690 9.2 */
+        fragmented = (buflen > CER_FRAGMENT_CHUNK_SIZE);
+        chunk_size = 1000;
+        break;
+    case BER_LENGTH_ENCODING_DEFINITE:
+        chunk_size = 0xffffffff;
+        break;
+    }
+
+    if (fragmented) {
+        ber_output_start_constructed(&aov->visitor, ber_type, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+    }
+
+    if (elem_size != sizeof(uint8_t) && !is_bigendian) {
+        /* intermediate buffer for endianess conversion */
+        conv_buffer = g_malloc(chunk_size);
+    }
+
+    do {
+        chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset;
+
+        /* calc how many >=2 byte-elements cleanly fit into the chunk */
+        n_elms = chunk / elem_size;
+        chunk = n_elms * elem_size;
+
+        type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0,
+                                     errp);
+        if (error_is_set(errp)) {
+            goto error;
+        }
+        num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes,
+                                   chunk, errp);
+        if (error_is_set(errp)) {
+            goto error;
+        }
+
+
+        switch (elem_size) {
+        case sizeof(uint8_t):
+#if __BYTE_ORDER == __BIG_ENDIAN
+        case sizeof(uint16_t):
+        case sizeof(uint32_t):
+        case sizeof(uint64_t):
+#endif
+            /* simple write, no endianess conversion */
+            conv_buffer = &buffer[offset];
+            break;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        case sizeof(uint16_t):
+            d16 = (uint16_t *)conv_buffer;
+            s16 = (uint16_t *)&buffer[offset];
+            for (i = 0; i < n_elms; i++) {
+                d16[i] = cpu_to_be16(s16[i]);
+            }
+            break;
+        case sizeof(uint32_t):
+            d32 = (uint32_t *)conv_buffer;
+            s32 = (uint32_t *)&buffer[offset];
+            for (i = 0; i < n_elms; i++) {
+                d32[i] = cpu_to_be32(s32[i]);
+            }
+            break;
+        case sizeof(uint64_t):
+            d64 = (uint64_t *)conv_buffer;
+            s64 = (uint64_t *)&buffer[offset];
+            for (i = 0; i < n_elms; i++) {
+                d64[i] = cpu_to_be64(s64[i]);
+            }
+            break;
+#endif
+        default:
+            error_setg(errp, "Illegal element size %" PRIu64, elem_size);
+            goto error;
+        }
+
+        if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) !=
+            type_bytes + num_bytes ||
+            qemu_write_bytes(aov->qfile, conv_buffer, chunk) != chunk) {
+            error_setg(errp, "QEMUFile error: Error while writing buffer");
+            goto error;
+        }
+
+        offset += chunk;
+    } while (offset < buflen);
+
+    if (fragmented) {
+        ber_output_end_constructed(&aov->visitor, ber_type, errp);
+    }
+
+error:
+    if (elem_size != sizeof(uint8_t) && !is_bigendian) {
+        g_free(conv_buffer);
+    }
+}
+
+static void ber_output_int(Visitor *v, int64_t val, uint8_t maxnumbytes,
+                           Error **errp)
+{
+    uint8_t buf[20];
+    int shift =  (maxnumbytes - 1) * 8;
+    uint64_t mask = 0xFF80ULL << (shift - 8);
+    bool exp_zeros;
+    int c = 0;
+    BEROutputVisitor *aov = to_aov(v);
+
+    DPRINTF("%s: Writing int 0x%lx (len=%d)\n",
+            __func__, val, maxnumbytes);
+
+    /*
+     * We encode ints with fixed-witdh so that they will use
+     * the same number of bytes indepent of their value.
+     * The 'universal' encoding would not encode a 32bit '0'
+     * with 4 bytes, so this is an application-specific encoding.
+     */
+    buf[0] = BER_TYPE_CLASS_APPLICATION | BER_TYPE_PRIMITIVE |
+             BER_TYPE_INTEGER;
+
+    if (maxnumbytes > 1) {
+        exp_zeros = ((mask & val) == 0) ? true : false;
+        while (mask != 0xFF) {
+            if (exp_zeros) {
+                if ((mask & val) != 0) {
+                    break;
+                }
+            } else {
+                if ((mask & val) != mask) {
+                    break;
+                }
+            }
+            shift -= 8;
+            mask >>= 8;
+        }
+    }
+
+    while (shift >= 0) {
+        buf[2+c] = val >> shift;
+        c++;
+        shift -= 8;
+    }
+    buf[1] = c;
+
+    if (qemu_write_bytes(aov->qfile, buf, 1 + 1 + c) != 1 + 1 + c) {
+        error_setg(errp, "QEMUFile error: Error while writing integer");
+        return;
+    }
+}
+static void ber_output_type_int(Visitor *v, int64_t *obj, const char *name,
+                                Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint8(Visitor *v, uint8_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint16(Visitor *v, uint16_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint32(Visitor *v, uint32_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_uint64(Visitor *v, uint64_t *obj,
+                                     const char *name, Error **errp)
+{
+    ber_output_int(v, *obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int8(Visitor *v, int8_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int16(Visitor *v, int16_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int32(Visitor *v, int32_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_int64(Visitor *v, int64_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp);
+}
+
+static void ber_output_type_bool(Visitor *v, bool *obj, const char *name,
+                                 Error **errp)
+{
+    bool b;
+
+    b = *obj;
+
+    ber_output_fragment(v, BER_TYPE_BOOLEAN, (uint8_t *)&b, sizeof(b),
+                        sizeof(b), errp);
+}
+
+static void ber_output_type_str(Visitor *v, char **obj, const char *name,
+                                Error **errp)
+{
+    DPRINTF("%s: Writing string %s, len = 0x%02x\n",
+            __func__, *obj, (int)strlen(*obj));
+    ber_output_fragment(v, BER_TYPE_IA5_STRING,
+                        (uint8_t *)*obj,
+                        *obj == NULL ? 0 : strlen(*obj), 1, errp);
+}
+
+static void ber_output_sized_buffer(Visitor *v, void **obj,
+                                    const char *name, size_t elem_count,
+                                    size_t elem_size, Error **errp)
+{
+    ber_output_fragment(v, BER_TYPE_OCTET_STRING,
+                        *obj, elem_count, elem_size, errp);
+}
+
+static void ber_output_type_number(Visitor *v, double *obj, const char *name,
+                                   Error **errp)
+{
+    BEROutputVisitor *aov = to_aov(v);
+    GDoubleIEEE754 num;
+    uint8_t first;
+    struct ieee754_buffer number;
+
+    /* encode it as fixed-width double */
+    number.type = BER_TYPE_CLASS_APPLICATION | BER_TYPE_PRIMITIVE |
+                  BER_TYPE_REAL;
+    number.length = sizeof(number) - offsetof(struct ieee754_buffer, first);
+
+    num.v_double = *obj;
+
+    if (isnan(*obj)) {
+        /* special encoding supported here; not found in spec. */
+        first = 0x42;
+    } else if (isinf(*obj)) {
+        /* spec. 8.5.8 */
+        if (num.mpn.sign) {
+            first = 0x41; /* -oo */
+        } else {
+            first = 0x40; /* +oo */
+        }
+    } else {
+        first = 0x80;
+        if (num.mpn.sign) {
+            first |= 0x40;
+        }
+        /* Base 2; 0 for scaling factor; 2nd and 3rd octet encode exp. */
+        first |= 0x1;
+    }
+
+    number.first = first;
+    number.exponent = cpu_to_be16(num.mpn.biased_exponent);
+    number.mant_hi = cpu_to_be32(num.mpn.mantissa_high);
+    number.mant_lo = cpu_to_be32(num.mpn.mantissa_low);
+
+    if (qemu_write_bytes(aov->qfile, (uint8_t *)&number, sizeof(number)) !=
+        sizeof(number)) {
+        error_setg(errp, "QEMUFile error: Error while writing double.");
+    }
+}
+
+void ber_output_visitor_cleanup(BEROutputVisitor *v)
+{
+    QStackEntry *e, *tmp;
+
+    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
+        QTAILQ_REMOVE(&v->stack, e, node);
+        if (e->qfile) {
+            qemu_fclose(e->qfile);
+        }
+        g_free(e);
+    }
+
+    g_free(v);
+}
+
+
+Visitor *ber_output_get_visitor(BEROutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+BERLengthEncoding ber_output_set_length_encoding(BEROutputVisitor *v,
+                                                 BERLengthEncoding le)
+{
+    BERLengthEncoding old = v->length_encoding;
+
+    v->length_encoding = le;
+
+    return old;
+}
+
+BEROutputVisitor *ber_output_visitor_new(QEMUFile *qfile,
+                                         BERLengthEncoding le)
+{
+    BEROutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.start_struct = ber_output_start_struct;
+    v->visitor.end_struct = ber_output_end_struct;
+    v->visitor.start_list = ber_output_start_list;
+    v->visitor.next_list = ber_output_next_list;
+    v->visitor.end_list = ber_output_end_list;
+    v->visitor.start_optional = ber_output_start_optional;
+    v->visitor.end_optional = ber_output_end_optional;
+    v->visitor.type_int = ber_output_type_int;
+    v->visitor.type_uint8 = ber_output_type_uint8;
+    v->visitor.type_uint16 = ber_output_type_uint16;
+    v->visitor.type_uint32 = ber_output_type_uint32;
+    v->visitor.type_uint64 = ber_output_type_uint64;
+    v->visitor.type_int8 = ber_output_type_int8;
+    v->visitor.type_int16 = ber_output_type_int16;
+    v->visitor.type_int32 = ber_output_type_int32;
+    v->visitor.type_int64 = ber_output_type_int64;
+    v->visitor.type_bool = ber_output_type_bool;
+    v->visitor.type_str = ber_output_type_str;
+    v->visitor.type_sized_buffer = ber_output_sized_buffer;
+    v->visitor.type_number = ber_output_type_number;
+
+    QTAILQ_INIT(&v->stack);
+    v->qfile = qfile;
+    v->length_encoding = le;
+
+    return v;
+}
diff --git a/util/qemu-file.c b/util/qemu-file.c
index 89b0614..0ba5ae9 100644
--- a/util/qemu-file.c
+++ b/util/qemu-file.c
@@ -1069,6 +1069,64 @@ QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
     return out;
 }
 
+/**
+ * Write the contents of a QEMUSizedBuffer into a QEMUFile.
+ *
+ * @qsb: A QEMUSizedBuffer
+ * @qfile: QEMUFile to write into
+ * @start: start offset of the data in the @qsb
+ * @count: number of bytes to write into @qfile
+ *
+ * Returns the actual number of bytes that were written,
+ * -EIO in case of an error.
+ */
+ssize_t qsb_qfile_write(const QEMUSizedBuffer *qsb, QEMUFile *qfile,
+                       off_t start, size_t count)
+{
+    size_t all_copy, to_copy;
+    off_t s_off;
+    const struct iovec *iov;
+    ssize_t index;
+    uint8_t *s;
+
+    if (start > qsb->used) {
+        return 0;
+    }
+
+    if (start + count > qsb->used) {
+        count = qsb->used - start;
+    }
+
+    all_copy = count;
+
+    index = qsb_get_iovec(qsb, start, &s_off);
+    if (index < 0) {
+        return 0;
+    }
+
+    while (all_copy > 0) {
+        iov = &qsb->iov[index];
+
+        s = iov->iov_base;
+
+        to_copy = iov->iov_len - s_off;
+        if (to_copy > all_copy) {
+            to_copy = all_copy;
+        }
+
+        if (qemu_write_bytes(qfile, &s[s_off], to_copy) != to_copy) {
+            return -EIO;
+        }
+
+        all_copy -= to_copy;
+
+        s_off = 0;
+        index++;
+    }
+
+    return count;
+}
+
 typedef struct QEMUBuffer {
     QEMUSizedBuffer *qsb;
     QEMUFile *file;
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 6/8] ASN.1 input visitor
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (4 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 5/8] ASN.1 output visitor Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 7/8] Extend test-visitor-serialization with ASN.1 visitor(s) Stefan Berger
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Implement an input visitor for ASN.1 BER and CER encoding.

Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 include/qapi/ber-input-visitor.h |   30 +
 qapi/Makefile.objs               |    2 +-
 qapi/ber-input-visitor.c         | 1153 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1184 insertions(+), 1 deletion(-)
 create mode 100644 include/qapi/ber-input-visitor.h
 create mode 100644 qapi/ber-input-visitor.c

diff --git a/include/qapi/ber-input-visitor.h b/include/qapi/ber-input-visitor.h
new file mode 100644
index 0000000..67eb1c0
--- /dev/null
+++ b/include/qapi/ber-input-visitor.h
@@ -0,0 +1,30 @@
+/*
+ * BER Input Visitor header
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.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 BER_INPUT_VISITOR_H
+#define BER_INPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct BERInputVisitor BERInputVisitor;
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *,
+                                       uint64_t max_allowd_buffer_size);
+void ber_input_visitor_cleanup(BERInputVisitor *v);
+uint64_t ber_input_get_parser_position(BERInputVisitor *v);
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v);
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 519e3ee..f7f080a 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -3,4 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
 
 util-obj-y += opts-visitor.o
-util-obj-y += ber-common.o ber-output-visitor.o
+util-obj-y += ber-common.o ber-output-visitor.o ber-input-visitor.o
diff --git a/qapi/ber-input-visitor.c b/qapi/ber-input-visitor.c
new file mode 100644
index 0000000..86dac71
--- /dev/null
+++ b/qapi/ber-input-visitor.c
@@ -0,0 +1,1153 @@
+/*
+ * BER Input Visitor
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Stefan Berger     <stefanb@us.ibm.com>
+ *  Michael Tsirkin   <mst@redhat.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 <math.h>
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/ber-common.h"
+#include "qapi/ber-input-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "qapi/ber.h"
+#include "include/qapi/qmp/qerror.h"
+#include "migration/qemu-file.h"
+#include "qapi/visitor-impl.h"
+
+#define AIV_STACK_SIZE 1024
+
+/* whether to allow the parsing of primitives that are fragmented */
+#define BER_ALLOW_FRAGMENTED_PRIMITIVES
+
+/* #define DEBUG_BER */
+
+#ifdef DEBUG_BER
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef struct StackEntry {
+    uint64_t cur_pos;
+    uint64_t counter;
+} StackEntry;
+
+struct BERInputVisitor {
+    Visitor visitor;
+    QEMUFile *qfile;
+    uint64_t cur_pos;
+    StackEntry stack[AIV_STACK_SIZE];
+    int nb_stack;
+    uint64_t max_allowed_buffer_size;
+    uint64_t largest_needed_buffer;
+};
+
+static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
+                                Error **errp);
+
+
+static BERInputVisitor *to_biv(Visitor *v)
+{
+    return container_of(v, BERInputVisitor, visitor);
+}
+
+static void ber_input_push(BERInputVisitor *aiv,
+                           uint64_t cur_pos, Error **errp)
+{
+    aiv->stack[aiv->nb_stack].cur_pos = cur_pos;
+    aiv->stack[aiv->nb_stack].counter = 0;
+    aiv->nb_stack++;
+
+    if (aiv->nb_stack >= AIV_STACK_SIZE) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+    }
+}
+
+static uint64_t ber_input_pop(BERInputVisitor *aiv, Error **errp)
+{
+    aiv->nb_stack--;
+
+    if (aiv->nb_stack < 0) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return 0;
+    }
+
+    return aiv->stack[aiv->nb_stack].cur_pos;
+}
+
+/*
+ * Read a type tag from the stream. Up-to 32 bit type tags are supported
+ * for reading and otherwise an error is returned. Anything larger than that
+ * would not be reasonable and could only be abused.
+ */
+static uint32_t ber_read_type(BERInputVisitor *aiv, uint8_t *ber_type_flags,
+                              Error **errp)
+{
+    uint32_t type;
+    uint8_t byte;
+    uint8_t ctr = 0;
+    char buf[128];
+
+    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+        error_setg(errp, "QEMUFile has an error, error was '%s'",
+                  "Error while reading type");
+        return 0;
+    }
+    aiv->cur_pos++;
+    type = byte;
+
+    *ber_type_flags = type & (BER_TYPE_P_C_MASK | BER_TYPE_CLASS_MASK);
+
+    if ((type & BER_TYPE_TAG_MASK) == BER_TYPE_LONG_FORM) {
+        type = 0;
+        while (true) {
+            type <<= 7;
+            if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+                error_setg(errp, "QEMUFile has an error, error was '%s'",
+                          "Error while reading long type");
+                return 0;
+            }
+            aiv->cur_pos++;
+
+            type |= (byte & 0x7f);
+            if ((byte & 0x80) == 0) {
+                break;
+            }
+            ctr += 7; /* read 7 bits */
+            if (ctr >= (sizeof(type) * 8)) {
+                /* only support 32 bit length identifiers */
+                snprintf(buf, sizeof(buf),
+                         "type tag is larger than 32 bit (offset %" PRIu64
+                         ")", aiv->cur_pos);
+                error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+                return 0;
+            }
+        }
+    } else {
+        type &= BER_TYPE_TAG_MASK;
+    }
+
+    return type;
+}
+
+static uint64_t ber_read_length(BERInputVisitor *aiv, bool *is_indefinite,
+                                Error **errp)
+{
+    uint8_t byte, c, int_len;
+    uint64_t len = 0;
+    QEMUFile *qfile = aiv->qfile;
+    unsigned char int_array[sizeof(len)];
+    char buf[128];
+
+    *is_indefinite = false;
+
+    if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+        error_setg(errp, "QEMUFile has an error, error was '%s'",
+                  "Error while reading length indicator");
+        return ~0x0ULL;
+    }
+    aiv->cur_pos++;
+
+    if (byte == BER_LENGTH_INDEFINITE) {
+        *is_indefinite = true;
+        return ~0x0ULL;
+    }
+
+    if (!(byte & BER_LENGTH_LONG)) {
+        len = byte;
+    } else {
+        int_len = byte & BER_LENGTH_MASK;
+        if (int_len > sizeof(len)) {
+            snprintf(buf, sizeof(buf),
+                     "ASN.1 integer length field %d > %" PRIu64,
+                     int_len, sizeof(len));
+            /* Length can be up to 127 byte, but it seems
+             * safe to assume any input will be < 1TB in length. */
+            error_set(errp, QERR_INVALID_PARAMETER, buf);
+            return ~0x0ULL;
+        }
+        if (qemu_read_bytes(qfile, int_array, int_len) != int_len) {
+            error_setg(errp, "QEMUFile error: Error while reading length");
+            return ~0x0ULL;
+        }
+        for (c = 0; c < int_len; c++) {
+            len <<= 8;
+            len |= int_array[c];
+        }
+        aiv->cur_pos += int_len;
+    }
+
+    if (len > aiv->max_allowed_buffer_size) {
+        snprintf(buf, sizeof(buf),
+                 "Length indicator (%"PRIu64") in input byte stream "
+                 "exceeds maximum allowed length (%"PRIu64").",
+                 len, aiv->max_allowed_buffer_size);
+        error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+        return ~0x0ULL;
+    }
+
+    if (len > aiv->largest_needed_buffer) {
+        aiv->largest_needed_buffer = len;
+    }
+
+    return len;
+}
+
+static uint64_t ber_peek_is_eoc(BERInputVisitor *biv, Error **errp)
+{
+    uint8_t buf[2];
+    QEMUFile *qfile = biv->qfile;
+
+    if (qemu_peek_bytes(qfile, buf, 2, 0) != 2) {
+        error_setg(errp, "QEMUFile has an error, error was '%s'",
+                   "Error while peeking for EOC");
+        return ~0x0ULL;
+    }
+
+    if (buf[0] == BER_TYPE_EOC && buf[1] == 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void ber_skip_bytes(BERInputVisitor *aiv, uint64_t to_skip,
+                           Error **errp)
+{
+    uint8_t buf[1024];
+    uint32_t skip;
+
+    /* skip length bytes */
+    while (to_skip > 0) {
+        skip = MIN(to_skip, sizeof(buf));
+        if (qemu_read_bytes(aiv->qfile, buf, skip) != skip) {
+            error_setg(errp, "QEMUFile error: Error while skipping over bytes");
+            return;
+        }
+        aiv->cur_pos += skip;
+        to_skip -= skip;
+    }
+}
+
+static void ber_skip_until_eoc(BERInputVisitor *aiv, Error **errp)
+{
+    uint32_t ber_type_tag;
+    uint64_t length;
+    bool is_indefinite;
+    uint8_t ber_type_flags;
+    uint64_t indefinite_nesting = 1;
+    char buf[128];
+
+    while (!error_is_set(errp)) {
+        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+
+        length = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+        if (ber_type_tag == BER_TYPE_EOC) {
+            if (length) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 EOC length field at offset %" PRIu64
+                         " is invalid", aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            if (!indefinite_nesting) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 EOC at offset %" PRIu64
+                         "not within an indefinite length",
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            DPRINTF("%s: found end! nesting=%" PRIdMAX
+                    ", pos=%" PRIu64 "\n",
+                    __func__, indefinite_nesting, aiv->cur_pos);
+            if (!--indefinite_nesting) {
+                return;
+            }
+        }
+        if (is_indefinite) {
+            if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 indefinite length in a primitive type "
+                         "at offset %" PRIu64,
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            if (indefinite_nesting == ~0x0ULL) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 indefinite nesting level is too large "
+                         "(offset %" PRIu64 ")",
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                return;
+            }
+            ++indefinite_nesting;
+        } else {
+            DPRINTF("%s: skipping type '%s' of length "
+                    "%" PRIu64 " at %" PRIu64 ".\n",
+                    __func__, ber_type_to_str(ber_type_tag),
+                    length, aiv->cur_pos);
+            ber_skip_bytes(aiv, length, errp);
+        }
+    }
+}
+
+static void ber_input_start_constructed(Visitor *v, uint32_t exp_ber_type,
+                                        uint8_t exp_ber_flags, void **obj,
+                                        const char *kind, const char *name,
+                                        size_t size, Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    int64_t len;
+    bool is_indefinite;
+    char buf[128];
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (ber_type_tag != exp_ber_type || ber_type_flags != exp_ber_flags) {
+        sprintf(buf, "%s at offset %" PRIu64,
+                ber_type_to_str(exp_ber_type), aiv->cur_pos);
+
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag),
+                  buf);
+        return;
+    }
+
+    if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+        snprintf(buf, sizeof(buf), "primitive type (%s)",
+                 ber_type_to_str(ber_type_tag));
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  buf, "constructed type");
+        return;
+    }
+
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (!is_indefinite) {
+        DPRINTF("%s: structure/set len: %" PRIi64 "\n", __func__, len);
+        ber_input_push(aiv, aiv->cur_pos + len, errp);
+    } else {
+        DPRINTF("%s: indefinite length encoding!\n", __func__);
+        ber_input_push(aiv, 0, errp);
+    }
+
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (size > 0 && *obj == NULL) {
+        *obj = g_malloc0(size);
+        DPRINTF("%s: for type '%s' allocated buffer at %p, "
+                "size = %zu\n",
+                __func__, ber_type_to_str(ber_type_tag), *obj, size);
+    }
+}
+
+static void ber_input_end_constructed(Visitor *v, Error **errp)
+{
+    uint64_t new_pos;
+    BERInputVisitor *aiv = to_biv(v);
+
+    new_pos = ber_input_pop(aiv, errp);
+
+    if (new_pos != 0) {
+        DPRINTF("%s: new_pos = %" PRIu64 "\n", __func__, new_pos);
+        aiv->cur_pos = new_pos;
+    } else {
+        DPRINTF("%s: searching for end...\n"
+                "%s: cur_pos = %" PRIu64 "\n",
+                __func__, __func__, aiv->cur_pos);
+        ber_skip_until_eoc(aiv, errp);
+    }
+}
+
+static void ber_input_start_struct(Visitor *v, void **obj, const char *kind,
+                                   const char *name, size_t size, Error **errp)
+{
+    ber_input_start_constructed(v, BER_TYPE_SEQUENCE, BER_TYPE_CONSTRUCTED,
+                                obj, kind, name, size, errp);
+}
+
+static void ber_input_end_struct(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_optional(Visitor *v, bool *present,
+                                     const char *name, Error **errp)
+{
+    ber_input_start_constructed(v, BER_TYPE_CUSTOM_OPTIONAL,
+                                BER_TYPE_CONSTRUCTED,
+                                NULL, "", name, 0, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    ber_input_type_bool(v, present, name, errp);
+}
+
+static void ber_input_end_optional(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_list(Visitor *v, const char *name,
+                                 Error **errp)
+{
+    void *obj = NULL;
+    ber_input_start_constructed(v, BER_TYPE_CUSTOM_LIST, BER_TYPE_CONSTRUCTED,
+                                obj, NULL, name, 0, errp);
+    g_free(obj);
+}
+
+static GenericList *ber_input_next_list(Visitor *v, GenericList **list,
+                                        Error **errp)
+{
+    BERInputVisitor *biv = to_biv(v);
+    GenericList *entry;
+    StackEntry *se = &biv->stack[biv->nb_stack - 1];
+    bool first = (se->counter++ == 0);
+
+    if (se->cur_pos == 0) {
+        /* indefinite lenght encoding is used */
+        se = &biv->stack[biv->nb_stack];
+        if (ber_peek_is_eoc(biv, errp) != 0) {
+            return NULL;
+        }
+    } else if (se->cur_pos <= biv->cur_pos) {
+        return NULL;
+    }
+
+    entry = g_malloc0(sizeof(*entry));
+    if (first) {
+        *list = entry;
+    } else {
+        (*list)->next = entry;
+    }
+
+    return entry;
+}
+
+static void ber_input_end_list(Visitor *v, Error **errp)
+{
+    ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_integer(Visitor *v, uint8_t *obj, uint8_t maxbytes,
+                              Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    bool is_indefinite;
+    uint64_t len;
+    uint64_t val = 0;
+    unsigned char int_array[sizeof(val)];
+    int c;
+    char buf[128], buf2[128];
+
+    DPRINTF("%s: reading int to %p\n", __func__, obj);
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    DPRINTF("%s: got type: 0x%02x, expected 0x%02x\n",
+            __func__, ber_type_tag, BER_TYPE_INTEGER);
+
+    if (ber_type_tag != BER_TYPE_INTEGER ||
+        ber_type_flags != (BER_TYPE_CLASS_APPLICATION|BER_TYPE_PRIMITIVE)) {
+        snprintf(buf, sizeof(buf), "%s/%s/%s",
+                 ber_type_class_to_str(ber_type_flags),
+                 ber_type_pc_to_str(ber_type_flags),
+                 ber_type_to_str(ber_type_tag));
+        snprintf(buf2, sizeof(buf2), "%s/%s/%s",
+                 ber_type_class_to_str(BER_TYPE_CLASS_APPLICATION),
+                 ber_type_pc_to_str(BER_TYPE_PRIMITIVE),
+                 ber_type_to_str(BER_TYPE_INTEGER));
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE, buf, buf2);
+        return;
+    }
+
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    DPRINTF("%s: pos: %" PRIu64 " int len: %" PRIi64 "\n",
+            __func__, aiv->cur_pos, len);
+
+    if (is_indefinite) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  "ASN.1 int indicator is indefinite",
+                  "[1..8]");
+        return;
+    }
+
+    if (maxbytes > sizeof(val)) {
+        snprintf(buf, sizeof(buf), "ASN.1 integers cannot have a length of "
+                 "%" PRIi32 " bytes", maxbytes);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "[1..8]");
+        return;
+    }
+
+    if (len > maxbytes) {
+        snprintf(buf, sizeof(buf), "ASN.1 integer length indicator %" PRIi64
+                 " is larger than expected (%u bytes)",
+                 len, maxbytes);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "[1..8]");
+        return;
+    }
+
+    if (qemu_read_bytes(aiv->qfile, int_array, len) != len) {
+        error_setg(errp, "QEMUFile error: Error while reading integer");
+        return;
+    }
+
+    for (c = 0; c < len ; c++) {
+        val <<= 8;
+        val |= int_array[c];
+        if (c == 0 && (val & 0x80) == 0x80) {
+            /* sign extend */
+            val |= 0xFFFFFFFFFFFFFF00ULL;
+        }
+    }
+    aiv->cur_pos += len;
+    DPRINTF("%s: pos: %" PRIu64 " int: %" PRIu64 "\n",
+            __func__, aiv->cur_pos, val);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    memcpy(obj, &val, maxbytes);
+#elif __BYTE_ORDER == __BIG_ENDIAN
+    memcpy(obj, &((char *)&val)[sizeof(val) - maxbytes], maxbytes);
+#endif
+}
+
+static void ber_input_type_int(Visitor *v, int64_t *obj, const char *name,
+                               Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint8(Visitor *v, uint8_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint16(Visitor *v, uint16_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint32(Visitor *v, uint32_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint64(Visitor *v, uint64_t *obj,
+                                    const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int8(Visitor *v, int8_t *obj,
+                                  const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int16(Visitor *v, int16_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int32(Visitor *v, int32_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int64(Visitor *v, int64_t *obj,
+                                   const char *name, Error **errp)
+{
+    ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
+                                Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags, byte;
+    bool is_indefinite;
+    uint64_t len;
+    char buf[128];
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (ber_type_tag != BER_TYPE_BOOLEAN || ber_type_flags != 0) {
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag),
+                  ber_type_to_str(BER_TYPE_BOOLEAN));
+        return;
+    }
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    DPRINTF("%s: pos: %" PRIu64 " bool len: %" PRIi64 "\n",
+            __func__, aiv->cur_pos, len);
+
+    if (is_indefinite || len != 1) {
+        snprintf(buf, sizeof(buf),
+                 "ASN.1 bool length indicator at offset %" PRIu64
+                 " is indefinite or != 1",
+                 aiv->cur_pos);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "1");
+        return;
+    }
+    if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+        error_setg(errp, "QEMUFile error: Error while reading boolean");
+        return;
+    }
+    aiv->cur_pos++;
+    *obj = byte;
+
+    DPRINTF("%s: pos: %" PRIu64 " bool: %d\n",
+            __func__, aiv->cur_pos, *obj);
+}
+
+/* Function for recursive reading of fragmented primitives */
+static uint32_t ber_input_fragment(BERInputVisitor *aiv,
+                                   uint32_t exp_type_tag,
+                                   uint8_t exp_type_flags,
+                                   uint8_t **buffer,
+                                   size_t *buffer_len, size_t elem_size,
+                                   bool may_realloc,
+                                   uint32_t offset, uint32_t nesting,
+                                   bool indefinite, uint64_t max_pos,
+                                   const char *name, Error **errp)
+{
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    uint32_t bytes_read = 0;
+    bool is_indefinite;
+    uint64_t len;
+    char buf[128];
+    uint8_t *conv_buffer;
+    size_t num_elms;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+    size_t i;
+    uint16_t *d16, *s16;
+    uint32_t *d32, *s32;
+    uint64_t *d64, *s64;
+#endif
+    bool is_bigendian;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+    is_bigendian = true;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+    is_bigendian = false;
+#else
+# error Unsupported endianess
+#endif
+
+    assert((exp_type_flags & BER_TYPE_CONSTRUCTED) == BER_TYPE_PRIMITIVE);
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return 0;
+    }
+
+    if (ber_type_tag != exp_type_tag) {
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                  ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+                  ber_type_to_str(exp_type_tag));
+        return 0;
+    }
+
+    if ((ber_type_flags & BER_TYPE_CONSTRUCTED)) {
+#ifndef BER_ALLOW_FRAGMENTED_PRIMITIVES
+        error_setg(errp, "Data stream is invalid, error was '%s'",
+                  "constructed encoding of primitive types is not supported");
+        goto err_exit;
+#else
+        if (nesting == 1) {
+            /* don't allow further nesting */
+            error_setg(errp, "Data stream is invalid, error was invalid "
+                       "nesting");
+            goto err_exit;
+        }
+        len = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+        DPRINTF("%s: pos: %" PRIu64 " string len: %" PRIi64 "\n",
+                __func__, aiv->cur_pos, len);
+
+        if (!is_indefinite) {
+            if ((*buffer) == NULL) {
+                /* allocate buffer once; due to the ASN.1 overhead it
+                 * will be bigger than what we need */
+                *buffer = g_malloc0(len);
+                *buffer_len = len;
+                may_realloc = false;
+            }
+        }
+        DPRINTF("%s: recursing now to read constructed type.\n"
+                "%s: is_indefinite: %d\n",
+                __func__, __func__, is_indefinite);
+        bytes_read += ber_input_fragment(aiv, exp_type_tag, exp_type_flags,
+                                         buffer, buffer_len, elem_size,
+                                         may_realloc, offset, nesting + 1,
+                                         is_indefinite, aiv->cur_pos + len,
+                                         name, errp);
+        return bytes_read;
+#endif
+    }
+
+    while (true) {
+        /* Would reading the length carry us beyond what we are allowed to
+         * read?
+         */
+        if (!indefinite &&
+            max_pos != 0 &&
+            aiv->cur_pos + 1 > max_pos) {
+            snprintf(buf, sizeof(buf),
+                     "data stream would cause parsing beyond "
+                     "allowed offset at %" PRIu64,
+                     max_pos);
+            /* input stream is malformed */
+            error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+            goto err_exit;
+        }
+
+        /* not-constructed case */
+        len = ber_read_length(aiv, &is_indefinite, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+        DPRINTF("%s: pos: %" PRIu64 " string len: %" PRIi64 "\n",
+                __func__, aiv->cur_pos, len);
+        if (is_indefinite) {
+            snprintf(buf, sizeof(buf),
+                     "Got indefinite type length in primitive type (%s) at"
+                     "offset %" PRIu64,
+                     ber_type_to_str(ber_type_tag), aiv->cur_pos);
+            error_set(errp, QERR_INVALID_PARAMETER, buf);
+            goto err_exit;
+        }
+        /* if max_pos is not set, set it here */
+        if (!indefinite && max_pos == 0) {
+            max_pos = aiv->cur_pos + len;
+        }
+
+        /* Would reading the data carry us beyond what we are allowed to
+         * read ?
+         */
+        if (!indefinite && aiv->cur_pos + len > max_pos) {
+            /* input stream is malformed */
+            snprintf(buf, sizeof(buf),
+                     "data stream would cause parsing beyond "
+                     "allowed offset at %" PRIu64,
+                     max_pos);
+            error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+            goto err_exit;
+        }
+
+        if (offset + len > *buffer_len) {
+            if (!may_realloc) {
+                snprintf(buf, sizeof(buf),
+                         "given buffer is too small (%lu < %"PRIu64")",
+                         (unsigned long)*buffer_len, offset + len);
+                error_setg(errp, "Data stream is invalid, error was '%s'",
+                           buf);
+                goto err_exit;
+            }
+            /* allocate one more byte for strings, set to 0 */
+            *buffer = g_realloc(*buffer, offset + len + 1);
+            *buffer_len = offset + len;
+            (*buffer)[offset+len] = 0;
+        }
+
+        num_elms = len / elem_size;
+        if (num_elms * elem_size != len) {
+            error_setg(errp, "Stream contains broken up element of size "
+                       "%"PRIi64, elem_size);
+            goto err_exit;
+        }
+
+        if (elem_size != sizeof(uint8_t) && !is_bigendian) {
+            /* intermediate buffer for endianess conversion */
+            conv_buffer = g_malloc(len);
+        } else {
+            conv_buffer = &((uint8_t *)*buffer)[offset];
+        }
+
+        if (qemu_read_bytes(aiv->qfile, conv_buffer, len) != len) {
+            error_setg(errp, "QEMUFile error: Error while reading data");
+            goto err_exit;
+        }
+
+        switch (elem_size) {
+        case sizeof(uint8_t):
+#if __BYTE_ORDER == __BIG_ENDIAN
+        case sizeof(uint16_t):
+        case sizeof(uint32_t):
+        case sizeof(uint64_t):
+#endif
+            /* nothing to convert */
+            break;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+        case sizeof(uint16_t):
+            s16 = (uint16_t *)conv_buffer;
+            d16 = (uint16_t *)&((uint8_t *)*buffer)[offset];
+            for (i = 0; i < num_elms; i++) {
+                d16[i] = be16_to_cpu(s16[i]);
+            }
+            break;
+        case sizeof(uint32_t):
+            s32 = (uint32_t *)conv_buffer;
+            d32 = (uint32_t *)&((uint8_t *)*buffer)[offset];
+            for (i = 0; i < num_elms; i++) {
+                d32[i] = be32_to_cpu(s32[i]);
+            }
+            break;
+        case sizeof(uint64_t):
+            s64 = (uint64_t *)conv_buffer;
+            d64 = (uint64_t *)&((uint8_t *)*buffer)[offset];
+            for (i = 0; i < num_elms; i++) {
+                d64[i] = be64_to_cpu(s64[i]);
+            }
+            break;
+#endif
+        }
+
+        if (elem_size != sizeof(uint8_t) && !is_bigendian) {
+            /* intermediate buffer for endianess conversion */
+            g_free(conv_buffer);
+        }
+
+        offset += len;
+        bytes_read += len;
+
+        aiv->cur_pos += len;
+
+        if (exp_type_tag == BER_TYPE_IA5_STRING) {
+            DPRINTF("%s: pos: %" PRIu64 " string: %.*s\n",
+                    __func__, aiv->cur_pos, offset, *buffer);
+        }
+
+        if (nesting == 0) {
+            break;
+        }
+
+        /* indefinite length case: loop until we encounter EOC */
+        if (indefinite) {
+            ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+            if (error_is_set(errp)) {
+                goto err_exit;
+            }
+
+            if (ber_type_tag == BER_TYPE_EOC) {
+                uint8_t byte;
+                if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+                    error_setg(errp, "QEMUFile error: Error while reading "
+                               "BER_TYPE_EOC length");
+                    goto err_exit;
+                }
+                aiv->cur_pos++;
+
+                if (byte != 0) {
+                    snprintf(buf, sizeof(buf),
+                             "ASN.1 EOC length field is invalid at offset "
+                             "%" PRIu64,
+                             aiv->cur_pos);
+                    error_set(errp, QERR_INVALID_PARAMETER, buf);
+                    goto err_exit;
+                }
+                return bytes_read;
+            }
+
+            if (ber_type_tag != exp_type_tag ||
+                ber_type_flags != exp_type_flags) {
+                snprintf(buf, sizeof(buf),
+                         "ASN.1 type field or flags are wrong. Found "
+                         "0x%x/%u, expected "
+                         "0x%x/%u at offset %" PRIu64,
+                         ber_type_tag, ber_type_flags,
+                         exp_type_tag, exp_type_flags,
+                         aiv->cur_pos);
+                error_set(errp, QERR_INVALID_PARAMETER, buf);
+                goto err_exit;
+            }
+            continue;
+        }
+
+        /* in definite length coding case; caller told us how far to read */
+        if (aiv->cur_pos == max_pos) {
+            return bytes_read;
+        }
+
+        ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+        if (error_is_set(errp)) {
+            goto err_exit;
+        }
+
+        if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_CONSTRUCTED) {
+            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                      "constructed BER type",
+                      ber_type_to_str(exp_type_tag));
+            goto err_exit;
+        }
+
+        if (ber_type_tag != exp_type_tag) {
+            error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+                      ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+                      ber_type_to_str(exp_type_tag));
+            goto err_exit;
+        }
+    }
+    return bytes_read;
+
+err_exit:
+    if (may_realloc) {
+        g_free(*buffer);
+        *buffer = NULL;
+    }
+    return 0;
+}
+
+static void ber_input_type_str(Visitor *v, char **obj, const char *name,
+                               Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    size_t buffer_len = 0;
+
+    ber_input_fragment(aiv, BER_TYPE_IA5_STRING, 0,
+                       (uint8_t **)obj, &buffer_len, 1, (*obj == NULL),
+                       0, 0, false, 0, name, errp);
+
+    if (!error_is_set(errp) && *obj == NULL) {
+        /* adjust NULL string to "" */
+        *obj = g_strdup("");
+    }
+}
+
+static void ber_input_sized_buffer(Visitor *v, void **obj, const char *name,
+                                   size_t num_elem, size_t elem_size,
+                                   Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    size_t len = num_elem * elem_size;
+
+    if (*obj == NULL) {
+        /*
+         * Allocating the memory will prevent a smaller structure from
+         * being allocated by ber_input_fragment.
+         * Setting len = 0  would allow less bytes to be allocated than
+         * expected by the caller, which may cause segfaults if the caller
+         * then steps on unallocated memory.
+         */
+        *obj = g_malloc(len);
+    }
+
+    ber_input_fragment(aiv, BER_TYPE_OCTET_STRING, 0,
+                       (uint8_t **)obj, &len, elem_size, (*obj == NULL),
+                       0, 0, false, 0, name, errp);
+
+#ifdef BER_DEBUG
+    DPRINTF("%s: pos: %" PRIu64 " data at: %p data:\n",
+            __func__, aiv->cur_pos, *obj);
+    int i;
+    for (i = 0; i < len; i++) {
+        DPRINTF("%02x ", (*obj)[i]);
+        if ((i & 0xf) == 0xf) {
+            DPRINTF("\n");
+        }
+    }
+    DPRINTF("\n");
+#endif
+}
+
+static void ber_input_type_number(Visitor *v, double *obj, const char *name,
+                                  Error **errp)
+{
+    BERInputVisitor *aiv = to_biv(v);
+    uint32_t ber_type_tag;
+    uint8_t ber_type_flags;
+    uint32_t len;
+    bool is_indefinite;
+    char buf[128], buf2[128];
+    GDoubleIEEE754 num;
+    struct ieee754_buffer number;
+    size_t to_read;
+
+    ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (ber_type_tag != BER_TYPE_REAL ||
+        ber_type_flags != (BER_TYPE_CLASS_APPLICATION|BER_TYPE_PRIMITIVE)) {
+        snprintf(buf, sizeof(buf), "%s/%s/%s",
+                 ber_type_class_to_str(ber_type_flags),
+                 ber_type_pc_to_str(ber_type_flags),
+                 ber_type_to_str(ber_type_tag));
+        snprintf(buf2, sizeof(buf2), "%s/%s/%s",
+                 ber_type_class_to_str(BER_TYPE_CLASS_APPLICATION),
+                 ber_type_pc_to_str(BER_TYPE_PRIMITIVE),
+                 ber_type_to_str(BER_TYPE_REAL));
+        error_set(errp, QERR_INVALID_PARAMETER_TYPE, buf, buf2);
+        return;
+    }
+
+    len = ber_read_length(aiv, &is_indefinite, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    to_read = sizeof(number) - offsetof(struct ieee754_buffer, first);
+
+    if (len != to_read) {
+        snprintf(buf, sizeof(buf),
+                 "Length indicator in input byte stream "
+                 "of real has unexpected length %"PRIu32"; "
+                 "expected %" PRIu64,
+                 len, sizeof(buf));
+        error_set(errp, QERR_INVALID_PARAMETER, buf);
+        return;
+    }
+
+    if (is_indefinite) {
+        snprintf(buf, sizeof(buf),
+                 "ASN.1 indefinite length in a real type "
+                 "at offset %" PRIu64,
+                 aiv->cur_pos);
+        error_set(errp, QERR_INVALID_PARAMETER, buf);
+        return;
+    }
+
+    if (qemu_read_bytes(aiv->qfile, &number.first, to_read) != to_read) {
+        error_setg(errp, "QEMUFile error: Error while reading real");
+        return;
+    }
+
+    switch (number.first) {
+    case 0x42:
+        *obj = nan("NAN");
+        break;
+    case 0x41:
+    case 0x40:
+        num.mpn.sign = ((number.first & 0x1) != 0);
+        num.mpn.biased_exponent = ~0;
+        num.mpn.mantissa_low = 0;
+        num.mpn.mantissa_high = 0;
+        *obj = num.v_double;
+        break;
+    default:
+        num.mpn.sign = ((number.first & 0x40) != 0);
+        num.mpn.biased_exponent = be16_to_cpu(number.exponent);
+        num.mpn.mantissa_low = be32_to_cpu(number.mant_lo);
+        num.mpn.mantissa_high = be32_to_cpu(number.mant_hi);
+        *obj = num.v_double;
+    }
+}
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v)
+{
+    return &v->visitor;
+}
+
+uint64_t ber_input_get_parser_position(BERInputVisitor *v)
+{
+    return v->cur_pos;
+}
+
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v)
+{
+    return v->largest_needed_buffer;
+}
+
+void ber_input_visitor_cleanup(BERInputVisitor *v)
+{
+    g_free(v);
+}
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *qfile,
+                                       uint64_t max_allowed_buffer_size)
+{
+    BERInputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.start_struct = ber_input_start_struct;
+    v->visitor.end_struct = ber_input_end_struct;
+    v->visitor.start_optional = ber_input_start_optional;
+    v->visitor.end_optional = ber_input_end_optional;
+    v->visitor.start_list = ber_input_start_list;
+    v->visitor.next_list = ber_input_next_list;
+    v->visitor.end_list = ber_input_end_list;
+    v->visitor.type_int = ber_input_type_int;
+    v->visitor.type_uint8 = ber_input_type_uint8;
+    v->visitor.type_uint16 = ber_input_type_uint16;
+    v->visitor.type_uint32 = ber_input_type_uint32;
+    v->visitor.type_uint64 = ber_input_type_uint64;
+    v->visitor.type_int8 = ber_input_type_int8;
+    v->visitor.type_int16 = ber_input_type_int16;
+    v->visitor.type_int32 = ber_input_type_int32;
+    v->visitor.type_int64 = ber_input_type_int64;
+    v->visitor.type_bool = ber_input_type_bool;
+    v->visitor.type_str = ber_input_type_str;
+    v->visitor.type_sized_buffer = ber_input_sized_buffer;
+    v->visitor.type_number = ber_input_type_number;
+
+    v->qfile = qfile;
+    v->cur_pos = 0;
+    v->max_allowed_buffer_size = max_allowed_buffer_size;
+    v->largest_needed_buffer = 0;
+
+    return v;
+}
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 7/8] Extend test-visitor-serialization with ASN.1 visitor(s)
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (5 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 6/8] ASN.1 input visitor Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 8/8] ASN.1 specific test cases Stefan Berger
  2013-03-28 11:43 ` [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

Add BER visitor hooks to test-visitor-serialization

Cc: Michael Tsirkin <mst@redhat.com>
Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 tests/Makefile                     |  2 +-
 tests/test-visitor-serialization.c | 78 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/tests/Makefile b/tests/Makefile
index 567e36e..578d732 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -124,7 +124,7 @@ tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-q
 tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) qapi-types.o qapi-visit.o libqemuutil.a libqemustub.a
-tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
+tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) $(block-obj-y) libqemuutil.a libqemustub.a
 
 tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
 
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 3c6b8df..ef3c8e8 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -23,6 +23,9 @@
 #include "qapi/qmp-output-visitor.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/string-output-visitor.h"
+#include "qapi/ber-input-visitor.h"
+#include "qapi/ber-output-visitor.h"
+#include "migration/qemu-file.h"
 
 typedef struct PrimitiveType {
     union {
@@ -701,6 +704,66 @@ static void string_cleanup(void *datap)
     string_input_visitor_cleanup(d->siv);
 }
 
+
+typedef struct BERSerializeData {
+    BEROutputVisitor *sov;
+    QEMUFile *qoutfile;
+    BERInputVisitor *siv;
+    QEMUFile *qinfile;
+} BERSerializeData;
+
+static void ber_serialize(void *native_in, void **datap,
+                          VisitorFunc visit, Error **errp,
+                          BERLengthEncoding ber_length_encoding)
+{
+    BERSerializeData *d = g_malloc0(sizeof(*d));
+
+    d->qoutfile = qemu_bufopen("w", NULL);
+    d->sov = ber_output_visitor_new(d->qoutfile, ber_length_encoding);
+    visit(ber_output_get_visitor(d->sov), &native_in, errp);
+    *datap = d;
+}
+
+static void ber_definite_serialize(void *native_in, void **datap,
+                                   VisitorFunc visit, Error **errp)
+{
+    ber_serialize(native_in, datap, visit, errp,
+                  BER_LENGTH_ENCODING_DEFINITE);
+}
+
+static void ber_indefinite_serialize(void *native_in, void **datap,
+                                     VisitorFunc visit, Error **errp)
+{
+    ber_serialize(native_in, datap, visit, errp,
+                  BER_LENGTH_ENCODING_INDEFINITE);
+}
+
+static void ber_deserialize(void **native_out, void *datap,
+                               VisitorFunc visit, Error **errp)
+{
+    BERSerializeData *d = datap;
+    const QEMUSizedBuffer *qsb = qemu_buf_get(d->qoutfile);
+    QEMUSizedBuffer *new_qsb = qsb_clone(qsb);
+    g_assert(new_qsb != NULL);
+
+    d->qinfile = qemu_bufopen("r", new_qsb);
+
+    d->siv = ber_input_visitor_new(d->qinfile, ~0);
+    visit(ber_input_get_visitor(d->siv), native_out, errp);
+}
+
+static void ber_cleanup(void *datap)
+{
+    BERSerializeData *d = datap;
+
+    ber_output_visitor_cleanup(d->sov);
+    ber_input_visitor_cleanup(d->siv);
+    qemu_fclose(d->qinfile);
+    qemu_fclose(d->qoutfile);
+    g_free(d);
+}
+
+
 /* visitor registration, test harness */
 
 /* note: to function interchangeably as a serialization mechanism your
@@ -722,6 +785,21 @@ static const SerializeOps visitors[] = {
         .cleanup = string_cleanup,
         .caps = VCAP_PRIMITIVES
     },
+    {
+        .type = "ASN.1 BER indefinite l.e.",
+        .serialize = ber_indefinite_serialize,
+        .deserialize = ber_deserialize,
+        .cleanup = ber_cleanup,
+        .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
+    },
+    {
+        .type = "ASN.1 BER definite l.e.",
+        .serialize = ber_definite_serialize,
+        .deserialize = ber_deserialize,
+        .cleanup = ber_cleanup,
+        .caps = VCAP_PRIMITIVES | VCAP_STRUCTURES | VCAP_LISTS
+    },
+
     { NULL }
 };
 
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v5 8/8] ASN.1 specific test cases
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (6 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 7/8] Extend test-visitor-serialization with ASN.1 visitor(s) Stefan Berger
@ 2013-03-28 11:38 ` Stefan Berger
  2013-03-28 11:43 ` [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:38 UTC (permalink / raw)
  To: stefanb, qemu-devel, anthony; +Cc: jschopp, coreyb, mdroth, mst

BER visitor tests give us some assurance that the BER visitor
code works, and also end up by extension helping out on our
code coverage of the filesystem tests.
After the output visitor invocation the resulting buffer is
compared against a known byte stream -- this will lock the
implementation into producing specific byte arrays.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 tests/Makefile           |  13 +
 tests/test-ber-visitor.c | 894 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 907 insertions(+)
 create mode 100644 tests/test-ber-visitor.c

diff --git a/tests/Makefile b/tests/Makefile
index 578d732..d1a3724 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -23,6 +23,10 @@ check-unit-y += tests/test-string-input-visitor$(EXESUF)
 gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c
 check-unit-y += tests/test-string-output-visitor$(EXESUF)
 gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c
+check-unit-y += tests/test-ber-visitor$(EXESUF)
+gcov-files-test-vistor-y = qapi/ber-input-visitor.c
+
+
 check-unit-y += tests/test-coroutine$(EXESUF)
 ifeq ($(CONFIG_WIN32),y)
 gcov-files-test-coroutine-y = coroutine-win32.c
@@ -134,6 +138,15 @@ tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o
 
+tests/test-ber-visitor.o : QEMU_CFLAGS += -I include/qapi
+
+tests/test-ber-visitor.o: \
+	$(addprefix include/qapi/, ber.h ber-input-visitor.h ber-output-visitor.h) \
+	$(addprefix qapi/, ber-common.c ber-input-visitor.c ber-output-visitor.c)
+tests/test-ber-visitor$(EXESUF): tests/test-ber-visitor.o $(tools-obj-y) \
+	qapi/ber-output-visitor.o qapi/ber-input-visitor.o \
+	qapi/ber-common.o $(block-obj-y) libqemuutil.a libqemustub.a
+
 # QTest rules
 
 TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
diff --git a/tests/test-ber-visitor.c b/tests/test-ber-visitor.c
new file mode 100644
index 0000000..526482a
--- /dev/null
+++ b/tests/test-ber-visitor.c
@@ -0,0 +1,894 @@
+/*
+ * BER Output Visitor unit-tests.
+ *
+ * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011, 2012, 2013 IBM Corporation
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/ber-output-visitor.h"
+#include "qapi/ber-input-visitor.h"
+#include "hw/hw.h"
+#include "include/qapi/visitor.h"
+
+
+typedef struct TestExpResult {
+    const uint8_t *exp;
+    size_t exp_len;
+} TestExpResult;
+
+typedef struct TestInputOutputVisitorData {
+    QEMUFile *qoutfile;
+    BEROutputVisitor *bov;
+    Visitor *ov;
+
+    QEMUFile *qinfile;
+    BERInputVisitor *biv;
+    Visitor *iv;
+
+    const TestExpResult *ter;
+} TestInputOutputVisitor;
+
+static void test_visitor_dump(const uint8_t *exp, size_t exp_len,
+                              const uint8_t *act, size_t act_len)
+{
+    size_t i;
+
+    fprintf(stderr, "\nExpected output:");
+
+    for (i = 0; i < exp_len; i++) {
+        if ((i % 0x8) == 0) {
+            fprintf(stderr, "\n    ");
+        }
+        fprintf(stderr, "0x%02x, ", exp[i]);
+    }
+
+    fprintf(stderr, "\nActual output:");
+
+    for (i = 0; i < act_len; i++) {
+        if ((i % 0x8) == 0) {
+            fprintf(stderr, "\n    ");
+        }
+        fprintf(stderr, "0x%02x, ", act[i]);
+    }
+    fprintf(stderr, "\n");
+}
+
+static void test_visitor_check_result(const uint8_t *exp, size_t exp_len,
+                                      const uint8_t *act, size_t act_len)
+{
+    size_t i;
+
+    if (exp_len != act_len) {
+        test_visitor_dump(exp, exp_len, act, act_len);
+    }
+
+    for (i = 0; i < exp_len; i++) {
+        if (exp[i] != act[i]) {
+            test_visitor_dump(exp, exp_len, act, act_len);
+        }
+    }
+}
+
+static void visitor_output_setup(TestInputOutputVisitor *data,
+                                 BERLengthEncoding ber_length_encoding,
+                                 const TestExpResult *ter)
+{
+    data->ter = ter;
+
+    data->qoutfile = qemu_bufopen("w", NULL);
+
+    data->bov = ber_output_visitor_new(data->qoutfile, ber_length_encoding);
+    g_assert(data->bov != NULL);
+
+    data->ov = ber_output_get_visitor(data->bov);
+    g_assert(data->ov != NULL);
+}
+
+static void visitor_output_setup_indefinite(TestInputOutputVisitor *data,
+                                           const void *results)
+{
+    const TestExpResult *ter = results;
+
+    visitor_output_setup(data, BER_LENGTH_ENCODING_INDEFINITE, ter);
+}
+
+static void visitor_output_setup_definite(TestInputOutputVisitor *data,
+                                          const void *results)
+{
+    const TestExpResult *ter = results;
+
+    visitor_output_setup(data, BER_LENGTH_ENCODING_DEFINITE, ter);
+}
+
+static void visitor_input_setup(TestInputOutputVisitor *data)
+{
+    const QEMUSizedBuffer *qsb = qemu_buf_get(data->qoutfile);
+    QEMUSizedBuffer *new_qsb = qsb_clone(qsb);
+    unsigned char *buffer = NULL;
+    g_assert(new_qsb != NULL);
+
+    qsb_get_buffer(qsb, 0, qsb_get_length(qsb), &buffer);
+    test_visitor_check_result(data->ter->exp, data->ter->exp_len,
+                              buffer, qsb_get_length(qsb));
+    g_free(buffer);
+
+    data->qinfile = qemu_bufopen("r", new_qsb);
+    g_assert(data->qinfile != NULL);
+
+    data->biv = ber_input_visitor_new(data->qinfile, ~0);
+    g_assert(data->biv != NULL);
+
+    data->iv = ber_input_get_visitor(data->biv);
+    g_assert(data->iv != NULL);
+}
+
+static void visitor_output_teardown(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    ber_output_visitor_cleanup(data->bov);
+    data->bov = NULL;
+    data->ov = NULL;
+
+    ber_input_visitor_cleanup(data->biv);
+    data->biv = NULL;
+    data->iv = NULL;
+
+    if (data->qinfile) {
+        qemu_fclose(data->qinfile);
+    }
+    if (data->qoutfile) {
+        qemu_fclose(data->qoutfile);
+    }
+}
+
+static const uint8_t test_visitor_int_exp[] = {
+    0x42, 0x01, 0xd6,
+};
+
+static void test_visitor_out_int(TestInputOutputVisitor *data,
+                                 const void *unused)
+{
+    int64_t value_in = -42, value_out = 0;
+    Error *errp = NULL;
+
+    visit_type_int(data->ov, &value_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_int(data->iv, &value_out, NULL, &errp);
+    g_assert_cmpint(value_out, ==, -42);
+}
+
+static const uint8_t test_visitor_boolean_exp[] = {
+    0x01, 0x01, 0x01,
+};
+
+static void test_visitor_out_boolean(TestInputOutputVisitor *data,
+                                     const void *unused)
+{
+    Error *errp = NULL;
+    bool value_in = true, value_out = false;
+
+    visit_type_bool(data->ov, &value_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_bool(data->iv, &value_out, NULL, &errp);
+    g_assert_cmpint(value_out, ==, true);
+
+}
+
+static const uint8_t test_visitor_string_exp[] = {
+    0x16, 0x07, 0x51, 0x20, 0x45, 0x20, 0x4d, 0x20,
+    0x55,
+};
+
+static void test_visitor_out_string(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    char *string_in = (char *) "Q E M U", *string_out = NULL;
+    Error *errp = NULL;
+
+    visit_type_str(data->ov, &string_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_str(data->iv, &string_out, NULL, &errp);
+
+    g_assert_cmpstr(string_out, ==, string_in);
+    g_free(string_out);
+}
+
+static const uint8_t test_visitor_no_string_exp[] = {
+    0x16, 0x00,
+};
+
+static void test_visitor_out_no_string(TestInputOutputVisitor *data,
+                                       const void *unused)
+{
+    char *string_in = NULL, *string_out = NULL;
+    Error *errp = NULL;
+
+    /* A null string should return "" */
+    visit_type_str(data->ov, &string_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+    g_assert(qsb_get_length(qemu_buf_get(data->qoutfile)) == 2);
+
+    visitor_input_setup(data);
+
+    visit_type_str(data->iv, &string_out, NULL, &errp);
+    g_assert_cmpstr(string_out, ==, "");
+
+    g_free(string_out);
+}
+
+#define SIMPLE_STRUCT_ARRAY_SIZE 5
+
+typedef struct SimpleStruct {
+    int64_t integer;
+    bool boolean;
+    char *string;
+    uint32_t data[SIMPLE_STRUCT_ARRAY_SIZE];
+    uint16_t *extdata;
+} SimpleStruct;
+
+static void visit_type_SimpleStruct(Visitor *v, SimpleStruct **obj,
+                                    const char *name, Error **errp)
+{
+    uint32_t *data;
+    uint16_t *extdata;
+    visit_start_struct(v, (void **)obj, "SimpleStruct", name,
+                       sizeof(SimpleStruct), errp);
+
+    visit_type_int(v, &(*obj)->integer, "integer", errp);
+    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
+    visit_type_str(v, &(*obj)->string, "string", errp);
+
+    data = (uint32_t *)&(*obj)->data;
+
+    visit_type_sized_buffer(v, (void **)&data, "data",
+                            SIMPLE_STRUCT_ARRAY_SIZE, sizeof(uint32_t), errp);
+
+    extdata = (uint16_t *)(*obj)->extdata;
+
+    visit_type_sized_buffer(v, (void **)&extdata, "extdata",
+                            SIMPLE_STRUCT_ARRAY_SIZE, sizeof(uint16_t), errp);
+
+    (*obj)->extdata = extdata;
+
+    visit_end_struct(v, errp);
+}
+
+static const uint8_t test_visitor_struct_exp_indef[] = {
+    0x30, 0x80, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x00,
+    0x16, 0x03, 0x66, 0x6f, 0x6f, 0x04, 0x14, 0x00,
+    0x00, 0x11, 0x11, 0x00, 0x00, 0x22, 0x22, 0x00,
+    0x00, 0x33, 0x33, 0x00, 0x00, 0x44, 0x44, 0x00,
+    0x00, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x01, 0x00,
+    0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00,
+    0x00,
+};
+
+static const uint8_t test_visitor_struct_exp_def[] = {
+    0x30, 0x2d, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x00,
+    0x16, 0x03, 0x66, 0x6f, 0x6f, 0x04, 0x14, 0x00,
+    0x00, 0x11, 0x11, 0x00, 0x00, 0x22, 0x22, 0x00,
+    0x00, 0x33, 0x33, 0x00, 0x00, 0x44, 0x44, 0x00,
+    0x00, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x01, 0x00,
+    0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05,
+};
+
+
+static void test_visitor_out_struct(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    uint16_t extdata[] = {1, 2, 3, 4, 5};
+    SimpleStruct test_struct = {
+        .integer = 42,
+        .boolean = false,
+        .string = (char *)"foo",
+        .data = {
+            0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+        },
+        .extdata = extdata,
+    };
+    SimpleStruct *p_in = &test_struct, *p_out = NULL;
+    Error *errp = NULL;
+    int i;
+
+    visit_type_SimpleStruct(data->ov, &p_in, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStruct(data->iv, &p_out, NULL, &errp);
+
+    g_assert_cmpint(p_out->integer, ==, 42);
+    g_assert_cmpint(p_out->boolean, ==, 0);
+    g_assert_cmpstr(p_out->string, ==, "foo");
+
+    for (i = 0; i < ARRAY_SIZE(test_struct.data); i++) {
+        g_assert_cmpint(p_out->data[i], ==, (i + 1) * 0x1111);
+    }
+
+    for (i = 0; i < ARRAY_SIZE(extdata); i++) {
+        g_assert_cmpint(p_out->extdata[i], ==, (i + 1));
+    }
+
+    g_free(p_out->extdata);
+    g_free(p_out->string);
+    g_free(p_out);
+}
+
+typedef struct NestedStruct {
+    int64_t integer;
+    bool boolean;
+    SimpleStruct simpleStruct[2];
+    char *string;
+} NestedStruct;
+
+static void visit_type_NestedStruct(Visitor *v, NestedStruct **obj,
+                                    const char *name, Error **errp)
+{
+    unsigned int i;
+
+    visit_start_struct(v, (void **)obj, "NestedStruct", name,
+                       sizeof(NestedStruct), errp);
+
+    visit_type_int(v, &(*obj)->integer, "integer", errp);
+    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
+
+    for (i = 0; i < ARRAY_SIZE((*obj)->simpleStruct); i++) {
+        SimpleStruct *simple = &(*obj)->simpleStruct[i];
+        visit_type_SimpleStruct(v, &simple, "SimpleStruct", errp);
+    }
+
+    visit_type_str(v, &(*obj)->string, "string", errp);
+
+    visit_end_struct(v, errp);
+}
+
+static const uint8_t test_visitor_nested_struct_exp_indef[] = {
+    0x30, 0x80, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x01,
+    0x30, 0x80, 0x42, 0x02, 0x03, 0xe8, 0x01, 0x01,
+    0x00, 0x16, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+    0x04, 0x14, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00,
+    0x22, 0x22, 0x00, 0x00, 0x33, 0x33, 0x00, 0x00,
+    0x44, 0x44, 0x00, 0x00, 0x55, 0x55, 0x04, 0x0a,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x02,
+    0x00, 0x01, 0x00, 0x00, 0x30, 0x80, 0x42, 0x02,
+    0x07, 0xd0, 0x01, 0x01, 0x00, 0x16, 0x06, 0x77,
+    0x6f, 0x72, 0x6c, 0x64, 0x21, 0x04, 0x14, 0x11,
+    0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33,
+    0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x55, 0x00,
+    0x44, 0x00, 0x33, 0x00, 0x22, 0x00, 0x11, 0x00,
+    0x00, 0x16, 0x03, 0x62, 0x61, 0x72, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_nested_struct_exp_def[] = {
+    0x30, 0x70, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x01,
+    0x30, 0x30, 0x42, 0x02, 0x03, 0xe8, 0x01, 0x01,
+    0x00, 0x16, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+    0x04, 0x14, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00,
+    0x22, 0x22, 0x00, 0x00, 0x33, 0x33, 0x00, 0x00,
+    0x44, 0x44, 0x00, 0x00, 0x55, 0x55, 0x04, 0x0a,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x02,
+    0x00, 0x01, 0x30, 0x31, 0x42, 0x02, 0x07, 0xd0,
+    0x01, 0x01, 0x00, 0x16, 0x06, 0x77, 0x6f, 0x72,
+    0x6c, 0x64, 0x21, 0x04, 0x14, 0x11, 0x11, 0x11,
+    0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33,
+    0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x55, 0x00, 0x44, 0x00,
+    0x33, 0x00, 0x22, 0x00, 0x11, 0x16, 0x03, 0x62,
+    0x61, 0x72,
+};
+
+static void test_visitor_out_nested_struct(TestInputOutputVisitor *data,
+                                           const void *unused)
+{
+    uint16_t extdata1[] = { 5, 4, 3, 2, 1};
+    uint16_t extdata2[] = { 0x55, 0x44, 0x33, 0x22, 0x11};
+    NestedStruct nested_struct = {
+        .integer = 42,
+        .boolean = true,
+        .simpleStruct = {
+            {
+                .integer = 1000,
+                .boolean = false,
+                .string = (char *)"Hello",
+                .data = {
+                    0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+                },
+                .extdata = extdata1,
+            }, {
+                .integer = 2000,
+                .boolean = false,
+                .string = (char *)"world!",
+                .data = {
+                    0x11111111, 0x22222222, 0x33333333, 0x44444444,
+                    0x55555555
+                },
+                .extdata = extdata2,
+            },
+        },
+        .string = (char *)"bar",
+    };
+    NestedStruct *p_in = &nested_struct, *p_out = NULL;
+    Error *errp = NULL;
+    unsigned int i;
+
+    visit_type_NestedStruct(data->ov, &p_in, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_NestedStruct(data->iv, &p_out, NULL, &errp);
+
+    g_assert_cmpint(p_out->integer, ==, 42);
+    g_assert_cmpint(p_out->boolean, ==, true);
+
+    g_assert_cmpint(p_out->simpleStruct[0].integer, ==, 1000);
+    g_assert_cmpint(p_out->simpleStruct[0].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[0].string , ==, "Hello");
+    for (i = 0; i < ARRAY_SIZE(nested_struct.simpleStruct[0].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].data[i], ==, (i + 1) * 0x1111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata1); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].extdata[i], ==, (5 - i));
+    }
+
+    g_assert_cmpint(p_out->simpleStruct[1].integer, ==, 2000);
+    g_assert_cmpint(p_out->simpleStruct[1].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[1].string , ==, "world!");
+    for (i = 0; i < ARRAY_SIZE(nested_struct.simpleStruct[1].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].data[i], ==,
+                        (i + 1) * 0x11111111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata2); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].extdata[i], ==, (5 - i) * 0x11);
+    }
+
+    g_assert_cmpstr(p_out->string, ==, "bar");
+
+    g_free(p_out->string);
+    for (i = 0; i < ARRAY_SIZE(p_out->simpleStruct); i++) {
+        g_free(p_out->simpleStruct[i].string);
+        g_free(p_out->simpleStruct[i].extdata);
+    }
+    g_free(p_out);
+}
+
+typedef struct Optional {
+    int64_t integer;
+    bool has_array;
+    SimpleStruct simpleStruct[2];
+    char *string;
+} Optional;
+
+static void visit_type_Optional(Visitor *v, Optional **obj,
+                                const char *name, Error **errp)
+{
+    unsigned int i;
+
+    visit_start_struct(v, (void **)obj, "Optional", name,
+                       sizeof(Optional), errp);
+
+    visit_type_int(v, &(*obj)->integer, "integer", errp);
+
+    visit_start_optional(v, &(*obj)->has_array, "has_array", errp);
+
+    if ((*obj)->has_array) {
+        for (i = 0; i < ARRAY_SIZE((*obj)->simpleStruct); i++) {
+            SimpleStruct *simple = &(*obj)->simpleStruct[i];
+            visit_type_SimpleStruct(v, &simple, "SimpleStruct", errp);
+        }
+    }
+
+    visit_end_optional(v, errp);
+
+    visit_type_str(v, &(*obj)->string, "string", errp);
+
+    visit_end_struct(v, errp);
+}
+
+static const uint8_t test_visitor_optional_exp_indef[] = {
+    0x30, 0x80, 0x42, 0x01, 0x2a, 0x3f, 0x21, 0x80,
+    0x01, 0x01, 0x01, 0x30, 0x80, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x48, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x30,
+    0x80, 0x42, 0x02, 0x07, 0xd0, 0x01, 0x01, 0x00,
+    0x16, 0x06, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
+    0x04, 0x14, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22,
+    0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44,
+    0x44, 0x44, 0x55, 0x55, 0x55, 0x55, 0x04, 0x0a,
+    0x00, 0x55, 0x00, 0x44, 0x00, 0x33, 0x00, 0x22,
+    0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03,
+    0x62, 0x61, 0x72, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_optional_exp_def[] = {
+    0x30, 0x73, 0x42, 0x01, 0x2a, 0x3f, 0x21, 0x68,
+    0x01, 0x01, 0x01, 0x30, 0x30, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x48, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x30, 0x31, 0x42,
+    0x02, 0x07, 0xd0, 0x01, 0x01, 0x00, 0x16, 0x06,
+    0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x04, 0x14,
+    0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22,
+    0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44,
+    0x55, 0x55, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x55,
+    0x00, 0x44, 0x00, 0x33, 0x00, 0x22, 0x00, 0x11,
+    0x16, 0x03, 0x62, 0x61, 0x72,
+};
+
+static void test_visitor_out_optional(TestInputOutputVisitor *data,
+                                      const void *unused)
+{
+    uint16_t extdata1[] = { 5, 4, 3, 2, 1};
+    uint16_t extdata2[] = { 0x55, 0x44, 0x33, 0x22, 0x11};
+    Optional optional_struct = {
+        .integer = 42,
+        .has_array = true,
+        .simpleStruct = {
+            {
+                .integer = 1000,
+                .boolean = false,
+                .string = (char *)"Hello",
+                .data = {
+                    0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+                },
+                .extdata = extdata1,
+            }, {
+                .integer = 2000,
+                .boolean = false,
+                .string = (char *)"world!",
+                .data = {
+                    0x11111111, 0x22222222, 0x33333333, 0x44444444,
+                    0x55555555
+                },
+                .extdata = extdata2,
+            },
+        },
+        .string = (char *)"bar",
+    };
+    Optional *p_in = &optional_struct, *p_out = NULL;
+    Error *errp = NULL;
+    unsigned int i;
+
+    visit_type_Optional(data->ov, &p_in, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_Optional(data->iv, &p_out, NULL, &errp);
+
+    g_assert_cmpint(p_out->integer, ==, 42);
+    g_assert_cmpint(p_out->has_array, ==, true);
+
+    g_assert_cmpint(p_out->simpleStruct[0].integer, ==, 1000);
+    g_assert_cmpint(p_out->simpleStruct[0].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[0].string , ==, "Hello");
+    for (i = 0; i < ARRAY_SIZE(optional_struct.simpleStruct[0].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].data[i], ==, (i + 1) * 0x1111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata1); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].extdata[i], ==, (5 - i));
+    }
+
+    g_assert_cmpint(p_out->simpleStruct[1].integer, ==, 2000);
+    g_assert_cmpint(p_out->simpleStruct[1].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[1].string , ==, "world!");
+    for (i = 0; i < ARRAY_SIZE(optional_struct.simpleStruct[1].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].data[i], ==,
+                        (i + 1) * 0x11111111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata2); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].extdata[i], ==, (5 - i) * 0x11);
+    }
+
+    g_assert_cmpstr(p_out->string, ==, "bar");
+
+    g_free(p_out->string);
+    for (i = 0; i < ARRAY_SIZE(p_out->simpleStruct); i++) {
+        g_free(p_out->simpleStruct[i].string);
+        g_free(p_out->simpleStruct[i].extdata);
+    }
+    g_free(p_out);
+}
+
+typedef struct SimpleStringList {
+    char *value;
+    struct SimpleStringList *next;
+} SimpleStringList;
+
+static void visit_type_SimpleStringList(Visitor *m, SimpleStringList ** obj,
+                                  const char *name, Error **errp)
+{
+    GenericList *i, **head = (GenericList **)obj;
+
+    visit_start_list(m, name, errp);
+
+    for (*head = i = visit_next_list(m, head, errp);
+         i;
+         i = visit_next_list(m, &i, errp)) {
+        SimpleStringList *native_i = (SimpleStringList *)i;
+        visit_type_str(m, &native_i->value, NULL, errp);
+    }
+
+    visit_end_list(m, errp);
+}
+
+static const uint8_t test_visitor_list_exp_indef[] = {
+    0x3f, 0x20, 0x80, 0x16, 0x05, 0x68, 0x65, 0x6c,
+    0x6c, 0x6f, 0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c,
+    0x64, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_list_exp_def[] = {
+    0x3f, 0x20, 0x0e, 0x16, 0x05, 0x68, 0x65, 0x6c,
+    0x6c, 0x6f, 0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c,
+    0x64,
+};
+
+static void test_visitor_out_list(TestInputOutputVisitor *data,
+                                  const void *unused)
+{
+    SimpleStringList test_list = {
+        .value = (char *)"hello",
+        .next = &(SimpleStringList) {
+            .value = (char *)"world",
+            .next = NULL,
+        },
+    };
+    SimpleStringList *p_in = &test_list, *p_out = NULL, *iter = NULL, *_iter;
+    Error *errp = NULL;
+
+    visit_type_SimpleStringList(data->ov, &p_in, "SimpleStringList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStringList(data->iv, &p_out, "SimpleStringList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    iter = p_out;
+    g_assert_cmpstr(iter->value, ==, "hello");
+    iter = iter->next;
+    g_assert_cmpstr(iter->value, ==, "world");
+
+    for (iter = p_out; iter; iter = _iter) {
+        _iter = iter->next;
+        g_free(iter->value);
+        g_free(iter);
+    }
+}
+
+typedef struct SimpleStructList {
+    SimpleStruct *value;
+    struct SimpleStructList *next;
+} SimpleStructList;
+
+static void visit_type_SimpleStructList(Visitor *m, SimpleStructList ** obj,
+                                        const char *name, Error **errp)
+{
+    GenericList *i, **head = (GenericList **)obj;
+
+    visit_start_list(m, name, errp);
+
+    for (*head = i = visit_next_list(m, head, errp);
+         i;
+         i = visit_next_list(m, &i, errp)) {
+        SimpleStructList *native_i = (SimpleStructList *)i;
+        visit_type_SimpleStruct(m, &native_i->value, NULL, errp);
+    }
+
+    visit_end_list(m, errp);
+}
+
+static const uint8_t test_visitor_simplestruct_list_exp_indef[] = {
+    0x3f, 0x20, 0x80, 0x30, 0x80, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x68, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x30,
+    0x80, 0x42, 0x02, 0x07, 0xd0, 0x01, 0x01, 0x01,
+    0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x04,
+    0x14, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22,
+    0x22, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44,
+    0x44, 0x55, 0x55, 0x55, 0x55, 0x04, 0x0a, 0x00,
+    0x55, 0x00, 0x44, 0x00, 0x33, 0x00, 0x22, 0x00,
+    0x11, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_simplestruct_list_exp_def[] = {
+    0x3f, 0x20, 0x64, 0x30, 0x30, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x68, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x30, 0x30, 0x42,
+    0x02, 0x07, 0xd0, 0x01, 0x01, 0x01, 0x16, 0x05,
+    0x77, 0x6f, 0x72, 0x6c, 0x64, 0x04, 0x14, 0x11,
+    0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33,
+    0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x55, 0x00,
+    0x44, 0x00, 0x33, 0x00, 0x22, 0x00, 0x11,
+};
+
+static void test_visitor_out_simplestruct_list(TestInputOutputVisitor *data,
+                                               const void *unused)
+{
+    uint16_t extdata1[] = { 5, 4, 3, 2, 1};
+    uint16_t extdata2[] = { 0x55, 0x44, 0x33, 0x22, 0x11};
+    SimpleStructList test_list = {
+        .value = &(SimpleStruct) {
+            .string = (char *)"hello",
+            .integer = 1000,
+            .boolean = false,
+            .data = {
+                0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+            },
+            .extdata = extdata1,
+        },
+        .next = &(SimpleStructList) {
+            .value = &(SimpleStruct) {
+                .string = (char *)"world",
+                .integer = 2000,
+                .boolean = true,
+                .data = {
+                    0x11111111, 0x22222222, 0x33333333, 0x44444444,
+                    0x55555555
+                },
+                .extdata = extdata2,
+            },
+            .next = NULL,
+        },
+    };
+    SimpleStructList *p_in = &test_list, *p_out = NULL, *iter = NULL, *_iter;
+    Error *errp = NULL;
+    size_t i;
+
+    visit_type_SimpleStructList(data->ov, &p_in, "SimpleStructList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStructList(data->iv, &p_out, "SimpleStructList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    iter = p_out;
+    g_assert_cmpstr(iter->value->string, ==, "hello");
+    for (i = 0; i < ARRAY_SIZE(test_list.value->data); i++) {
+        g_assert_cmpint(iter->value->data[i], ==, (i + 1) * 0x1111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata1); i++) {
+        g_assert_cmpint(iter->value->extdata[i], ==, (5 - i));
+    }
+
+    iter = iter->next;
+    g_assert_cmpstr(iter->value->string, ==, "world");
+    for (i = 0; i < ARRAY_SIZE(test_list.next->value->data); i++) {
+        g_assert_cmpint(iter->value->data[i], ==, (i + 1) * 0x11111111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata2); i++) {
+        g_assert_cmpint(iter->value->extdata[i], ==, (5 - i) * 0x11);
+    }
+
+    for (iter = p_out; iter; iter = _iter) {
+        _iter = iter->next;
+        g_free(iter->value->string);
+        g_free(iter->value->extdata);
+        g_free(iter->value);
+        g_free(iter);
+    }
+}
+
+static void test_visitor_test_add(const char *testpath,
+                         BERLengthEncoding ber_length_encoding,
+                         const TestExpResult *ter,
+                         void (*test_func)(TestInputOutputVisitor *data,
+                                           const void *user_data))
+{
+    char path[64];
+    void (*setup)(TestInputOutputVisitor *data, const void *unused);
+
+    switch (ber_length_encoding) {
+    case BER_LENGTH_ENCODING_DEFINITE:
+        snprintf(path, sizeof(path), "/definite%s", testpath);
+        setup = visitor_output_setup_definite;
+        break;
+    default:
+        snprintf(path, sizeof(path), "/indefinite%s", testpath);
+        setup = visitor_output_setup_indefinite;
+        break;
+    }
+
+    g_test_add(path, TestInputOutputVisitor, ter, setup,
+               test_func, visitor_output_teardown);
+}
+
+struct test_data {
+    const char *path;
+    void (*test_func)(TestInputOutputVisitor *, const void *);
+    TestExpResult ter[2];
+};
+
+#define _TEST_CASE(FUNC, POST1, POST2, PATH) \
+    {\
+        .path = PATH,\
+        .test_func = test_visitor_out_## FUNC,\
+        .ter = {\
+            {\
+                .exp = test_visitor_## FUNC ## _exp ## POST1,\
+                .exp_len = sizeof(test_visitor_## FUNC ##_exp ## POST1),\
+            }, {\
+                .exp = test_visitor_## FUNC ##_exp ## POST2,\
+                .exp_len = sizeof(test_visitor_## FUNC ##_exp ## POST2),\
+            } \
+        } \
+    }
+
+#define TEST_CASE(PATH, FUNC) \
+    _TEST_CASE(FUNC, _indef, _def, PATH)
+
+#define TEST_CASE_SIMPLE(PATH, FUNC) \
+    _TEST_CASE(FUNC, , , PATH)
+
+static const struct test_data tests[] = {
+    TEST_CASE_SIMPLE("/visitor/output/int", int),
+    TEST_CASE_SIMPLE("/visitor/output/bool", boolean),
+    TEST_CASE_SIMPLE("/visitor/output/string", string),
+    TEST_CASE_SIMPLE("/visitor/output/no-string", no_string),
+    TEST_CASE("/visitor/output/struct", struct),
+    TEST_CASE("/visitor/output/optional", optional),
+    TEST_CASE("/visitor/output/nested-struct", nested_struct),
+    TEST_CASE("/visitor/output/optional", optional),
+    TEST_CASE("/visitor/output/list", list),
+    TEST_CASE("/visitor/output/simplestruct_list", simplestruct_list),
+};
+
+int main(int argc, char **argv)
+{
+    BERLengthEncoding length_encoding[] =
+        {BER_LENGTH_ENCODING_INDEFINITE, BER_LENGTH_ENCODING_DEFINITE};
+    int i;
+    int j;
+
+    g_test_init(&argc, &argv, NULL);
+
+    for (i = 0; i < ARRAY_SIZE(length_encoding); i++) {
+        for (j = 0; j < ARRAY_SIZE(tests); j++) {
+            test_visitor_test_add(tests[j].path, length_encoding[i],
+                                  &tests[j].ter[i],
+                                  tests[j].test_func);
+        }
+    }
+
+    g_test_run();
+
+    return 0;
+}
-- 
1.7.11.7

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors
  2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
                   ` (7 preceding siblings ...)
  2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 8/8] ASN.1 specific test cases Stefan Berger
@ 2013-03-28 11:43 ` Stefan Berger
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Berger @ 2013-03-28 11:43 UTC (permalink / raw)
  To: qemu-devel

On 03/28/2013 07:38 AM, Stefan Berger wrote:
> This patch series implements ASN.1 BER visitors for encoding and decoding
> of data into byte streams.
>
> This is the last post of this series with some issues fixed, the visitor_optional
> implemented and all possible tests in test-visitor-serialization passing.

The series applies to f7d42093a5.

I won't need it anymore for what I had planned to use it for.

    Stefan

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2013-03-28 11:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-28 11:38 [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 1/8] Move some contents of savevm.c to qemu-file.c Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 2/8] 3 new file wrappers Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 3/8] QEMUSizedBuffer Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 4/8] QAPI: add type_sized_buffer Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 5/8] ASN.1 output visitor Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 6/8] ASN.1 input visitor Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 7/8] Extend test-visitor-serialization with ASN.1 visitor(s) Stefan Berger
2013-03-28 11:38 ` [Qemu-devel] [PATCH v5 8/8] ASN.1 specific test cases Stefan Berger
2013-03-28 11:43 ` [Qemu-devel] [PATCH v5 0/8] Implement and test ASN.1 BER visitors Stefan Berger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).