tenfourfox/xpcom/typelib/xpt/xpt_xdr.cpp

640 lines
18 KiB
C++

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Implementation of XDR primitives. */
#include "xpt_xdr.h"
#include "nspr.h"
#include "nscore.h"
#include <string.h> /* strchr */
#include "mozilla-config.h"
#include "plvmx.h"
static PRBool
CheckForRepeat(XPTCursor *cursor, void **addrp, XPTPool pool, uint32_t len,
XPTCursor *new_cursor, PRBool *already);
#define ENCODING(cursor) \
((cursor)->state->mode == XPT_ENCODE)
#define CURS_POOL_OFFSET_RAW(cursor) \
((cursor)->pool == XPT_HEADER \
? (cursor)->offset \
: (XPT_ASSERT((cursor)->state->data_offset), \
(cursor)->offset + (cursor)->state->data_offset))
#define CURS_POOL_OFFSET(cursor) \
(CURS_POOL_OFFSET_RAW(cursor) - 1)
/* can be used as lvalue */
#define CURS_POINT(cursor) \
((cursor)->state->pool->data[CURS_POOL_OFFSET(cursor)])
#if defined(DEBUG_shaver) || defined(DEBUG_jband) || defined(DEBUG_mccabe)
#define DBG(x) printf x
#else
#define DBG(x) (0)
#endif
/* XXX fail if XPT_DATA and !state->data_offset */
#define CHECK_COUNT_(cursor, space) \
/* if we're in the header, then exceeding the data_offset is illegal */ \
((cursor)->pool == XPT_HEADER ? \
(ENCODING(cursor) && \
((cursor)->state->data_offset && \
((cursor)->offset - 1 + (space) > (cursor)->state->data_offset)) \
? (DBG(("no space left in HEADER %d + %d > %d\n", (cursor)->offset, \
(space), (cursor)->state->data_offset)) && PR_FALSE) \
: PR_TRUE) : \
/* if we're in the data area and we're about to exceed the allocation */ \
(CURS_POOL_OFFSET(cursor) + (space) > (cursor)->state->pool->allocated ? \
/* then grow if we're in ENCODE mode */ \
(ENCODING(cursor) ? GrowPool((cursor)->state->arena, \
(cursor)->state->pool, \
(cursor)->state->pool->allocated, \
0, CURS_POOL_OFFSET(cursor) + (space)) \
/* and fail if we're in DECODE mode */ \
: (DBG(("can't extend in DECODE")) && PR_FALSE)) \
/* otherwise we're OK */ \
: PR_TRUE))
#define CHECK_COUNT(cursor, space) \
(CHECK_COUNT_(cursor, space) \
? PR_TRUE \
: (XPT_ASSERT(0), \
fprintf(stderr, "FATAL: can't no room for %d in cursor\n", space), \
PR_FALSE))
/* increase the data allocation for the pool by XPT_GROW_CHUNK */
#define XPT_GROW_CHUNK 8192
/*
* quick and dirty hardcoded hashtable, to avoid dependence on nspr or glib.
* XXXmccabe it might turn out that we could use a simpler data structure here.
*/
typedef struct XPTHashRecord {
void *key;
void *value;
struct XPTHashRecord *next;
} XPTHashRecord;
#define XPT_HASHSIZE 512
struct XPTHashTable { /* it's already typedef'ed from before. */
XPTHashRecord *buckets[XPT_HASHSIZE];
XPTArena *arena;
};
static XPTHashTable *
XPT_NewHashTable(XPTArena *arena) {
XPTHashTable *table;
table = XPT_NEWZAP(arena, XPTHashTable);
if (table)
table->arena = arena;
return table;
}
static void trimrecord(XPTArena* arena, XPTHashRecord *record) {
if (record == NULL)
return;
trimrecord(arena, record->next);
XPT_DELETE(arena, record);
}
static void
XPT_HashTableDestroy(XPTHashTable *table) {
int i;
for (i = 0; i < XPT_HASHSIZE; i++)
trimrecord(table->arena, table->buckets[i]);
XPT_FREE(table->arena, table);
}
static void *
XPT_HashTableAdd(XPTHashTable *table, void *key, void *value) {
XPTHashRecord **bucketloc = table->buckets +
(NS_PTR_TO_UINT32(key) % XPT_HASHSIZE);
XPTHashRecord *bucket;
while (*bucketloc != NULL)
bucketloc = &((*bucketloc)->next);
bucket = XPT_NEW(table->arena, XPTHashRecord);
bucket->key = key;
bucket->value = value;
bucket->next = NULL;
*bucketloc = bucket;
return value;
}
static void *
XPT_HashTableLookup(XPTHashTable *table, void *key) {
XPTHashRecord *bucket = table->buckets[NS_PTR_TO_UINT32(key) % XPT_HASHSIZE];
while (bucket != NULL) {
if (bucket->key == key)
return bucket->value;
bucket = bucket->next;
}
return NULL;
}
XPT_PUBLIC_API(XPTState *)
XPT_NewXDRState(XPTMode mode, char *data, uint32_t len)
{
XPTState *state;
XPTArena *arena;
arena = XPT_NewArena(512, sizeof(double), "an XDRState");
if (!arena)
return NULL;
state = XPT_NEWZAP(arena, XPTState);
if (!state)
goto err_free_arena;
state->arena = arena;
state->mode = mode;
state->pool = XPT_NEW(arena, XPTDatapool);
state->next_cursor[0] = state->next_cursor[1] = 1;
if (!state->pool)
goto err_free_state;
state->pool->count = 0;
state->pool->offset_map = XPT_NewHashTable(arena);
if (!state->pool->offset_map)
goto err_free_pool;
if (mode == XPT_DECODE) {
state->pool->data = data;
state->pool->allocated = len;
} else {
state->pool->data = (char*)XPT_MALLOC(arena, XPT_GROW_CHUNK);
if (!state->pool->data)
goto err_free_hash;
state->pool->allocated = XPT_GROW_CHUNK;
}
return state;
err_free_hash:
XPT_HashTableDestroy(state->pool->offset_map);
err_free_pool:
XPT_DELETE(arena, state->pool);
err_free_state:
XPT_DELETE(arena, state);
err_free_arena:
if (arena)
XPT_DestroyArena(arena);
return NULL;
}
XPT_PUBLIC_API(void)
XPT_DestroyXDRState(XPTState *state)
{
XPTArena *arena = state->arena;
if (state->pool->offset_map)
XPT_HashTableDestroy(state->pool->offset_map);
if (state->mode == XPT_ENCODE)
XPT_DELETE(arena, state->pool->data);
XPT_DELETE(arena, state->pool);
XPT_DELETE(arena, state);
if (arena)
XPT_DestroyArena(arena);
}
XPT_PUBLIC_API(void)
XPT_GetXDRDataLength(XPTState *state, XPTPool pool, uint32_t *len)
{
*len = state->next_cursor[pool] - 1;
}
XPT_PUBLIC_API(void)
XPT_GetXDRData(XPTState *state, XPTPool pool, char **data, uint32_t *len)
{
if (pool == XPT_HEADER) {
*data = state->pool->data;
} else {
*data = state->pool->data + state->data_offset;
}
*len = state->next_cursor[pool] - 1;
}
/* All offsets are 1-based */
XPT_PUBLIC_API(void)
XPT_DataOffset(XPTState *state, uint32_t *data_offsetp)
{
if (state->mode == XPT_DECODE)
XPT_SetDataOffset(state, *data_offsetp);
else
*data_offsetp = state->data_offset;
}
/* if 'exact' is set use that, else grow by the next chunk but
* be sure to grow no less that 'at_least' so that we can't get
* behind on required space.
*/
static PRBool
GrowPool(XPTArena *arena, XPTDatapool *pool, uint32_t old_size,
uint32_t exact, uint32_t at_least)
{
uint32_t total_size;
char *newdata;
if (exact) {
XPT_ASSERT(exact > pool->allocated);
total_size = exact;
} else {
total_size = pool->allocated + XPT_GROW_CHUNK;
if (at_least > total_size)
total_size = at_least;
}
newdata = (char*)XPT_MALLOC(arena, total_size);
if (!newdata)
return PR_FALSE;
if (pool->data) {
if (old_size)
memcpy(newdata, pool->data, old_size);
XPT_FREE(arena, pool->data);
}
pool->data = newdata;
pool->allocated = total_size;
return PR_TRUE;
}
XPT_PUBLIC_API(void)
XPT_SetDataOffset(XPTState *state, uint32_t data_offset)
{
state->data_offset = data_offset;
/* make sure we've allocated enough space for the header */
if (state->mode == XPT_ENCODE &&
data_offset > state->pool->allocated) {
(void)GrowPool(state->arena, state->pool, state->pool->allocated,
data_offset, 0);
}
}
XPT_PUBLIC_API(PRBool)
XPT_MakeCursor(XPTState *state, XPTPool pool, uint32_t len, XPTCursor *cursor)
{
cursor->state = state;
cursor->pool = pool;
cursor->bits = 0;
cursor->offset = state->next_cursor[pool];
if (!(CHECK_COUNT(cursor, len)))
return PR_FALSE;
/* this check should be in CHECK_CURSOR */
if (pool == XPT_DATA && !state->data_offset) {
fprintf(stderr, "no data offset for XPT_DATA cursor!\n");
return PR_FALSE;
}
state->next_cursor[pool] += len;
return PR_TRUE;
}
XPT_PUBLIC_API(PRBool)
XPT_SeekTo(XPTCursor *cursor, uint32_t offset)
{
/* XXX do some real checking and update len and stuff */
cursor->offset = offset;
return PR_TRUE;
}
XPT_PUBLIC_API(XPTString *)
XPT_NewString(XPTArena *arena, uint16_t length, const char *bytes)
{
XPTString *str = XPT_NEW(arena, XPTString);
if (!str)
return NULL;
str->length = length;
/* Alloc one extra to store the trailing nul. */
str->bytes = (char*)XPT_MALLOC(arena, length + 1u);
if (!str->bytes) {
XPT_DELETE(arena, str);
return NULL;
}
memcpy(str->bytes, bytes, length);
/* nul-terminate it. */
str->bytes[length] = '\0';
return str;
}
XPT_PUBLIC_API(XPTString *)
XPT_NewStringZ(XPTArena *arena, const char *bytes)
{
uint32_t length = strlen(bytes);
if (length > 0xffff)
return NULL; /* too long */
return XPT_NewString(arena, (uint16_t)length, bytes);
}
XPT_PUBLIC_API(PRBool)
XPT_DoStringInline(XPTArena *arena, XPTCursor *cursor, XPTString **strp)
{
XPTString *str = *strp;
XPTMode mode = cursor->state->mode;
int i;
if (mode == XPT_DECODE) {
str = XPT_NEWZAP(arena, XPTString);
if (!str)
return PR_FALSE;
*strp = str;
}
if (!XPT_Do16(cursor, &str->length))
goto error;
if (mode == XPT_DECODE)
if (!(str->bytes = (char*)XPT_MALLOC(arena, str->length + 1u)))
goto error;
for (i = 0; i < str->length; i++)
if (!XPT_Do8(cursor, (uint8_t *)&str->bytes[i]))
goto error_2;
if (mode == XPT_DECODE)
str->bytes[str->length] = 0;
return PR_TRUE;
error_2:
XPT_DELETE(arena, str->bytes);
error:
XPT_DELETE(arena, str);
return PR_FALSE;
}
XPT_PUBLIC_API(PRBool)
XPT_DoString(XPTArena *arena, XPTCursor *cursor, XPTString **strp)
{
XPTCursor my_cursor;
XPTString *str = *strp;
PRBool already;
XPT_PREAMBLE_NO_ALLOC(cursor, strp, XPT_DATA, str->length + 2u, my_cursor,
already)
return XPT_DoStringInline(arena, &my_cursor, strp);
}
XPT_PUBLIC_API(PRBool)
XPT_DoCString(XPTArena *arena, XPTCursor *cursor, char **identp)
{
XPTCursor my_cursor;
char *ident = *identp;
uint32_t offset = 0;
XPTMode mode = cursor->state->mode;
if (mode == XPT_DECODE) {
char *start, *end;
int len;
if (!XPT_Do32(cursor, &offset))
return PR_FALSE;
if (!offset) {
*identp = NULL;
return PR_TRUE;
}
my_cursor.pool = XPT_DATA;
my_cursor.offset = offset;
my_cursor.state = cursor->state;
start = &CURS_POINT(&my_cursor);
end = VMX_STRCHR(start, 0); /* find the end of the string */
if (!end) {
fprintf(stderr, "didn't find end of string on decode!\n");
return PR_FALSE;
}
len = end - start;
XPT_ASSERT(len > 0);
ident = (char*)XPT_MALLOC(arena, len + 1u);
if (!ident)
return PR_FALSE;
memcpy(ident, start, (size_t)len);
ident[len] = 0;
*identp = ident;
} else {
if (!ident) {
offset = 0;
if (!XPT_Do32(cursor, &offset))
return PR_FALSE;
return PR_TRUE;
}
if (!XPT_MakeCursor(cursor->state, XPT_DATA, strlen(ident) + 1,
&my_cursor) ||
!XPT_Do32(cursor, &my_cursor.offset))
return PR_FALSE;
while(*ident)
if (!XPT_Do8(&my_cursor, (uint8_t *)ident++))
return PR_FALSE;
if (!XPT_Do8(&my_cursor, (uint8_t *)ident)) /* write trailing zero */
return PR_FALSE;
}
return PR_TRUE;
}
/* XXXjband it bothers me that this is one hashtable instead of two.
*/
XPT_PUBLIC_API(uint32_t)
XPT_GetOffsetForAddr(XPTCursor *cursor, void *addr)
{
XPTHashTable *table = cursor->state->pool->offset_map;
return NS_PTR_TO_UINT32(XPT_HashTableLookup(table, addr));
}
XPT_PUBLIC_API(PRBool)
XPT_SetOffsetForAddr(XPTCursor *cursor, void *addr, uint32_t offset)
{
return XPT_HashTableAdd(cursor->state->pool->offset_map,
addr, NS_INT32_TO_PTR(offset)) != NULL;
}
XPT_PUBLIC_API(PRBool)
XPT_SetAddrForOffset(XPTCursor *cursor, uint32_t offset, void *addr)
{
return XPT_HashTableAdd(cursor->state->pool->offset_map,
NS_INT32_TO_PTR(offset), addr) != NULL;
}
XPT_PUBLIC_API(void *)
XPT_GetAddrForOffset(XPTCursor *cursor, uint32_t offset)
{
return XPT_HashTableLookup(cursor->state->pool->offset_map,
NS_INT32_TO_PTR(offset));
}
/* Used by XPT_PREAMBLE_NO_ALLOC. */
static PRBool
CheckForRepeat(XPTCursor *cursor, void **addrp, XPTPool pool, uint32_t len,
XPTCursor *new_cursor, PRBool *already)
{
void *last = *addrp;
*already = PR_FALSE;
new_cursor->state = cursor->state;
new_cursor->pool = pool;
new_cursor->bits = 0;
if (cursor->state->mode == XPT_DECODE) {
last = XPT_GetAddrForOffset(new_cursor, new_cursor->offset);
if (last) {
*already = PR_TRUE;
*addrp = last;
}
} else {
new_cursor->offset = XPT_GetOffsetForAddr(new_cursor, last);
if (new_cursor->offset) {
*already = PR_TRUE;
return PR_TRUE;
}
/* haven't already found it, so allocate room for it. */
if (!XPT_MakeCursor(cursor->state, pool, len, new_cursor) ||
!XPT_SetOffsetForAddr(new_cursor, *addrp, new_cursor->offset))
return PR_FALSE;
}
return PR_TRUE;
}
/*
* IIDs are written in struct order, in the usual big-endian way. From the
* typelib file spec:
*
* "For example, this IID:
* {00112233-4455-6677-8899-aabbccddeeff}
* is converted to the 128-bit value
* 0x00112233445566778899aabbccddeeff
* Note that the byte storage order corresponds to the layout of the nsIID
* C-struct on a big-endian architecture."
*
* (http://www.mozilla.org/scriptable/typelib_file.html#iid)
*/
XPT_PUBLIC_API(PRBool)
XPT_DoIID(XPTCursor *cursor, nsID *iidp)
{
int i;
if (!XPT_Do32(cursor, &iidp->m0) ||
!XPT_Do16(cursor, &iidp->m1) ||
!XPT_Do16(cursor, &iidp->m2))
return PR_FALSE;
for (i = 0; i < 8; i++)
if (!XPT_Do8(cursor, (uint8_t *)&iidp->m3[i]))
return PR_FALSE;
return PR_TRUE;
}
XPT_PUBLIC_API(PRBool)
XPT_Do64(XPTCursor *cursor, int64_t *u64p)
{
return XPT_Do32(cursor, (uint32_t *)u64p) &&
XPT_Do32(cursor, ((uint32_t *)u64p) + 1);
}
/*
* When we're writing 32- or 16-bit quantities, we write a byte at a time to
* avoid alignment issues. Someone could come and optimize this to detect
* well-aligned cases and do a single store, if they cared. I might care
* later.
*/
XPT_PUBLIC_API(PRBool)
XPT_Do32(XPTCursor *cursor, uint32_t *u32p)
{
union {
uint8_t b8[4];
uint32_t b32;
} u;
if (!CHECK_COUNT(cursor, 4))
return PR_FALSE;
if (ENCODING(cursor)) {
u.b32 = XPT_SWAB32(*u32p);
CURS_POINT(cursor) = u.b8[0];
cursor->offset++;
CURS_POINT(cursor) = u.b8[1];
cursor->offset++;
CURS_POINT(cursor) = u.b8[2];
cursor->offset++;
CURS_POINT(cursor) = u.b8[3];
} else {
u.b8[0] = CURS_POINT(cursor);
cursor->offset++;
u.b8[1] = CURS_POINT(cursor);
cursor->offset++;
u.b8[2] = CURS_POINT(cursor);
cursor->offset++;
u.b8[3] = CURS_POINT(cursor);
*u32p = XPT_SWAB32(u.b32);
}
cursor->offset++;
return PR_TRUE;
}
XPT_PUBLIC_API(PRBool)
XPT_Do16(XPTCursor *cursor, uint16_t *u16p)
{
union {
uint8_t b8[2];
uint16_t b16;
} u;
if (!CHECK_COUNT(cursor, 2))
return PR_FALSE;
if (ENCODING(cursor)) {
u.b16 = XPT_SWAB16(*u16p);
CURS_POINT(cursor) = u.b8[0];
cursor->offset++;
CURS_POINT(cursor) = u.b8[1];
} else {
u.b8[0] = CURS_POINT(cursor);
cursor->offset++;
u.b8[1] = CURS_POINT(cursor);
*u16p = XPT_SWAB16(u.b16);
}
cursor->offset++;
return PR_TRUE;
}
XPT_PUBLIC_API(PRBool)
XPT_Do8(XPTCursor *cursor, uint8_t *u8p)
{
if (!CHECK_COUNT(cursor, 1))
return PR_FALSE;
if (cursor->state->mode == XPT_ENCODE)
CURS_POINT(cursor) = *u8p;
else
*u8p = CURS_POINT(cursor);
cursor->offset++;
return PR_TRUE;
}