From 0cca1ed2af3f74ec6519382c2d52dbfaa9762700 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 14 Jun 2010 16:11:04 +0200 Subject: Add SpiceMarshaller for easy marshalling --- common/Makefile.am | 2 + common/marshaller.c | 588 ++++++++++++++++++++++++++++++++++++++++++++++++++++ common/marshaller.h | 58 ++++++ server/Makefile.am | 1 + 4 files changed, 649 insertions(+) create mode 100644 common/marshaller.c create mode 100644 common/marshaller.h diff --git a/common/Makefile.am b/common/Makefile.am index 222965e8..5c1d6e07 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -39,6 +39,8 @@ COMMON_SRCS = \ lz_config.h \ lz_decompress_tmpl.c \ lz.h \ + marshaller.h \ + marshaller.c \ quic_family_tmpl.c \ quic_rgb_tmpl.c \ quic_tmpl.c \ diff --git a/common/marshaller.c b/common/marshaller.c new file mode 100644 index 00000000..13385e59 --- /dev/null +++ b/common/marshaller.c @@ -0,0 +1,588 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2010 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include "config.h" + +#include "marshaller.h" +#include "mem.h" +#include +#include +#include + +#ifdef WORDS_BIGENDIAN +#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v) +#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v) +#define write_int16(ptr,v) (*((int16_t)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v))) +#define write_uint16(ptr,v) (*((uint16_t)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v))) +#define write_int32(ptr,v) (*((int32_t)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v))) +#define write_uint32(ptr,v) (*((uint32_t)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v))) +#define write_int64(ptr,v) (*((int64_t)(ptr)) = SPICE_BYTESWAP64((uint63_t)(v))) +#define write_uint64(ptr,v) (*((uint64_t)(ptr)) = SPICE_BYTESWAP64((uint63_t)(v))) +#else +#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v) +#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v) +#define write_int16(ptr,v) (*((int16_t *)(ptr)) = v) +#define write_uint16(ptr,v) (*((uint16_t *)(ptr)) = v) +#define write_int32(ptr,v) (*((int32_t *)(ptr)) = v) +#define write_uint32(ptr,v) (*((uint32_t *)(ptr)) = v) +#define write_int64(ptr,v) (*((int64_t *)(ptr)) = v) +#define write_uint64(ptr,v) (*((uint64_t *)(ptr)) = v) +#endif + +typedef struct { + uint8_t *data; + size_t len; + spice_marshaller_item_free_func free_data; + void *opaque; +} MarshallerItem; + +/* Try to fit in 4k page with 2*pointer-size overhead (next ptr and malloc size) */ +#define MARSHALLER_BUFFER_SIZE (4096 - sizeof(void *) * 2) + +typedef struct MarshallerBuffer MarshallerBuffer; +struct MarshallerBuffer { + MarshallerBuffer *next; + uint8_t data[MARSHALLER_BUFFER_SIZE]; +}; + +#define N_STATIC_ITEMS 4 + +typedef struct SpiceMarshallerData SpiceMarshallerData; + +typedef struct { + SpiceMarshaller *marshaller; + int item_nr; + int is_64bit; + size_t offset; +} MarshallerRef; + +struct SpiceMarshaller { + size_t total_size; + SpiceMarshallerData *data; + SpiceMarshaller *next; + + MarshallerRef pointer_ref; + + int n_items; + int items_size; /* number of items availible in items */ + MarshallerItem *items; + + MarshallerItem static_items[N_STATIC_ITEMS]; +}; + +struct SpiceMarshallerData { + size_t total_size; + size_t base; + SpiceMarshaller *marshallers; + SpiceMarshaller *last_marshaller; + + size_t current_buffer_position; + MarshallerBuffer *current_buffer; + MarshallerItem *current_buffer_item; + MarshallerBuffer *buffers; + + SpiceMarshaller static_marshaller; + MarshallerBuffer static_buffer; +}; + +static void spice_marshaller_init(SpiceMarshaller *m, + SpiceMarshallerData *data) +{ + m->data = data; + m->next = NULL; + m->total_size = 0; + m->pointer_ref.marshaller = NULL; + m->n_items = 0; + m->items_size = N_STATIC_ITEMS; + m->items = m->static_items; +} + +SpiceMarshaller *spice_marshaller_new(void) +{ + SpiceMarshallerData *d; + SpiceMarshaller *m; + + d = spice_new(SpiceMarshallerData, 1); + + d->last_marshaller = d->marshallers = &d->static_marshaller; + d->total_size = 0; + d->base = 0; + d->buffers = &d->static_buffer; + d->buffers->next = NULL; + d->current_buffer = d->buffers; + d->current_buffer_position = 0; + d->current_buffer_item = NULL; + + m = &d->static_marshaller; + spice_marshaller_init(m, d); + + return m; +} + +static void free_item_data(SpiceMarshaller *m) +{ + MarshallerItem *item; + int i; + + /* Free all user data */ + for (i = 0; i < m->n_items; i++) { + item = &m->items[i]; + if (item->free_data != NULL) { + item->free_data(item->data, item->opaque); + } + } +} + +static void free_items(SpiceMarshaller *m) +{ + if (m->items != m->static_items) { + free(m->items); + } +} + +void spice_marshaller_reset(SpiceMarshaller *m) +{ + SpiceMarshaller *m2; + SpiceMarshallerData *d; + + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + for (m2 = m; m2 != NULL; m2 = m2->next) { + free_item_data(m2); + + /* Free non-root marshallers */ + if (m2 != m) { + free_items(m2); + free(m2); + } + } + + m->next = NULL; + m->n_items = 0; + m->total_size = 0; + + d = m->data; + d->last_marshaller = d->marshallers; + d->total_size = 0; + d->base = 0; + d->current_buffer_item = NULL; + d->current_buffer = d->buffers; + d->current_buffer_position = 0; +} + +void spice_marshaller_destroy(SpiceMarshaller *m) +{ + MarshallerBuffer *buf, *next; + SpiceMarshallerData *d; + + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + spice_marshaller_reset(m); + + free_items(m); + + d = m->data; + + buf = d->buffers->next; + while (buf != NULL) { + next = buf->next; + free(buf); + buf = next; + } + + free(d); +} + +static MarshallerItem *spice_marshaller_add_item(SpiceMarshaller *m) +{ + MarshallerItem *item; + + if (m->n_items == m->items_size) { + int items_size = m->items_size * 2; + + if (m->items == m->static_items) { + m->items = spice_new(MarshallerItem, items_size); + memcpy(m->items, m->static_items, sizeof(MarshallerItem) * m->n_items); + } else { + m->items = spice_renew(MarshallerItem, m->items, items_size); + } + m->items_size = items_size; + } + item = &m->items[m->n_items++]; + item->free_data = NULL; + + return item; +} + +static size_t remaining_buffer_size(SpiceMarshallerData *d) +{ + return MARSHALLER_BUFFER_SIZE - d->current_buffer_position; +} + +uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size) +{ + MarshallerItem *item; + SpiceMarshallerData *d; + uint8_t *res; + + if (size == 0) { + return NULL; + } + + d = m->data; + + /* Check current item */ + item = &m->items[m->n_items - 1]; + if (item == d->current_buffer_item && + remaining_buffer_size(d) >= size) { + assert(m->n_items >= 1); + /* We can piggy back on existing item+buffer */ + res = item->data + item->len; + item->len += size; + d->current_buffer_position += size; + d->total_size += size; + m->total_size += size; + return res; + } + + item = spice_marshaller_add_item(m); + + if (remaining_buffer_size(d) >= size) { + /* Fits in current buffer */ + item->data = d->current_buffer->data + d->current_buffer_position; + item->len = size; + d->current_buffer_position += size; + d->current_buffer_item = item; + } else if (size > MARSHALLER_BUFFER_SIZE / 2) { + /* Large item, allocate by itself */ + item->data = (uint8_t *)spice_malloc(size); + item->len = size; + item->free_data = (spice_marshaller_item_free_func)free; + item->opaque = NULL; + } else { + /* Use next buffer */ + if (d->current_buffer->next == NULL) { + d->current_buffer->next = spice_new(MarshallerBuffer, 1); + d->current_buffer->next->next = NULL; + } + d->current_buffer = d->current_buffer->next; + d->current_buffer_position = size; + d->current_buffer_item = item; + item->data = d->current_buffer->data; + item->len = size; + } + + d->total_size += size; + m->total_size += size; + return item->data; +} + +void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size) +{ + MarshallerItem *item; + + if (size == 0) { + return; + } + + item = &m->items[m->n_items - 1]; + + assert(item->len >= size); + item->len -= size; +} + +uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size, + spice_marshaller_item_free_func free_data, void *opaque) +{ + MarshallerItem *item; + SpiceMarshallerData *d; + + if (data == NULL || size == 0) { + return NULL; + } + + item = spice_marshaller_add_item(m); + item->data = data; + item->len = size; + item->free_data = free_data; + item->opaque = opaque; + + d = m->data; + m->total_size += size; + d->total_size += size; + + return data; +} + +uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, size); + memcpy(ptr, data, size); + return ptr; +} + +uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size) +{ + return spice_marshaller_add_ref_full(m, data, size, NULL, NULL); +} + +SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m) +{ + SpiceMarshallerData *d; + SpiceMarshaller *m2; + + d = m->data; + + m2 = spice_new(SpiceMarshaller, 1); + spice_marshaller_init(m2, d); + + d->last_marshaller->next = m2; + d->last_marshaller = m2; + + return m2; +} + +SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit) +{ + SpiceMarshaller *m2; + uint8_t *p; + int size; + + size = is_64bit ? 8 : 4; + + p = spice_marshaller_reserve_space(m, size); + memset(p, 0, size); + m2 = spice_marshaller_get_submarshaller(m); + m2->pointer_ref.marshaller = m; + m2->pointer_ref.item_nr = m->n_items - 1; + m2->pointer_ref.offset = m->items[m->n_items - 1].len - size; + m2->pointer_ref.is_64bit = is_64bit; + + return m2; +} + +uint8_t *lookup_ref(MarshallerRef *ref) +{ + MarshallerItem *item; + + item = &ref->marshaller->items[ref->item_nr]; + return item->data + ref->offset; +} + + +void spice_marshaller_set_base(SpiceMarshaller *m, size_t base) +{ + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + m->data->base = base; +} + +uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip_bytes, + size_t *len, int *free_res) +{ + MarshallerItem *item; + uint8_t *res, *p; + int i; + + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + if (m->n_items == 1) { + *free_res = FALSE; + if (m->items[0].len <= skip_bytes) { + *len = 0; + return NULL; + } + *len = m->items[0].len - skip_bytes; + return m->items[0].data + skip_bytes; + } + + *free_res = TRUE; + res = (uint8_t *)spice_malloc(m->data->total_size - skip_bytes); + *len = m->data->total_size - skip_bytes; + p = res; + + do { + for (i = 0; i < m->n_items; i++) { + item = &m->items[i]; + + if (item->len <= skip_bytes) { + skip_bytes -= item->len; + continue; + } + memcpy(p, item->data + skip_bytes, item->len - skip_bytes); + p += item->len - skip_bytes; + skip_bytes = 0; + } + m = m->next; + } while (m != NULL); + + return res; +} + +uint8_t *spice_marshaller_get_ptr(SpiceMarshaller *m) +{ + return m->items[0].data; +} + +size_t spice_marshaller_get_offset(SpiceMarshaller *m) +{ + SpiceMarshaller *m2; + size_t offset; + + offset = 0; + m2 = m->data->marshallers; + while (m2 != m) { + offset += m2->total_size; + m2 = m2->next; + } + return offset - m->data->base; +} + +size_t spice_marshaller_get_size(SpiceMarshaller *m) +{ + return m->total_size; +} + +size_t spice_marshaller_get_total_size(SpiceMarshaller *m) +{ + return m->data->total_size; +} + +void spice_marshaller_flush(SpiceMarshaller *m) +{ + SpiceMarshaller *m2; + uint8_t *ptr_pos; + + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + for (m2 = m; m2 != NULL; m2 = m2->next) { + if (m2->pointer_ref.marshaller != NULL && m2->total_size > 0) { + ptr_pos = lookup_ref(&m2->pointer_ref); + if (m2->pointer_ref.is_64bit) { + write_uint64(ptr_pos, + spice_marshaller_get_offset(m2)); + } else { + write_uint32(ptr_pos, + spice_marshaller_get_offset(m2)); + } + } + } +} + +int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec, + int n_vec, size_t skip_bytes) +{ + MarshallerItem *item; + int v, i; + + /* Only supported for root marshaller */ + assert(m->data->marshallers == m); + + v = 0; + do { + for (i = 0; i < m->n_items; i++) { + item = &m->items[i]; + + if (item->len <= skip_bytes) { + skip_bytes -= item->len; + continue; + } + if (v == n_vec) { + return v; /* Not enough space in vec */ + } + vec[v].iov_base = item->data + skip_bytes; + vec[v].iov_len = item->len - skip_bytes; + skip_bytes = 0; + v++; + } + m = m->next; + } while (m != NULL); + + return v; +} + +void spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(uint64_t)); + write_uint64(ptr, v); +} + +void spice_marshaller_add_int64(SpiceMarshaller *m, int64_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(int64_t)); + write_int64(ptr, v); +} + +void spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(uint32_t)); + write_uint32(ptr, v); +} + +void spice_marshaller_add_int32(SpiceMarshaller *m, int32_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(int32_t)); + write_int32(ptr, v); +} + +void spice_marshaller_add_uint16(SpiceMarshaller *m, uint16_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(uint16_t)); + write_uint16(ptr, v); +} + +void spice_marshaller_add_int16(SpiceMarshaller *m, int16_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(int16_t)); + write_int16(ptr, v); +} + +void spice_marshaller_add_uint8(SpiceMarshaller *m, uint8_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(uint8_t)); + write_uint8(ptr, v); +} + +void spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v) +{ + uint8_t *ptr; + + ptr = spice_marshaller_reserve_space(m, sizeof(int8_t)); + write_int8(ptr, v); +} diff --git a/common/marshaller.h b/common/marshaller.h new file mode 100644 index 00000000..3b159aa9 --- /dev/null +++ b/common/marshaller.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2010 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#ifndef _H_MARSHALLER +#define _H_MARSHALLER + +#include +#include + +typedef struct SpiceMarshaller SpiceMarshaller; +typedef void (*spice_marshaller_item_free_func)(uint8_t *data, void *opaque); + +SpiceMarshaller *spice_marshaller_new(void); +void spice_marshaller_reset(SpiceMarshaller *m); +void spice_marshaller_destroy(SpiceMarshaller *m); +uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size); +void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size); +uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size); +uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size); +uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size, + spice_marshaller_item_free_func free_data, void *opaque); +void spice_marshaller_flush(SpiceMarshaller *m); +void spice_marshaller_set_base(SpiceMarshaller *m, size_t base); +uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip, + size_t *len, int *free_res); +uint8_t *spice_marshaller_get_ptr(SpiceMarshaller *m); +size_t spice_marshaller_get_offset(SpiceMarshaller *m); +size_t spice_marshaller_get_size(SpiceMarshaller *m); +size_t spice_marshaller_get_total_size(SpiceMarshaller *m); +SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m); +SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit); +int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec, + int n_vec, size_t skip_bytes); +void spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v); +void spice_marshaller_add_int64(SpiceMarshaller *m, int64_t v); +void spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v); +void spice_marshaller_add_int32(SpiceMarshaller *m, int32_t v); +void spice_marshaller_add_uint16(SpiceMarshaller *m, uint16_t v); +void spice_marshaller_add_int16(SpiceMarshaller *m, int16_t v); +void spice_marshaller_add_uint8(SpiceMarshaller *m, uint8_t v); +void spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v); + +#endif diff --git a/server/Makefile.am b/server/Makefile.am index 8d936183..ff97a40a 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -31,6 +31,7 @@ COMMON_SRCS = \ $(top_srcdir)/common/lz.c \ $(top_srcdir)/common/canvas_utils.c \ $(top_srcdir)/common/mem.c \ + $(top_srcdir)/common/marshaller.c \ $(NULL) lib_LTLIBRARIES = libspice-server.la -- cgit