/* -*- 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 . */ #ifdef HAVE_CONFIG_H #include #endif #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((uint64_t)(v))) #define write_uint64(ptr,v) (*((uint64_t *)(ptr)) = SPICE_BYTESWAP64((uint64_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, *next; SpiceMarshallerData *d; /* Only supported for root marshaller */ assert(m->data->marshallers == m); for (m2 = m; m2 != NULL; m2 = next) { next = 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); } void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks) { unsigned int i; for (i = 0; i < chunks->num_chunks; i++) { spice_marshaller_add_ref(m, chunks->chunk[i].data, chunks->chunk[i].len); } } 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)); } } } } #ifndef WIN32 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; } #endif 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); return (void *)ptr; } 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); return (void *)ptr; } 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); return (void *)ptr; } void spice_marshaller_set_uint32(SpiceMarshaller *m, void *ref, uint32_t v) { write_uint32((uint8_t *)ref, 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); return (void *)ptr; } 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); return (void *)ptr; } 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); return (void *)ptr; } 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); return (void *)ptr; } 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); return (void *)ptr; }