/* gserialize.h * Copyright (C) 1998 Jay Cox * * 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 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 * Library 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gserialize.h" struct _GSerialDescription { char *struct_name; GSList *list; }; struct _GSerialItem { GSerialType type; /* the type of this data member */ gulong offset; /* the offset into the struct of this item */ glong length; /* the number of elements (if this is an array) */ /* or -1 if this is a variable length array */ gulong length_offset; /* offset to the length of the array */ }; #define g_serial_copy_from_n g_serial_copy_to_n static long g_serial_copy_to_n(char *dest, char *source, long data_size, long n_items) { int i; int length = n_items*data_size; #if G_BYTE_ORDER == G_BIG_ENDIAN memcpy(dest, source, length); #else switch (data_size) { case 1: memcpy(dest, source, n_items); break; case 2: for (i = 0; i < length; i+=2) { dest[i] = source[i + 1]; dest[i+1] = source[i]; } break; case 4: for (i = 0; i < length; i+=4) { dest[i] = source[i + 3]; dest[i+1] = source[i + 2]; dest[i+2] = source[i + 1]; dest[i+3] = source[i]; } break; case 8: for (i = 0; i < length; i+=8) { dest[i] = source[i+7]; dest[i+1] = source[i+6]; dest[i+2] = source[i+5]; dest[i+3] = source[i+4]; dest[i+4] = source[i+3]; dest[i+5] = source[i+2]; dest[i+6] = source[i+1]; dest[i+7] = source[i]; } break; default: g_assert_not_reached(); } #endif /* G_BYTE_ORDER != G_BIG_ENDIAN */ return length; } GSerialItem *g_new_serial_item(GSerialType type, gulong offset, gint32 length, gulong length_offset) { GSerialItem *item = g_new(GSerialItem, 1); item->type = type; item->offset = offset; item->length = length; item->length_offset = length_offset; return item; } GSerialDescription *g_new_serial_description(char *name, ...) { va_list argp; void *tmp; GSerialDescription *d = g_new(GSerialDescription, 1); d->struct_name = g_strdup(name); d->list = 0; va_start(argp, name); while ((tmp = va_arg (argp, void*))) d->list = g_slist_append(d->list, tmp); va_end(argp); return d; } void g_free_serial_description(GSerialDescription *d) { g_free (d->struct_name); while (d->list) { g_free(d->list->data); d->list = g_slist_remove_link(d->list, d->list); } g_free (d); } static int g_serial_item_is_array(GSerialItem *item) { if (item->type < GSERIAL_STRING) return 0; return 1; } static long g_serial_item_data_size(GSerialItem *item) { static long sizes[] = {0, 1, 2, 4, 4, 8, 1, 1, 2, 4, 4, 8}; if (item->type >= GSERIAL_STRING) return sizes[item->type - 95]; return sizes[item->type]; } static long g_serial_item_n_items(GSerialItem *item, void *struct_data) { if (item->type < GSERIAL_STRING) return 1; if (item->type == GSERIAL_STRING) return (strlen(G_STRUCT_MEMBER(char*, struct_data, item->offset)) + 1); if (item->length >= 0) return item->length; return (G_STRUCT_MEMBER(gint32, struct_data, item->length_offset)); } static long g_serial_item_compute_length(GSerialItem *item, void *struct_data) { int length; length = g_serial_item_n_items(item, struct_data) * g_serial_item_data_size(item); length += g_serial_item_is_array(item) * 4; return length + 1; } static long g_serial_description_compute_length(GSerialDescription *d, void *struct_data) { long length = 0; GSList *list; list = d->list; while (list) { length += g_serial_item_compute_length((GSerialItem *) list->data, struct_data); list = list->next; } return length; } static long g_serial_item_serialize(GSerialItem *item, char *buffer, void *struct_data) { char *buf = buffer; gint32 tmp; if (item->type >= GSERIAL_LAST_TYPE || (item->type > GSERIAL_DOUBLE && item->type < GSERIAL_STRING)) { g_warning("Error serializing: Unknown serial item type.\n"); return 0; } *buf++ = item->type; if (g_serial_item_is_array(item)) { tmp = g_serial_item_n_items(item, struct_data); buf += g_serial_copy_to_n(buf, (char *)&tmp, 4, 1); buf += g_serial_copy_to_n(buf, G_STRUCT_MEMBER(char*, struct_data,item->offset), g_serial_item_data_size(item), tmp); } else { buf += g_serial_copy_to_n(buf, G_STRUCT_MEMBER_P(struct_data, item->offset), g_serial_item_data_size(item), 1); } return (buf - buffer); } static long g_serial_item_deserialize(GSerialItem *item, void *struct_data, char *buffer) { char *buf = buffer; gint32 n_items; if (*buf != item->type) { g_warning("Error deserializing: item types do not match: %d vs %d.\n", *buf, item->type); return 0; } buf++; if (g_serial_item_is_array(item)) { buf += g_serial_copy_from_n((char *)&n_items, buf, 4, 1); if (item->length < 0) G_STRUCT_MEMBER(gint32, struct_data, item->length_offset) = n_items; G_STRUCT_MEMBER(void*, struct_data, item->offset) = g_malloc(n_items*g_serial_item_data_size(item)); buf += g_serial_copy_from_n(G_STRUCT_MEMBER(void *, struct_data, item->offset), buf, g_serial_item_data_size(item), n_items); } else { buf += g_serial_copy_from_n(G_STRUCT_MEMBER_P(struct_data, item->offset), buf, g_serial_item_data_size(item), 1); } return (buf - buffer); } long g_serialize(GSerialDescription *d, void **output, void *struct_data) { int length = g_serial_description_compute_length(d, struct_data); char *outbuf; GSList *list; long item_length; outbuf = (char *)g_malloc(length); *output = outbuf; list = d->list; while (list) { item_length = g_serial_item_serialize((GSerialItem *)list->data, outbuf, struct_data); if (item_length == 0) g_error("Error serializing %s\n", d->struct_name); outbuf += item_length; list = list->next; } return length; } long g_deserialize(GSerialDescription *d, void *struct_data, void *serial) { GSList *list; char *in_buf = serial; char *out_buf = struct_data; list = d->list; while (list) { in_buf += g_serial_item_deserialize((GSerialItem *)list->data, out_buf, in_buf); list = list->next; } return (in_buf - (char *)serial); }