mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
533 lines
13 KiB
C
533 lines
13 KiB
C
/* -*- Mode: C; tab-width: 8; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "jsstddef.h"
|
|
|
|
#include <string.h>
|
|
#include "prtypes.h"
|
|
#include "prassert.h"
|
|
#include "jsapi.h"
|
|
#include "jsobj.h" /* js_XDRObject */
|
|
#include "jsstr.h"
|
|
#include "jsxdrapi.h"
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x) x
|
|
#else
|
|
#define DBG(x) ((void)0)
|
|
#endif
|
|
|
|
typedef struct JSXDRMemState {
|
|
JSXDRState state;
|
|
uint32 count;
|
|
uint32 limit;
|
|
} JSXDRMemState;
|
|
|
|
#define MEM_BLOCK 8192
|
|
#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr))
|
|
|
|
#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count)
|
|
#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit)
|
|
|
|
static char unexpected_end_of_data_str[] = "unexpected end of data";
|
|
|
|
#define MEM_LEFT(xdr, bytes) \
|
|
PR_BEGIN_MACRO \
|
|
if ((xdr)->mode == JSXDR_DECODE && \
|
|
MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \
|
|
JS_ReportError((xdr)->cx, unexpected_end_of_data_str); \
|
|
return 0; \
|
|
} \
|
|
PR_END_MACRO
|
|
|
|
/* XXXbe why does NEED even allow or cope with non-ENCODE mode? */
|
|
#define MEM_NEED(xdr, bytes) \
|
|
PR_BEGIN_MACRO \
|
|
if ((xdr)->mode == JSXDR_ENCODE) { \
|
|
if (MEM_LIMIT(xdr) && \
|
|
MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \
|
|
void *_data = JS_realloc((xdr)->cx, \
|
|
(xdr)->data, \
|
|
MEM_LIMIT(xdr) + MEM_BLOCK); \
|
|
if (!_data) \
|
|
return 0; \
|
|
(xdr)->data = _data; \
|
|
MEM_LIMIT(xdr) += MEM_BLOCK; \
|
|
} \
|
|
} else { \
|
|
if (MEM_LIMIT(xdr) < MEM_COUNT(xdr) + bytes) { \
|
|
JS_ReportError((xdr)->cx, unexpected_end_of_data_str); \
|
|
return 0; \
|
|
} \
|
|
} \
|
|
PR_END_MACRO
|
|
|
|
#define MEM_DATA(xdr) ((void *)((char *)(xdr)->data + MEM_COUNT(xdr)))
|
|
#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes))
|
|
|
|
static JSBool
|
|
mem_get32(JSXDRState *xdr, uint32 *lp)
|
|
{
|
|
MEM_LEFT(xdr, 4);
|
|
*lp = *(uint32 *)MEM_DATA(xdr);
|
|
MEM_INCR(xdr, 4);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
mem_set32(JSXDRState *xdr, uint32 *lp)
|
|
{
|
|
MEM_NEED(xdr, 4);
|
|
*(uint32 *)MEM_DATA(xdr) = *lp;
|
|
MEM_INCR(xdr, 4);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
mem_getbytes(JSXDRState *xdr, char **bytesp, uint32 len)
|
|
{
|
|
MEM_LEFT(xdr, len);
|
|
memcpy(*bytesp, MEM_DATA(xdr), len);
|
|
MEM_INCR(xdr, len);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static JSBool
|
|
mem_setbytes(JSXDRState *xdr, char **bytesp, uint32 len)
|
|
{
|
|
MEM_NEED(xdr, len);
|
|
memcpy(MEM_DATA(xdr), *bytesp, len);
|
|
MEM_INCR(xdr, len);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
static void *
|
|
mem_raw(JSXDRState *xdr, uint32 len)
|
|
{
|
|
void *data;
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
MEM_NEED(xdr, len);
|
|
} else if (xdr->mode == JSXDR_DECODE) {
|
|
MEM_LEFT(xdr, len);
|
|
}
|
|
data = MEM_DATA(xdr);
|
|
MEM_INCR(xdr, len);
|
|
return data;
|
|
}
|
|
|
|
static JSBool
|
|
mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence)
|
|
{
|
|
switch (whence) {
|
|
case JSXDR_SEEK_CUR:
|
|
if ((int32)MEM_COUNT(xdr) + offset < 0) {
|
|
JS_ReportError(xdr->cx, "illegal seek beyond start");
|
|
return JS_FALSE;
|
|
}
|
|
if (offset > 0)
|
|
MEM_NEED(xdr, offset);
|
|
MEM_COUNT(xdr) += offset;
|
|
return JS_TRUE;
|
|
case JSXDR_SEEK_SET:
|
|
if (offset < 0) {
|
|
JS_ReportError(xdr->cx, "illegal seek beyond start");
|
|
return JS_FALSE;
|
|
}
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
if ((uint32)offset > MEM_COUNT(xdr))
|
|
MEM_NEED(xdr, offset - MEM_COUNT(xdr));
|
|
MEM_COUNT(xdr) = offset;
|
|
} else {
|
|
if ((uint32)offset > MEM_LIMIT(xdr)) {
|
|
JS_ReportError(xdr->cx, "illegal seek beyond end");
|
|
return JS_FALSE;
|
|
}
|
|
MEM_COUNT(xdr) = offset;
|
|
}
|
|
return JS_TRUE;
|
|
case JSXDR_SEEK_END:
|
|
if (offset >= 0 ||
|
|
xdr->mode == JSXDR_ENCODE ||
|
|
(int32)MEM_LIMIT(xdr) + offset < 0) {
|
|
JS_ReportError(xdr->cx, "illegal end-based seek");
|
|
return JS_FALSE;
|
|
}
|
|
MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset;
|
|
return JS_TRUE;
|
|
default:
|
|
JS_ReportError(xdr->cx, "unknown seek whence: %d", whence);
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
static uint32
|
|
mem_tell(JSXDRState *xdr)
|
|
{
|
|
return MEM_COUNT(xdr);
|
|
}
|
|
|
|
static void
|
|
mem_finalize(JSXDRState *xdr)
|
|
{
|
|
JSContext *cx = xdr->cx;
|
|
JS_free(cx, xdr->data);
|
|
}
|
|
|
|
static JSXDROps xdrmem_ops = {
|
|
mem_get32, mem_set32, mem_getbytes, mem_setbytes,
|
|
mem_raw, mem_seek, mem_tell, mem_finalize
|
|
};
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_XDRNewBase(JSContext *cx, JSXDRState *xdr, JSXDRMode mode)
|
|
{
|
|
xdr->cx = cx;
|
|
xdr->mode = mode;
|
|
xdr->registry = NULL;
|
|
xdr->nclasses = 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSXDRState *)
|
|
JS_XDRNewMem(JSContext *cx, JSXDRMode mode)
|
|
{
|
|
JSXDRState *xdr = JS_malloc(cx, sizeof(JSXDRMemState));
|
|
if (!xdr)
|
|
return NULL;
|
|
JS_XDRNewBase(cx, xdr, mode);
|
|
if (mode == JSXDR_ENCODE) {
|
|
if (!(xdr->data = JS_malloc(cx, MEM_BLOCK))) {
|
|
JS_free(cx, xdr);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/* XXXbe ok, so better not deref xdr->data if not ENCODE */
|
|
xdr->data = NULL;
|
|
}
|
|
xdr->ops = &xdrmem_ops;
|
|
MEM_PRIV(xdr)->count = 0;
|
|
MEM_PRIV(xdr)->limit = MEM_BLOCK;
|
|
return xdr;
|
|
}
|
|
|
|
JS_PUBLIC_API(void *)
|
|
JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp)
|
|
{
|
|
if (xdr->ops != &xdrmem_ops)
|
|
return NULL;
|
|
*lp = MEM_PRIV(xdr)->count;
|
|
return xdr->data;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len)
|
|
{
|
|
if (xdr->ops != &xdrmem_ops)
|
|
return;
|
|
MEM_PRIV(xdr)->limit = len;
|
|
xdr->data = data;
|
|
MEM_PRIV(xdr)->count = 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRUint8(JSXDRState *xdr, uint8 *b)
|
|
{
|
|
uint32 l = *b;
|
|
if (!JS_XDRUint32(xdr, &l))
|
|
return JS_FALSE;
|
|
*b = (uint8) l & 0xff;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRUint16(JSXDRState *xdr, uint16 *s)
|
|
{
|
|
uint32 l = *s;
|
|
if (!JS_XDRUint32(xdr, &l))
|
|
return JS_FALSE;
|
|
*s = (uint16) l & 0xffff;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRUint32(JSXDRState *xdr, uint32 *lp)
|
|
{
|
|
JSBool ok;
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
uint32 xl = JSXDR_SWAB32(*lp);
|
|
ok = xdr->ops->set32(xdr, &xl);
|
|
} else if (xdr->mode == JSXDR_DECODE) {
|
|
ok = xdr->ops->get32(xdr, lp);
|
|
*lp = JSXDR_SWAB32(*lp);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRBytes(JSXDRState *xdr, char **bytesp, uint32 len)
|
|
{
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
if (!xdr->ops->setbytes(xdr, bytesp, len))
|
|
return JS_FALSE;
|
|
} else {
|
|
if (!xdr->ops->getbytes(xdr, bytesp, len))
|
|
return JS_FALSE;
|
|
}
|
|
len = xdr->ops->tell(xdr);
|
|
if (len % JSXDR_ALIGN) {
|
|
if (!xdr->ops->seek(xdr, JSXDR_ALIGN - (len % JSXDR_ALIGN),
|
|
JSXDR_SEEK_CUR)) {
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/**
|
|
* Convert between a C string and the XDR representation:
|
|
* leading 32-bit count, then counted vector of chars,
|
|
* then possibly \0 padding to multiple of 4.
|
|
*/
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRCString(JSXDRState *xdr, char **sp)
|
|
{
|
|
uint32 len;
|
|
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
len = strlen(*sp);
|
|
JS_XDRUint32(xdr, &len);
|
|
if (xdr->mode == JSXDR_DECODE) {
|
|
if (!(*sp = JS_malloc(xdr->cx, len + 1)))
|
|
return JS_FALSE;
|
|
}
|
|
if (!JS_XDRBytes(xdr, sp, len)) {
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
JS_free(xdr->cx, *sp);
|
|
return JS_FALSE;
|
|
}
|
|
if (xdr->mode == JSXDR_DECODE) {
|
|
(*sp)[len] = '\0';
|
|
} else if (xdr->mode == JSXDR_FREE) {
|
|
JS_free(xdr->cx, *sp);
|
|
*sp = NULL;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)
|
|
{
|
|
uint32 null = (*sp == NULL);
|
|
if (!JS_XDRUint32(xdr, &null))
|
|
return JS_FALSE;
|
|
if (null) {
|
|
*sp = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
return JS_XDRCString(xdr, sp);
|
|
}
|
|
|
|
/*
|
|
* Convert between a JS (Unicode) string and the XDR representation.
|
|
*/
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRString(JSXDRState *xdr, JSString **strp)
|
|
{
|
|
uint32 i, len, nbytes;
|
|
jschar *chars, *raw;
|
|
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
len = (*strp)->length;
|
|
if (!JS_XDRUint32(xdr, &len))
|
|
return JS_FALSE;
|
|
nbytes = len * sizeof(jschar);
|
|
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
chars = (*strp)->chars;
|
|
} else if (xdr->mode == JSXDR_DECODE) {
|
|
if (!(chars = JS_malloc(xdr->cx, nbytes + sizeof(jschar))))
|
|
return JS_FALSE;
|
|
}
|
|
|
|
if (nbytes % JSXDR_ALIGN)
|
|
nbytes += JSXDR_ALIGN - (nbytes % JSXDR_ALIGN);
|
|
if (!(raw = xdr->ops->raw(xdr, nbytes)))
|
|
goto bad;
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
for (i = 0; i < len; i++)
|
|
raw[i] = JSXDR_SWAB16(chars[i]);
|
|
} else if (xdr->mode == JSXDR_DECODE) {
|
|
for (i = 0; i < len; i++)
|
|
chars[i] = JSXDR_SWAB16(raw[i]);
|
|
if (!(*strp = JS_NewUCString(xdr->cx, chars, len)))
|
|
goto bad;
|
|
}
|
|
return JS_TRUE;
|
|
|
|
bad:
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
JS_free(xdr->cx, chars);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp)
|
|
{
|
|
uint32 null = (*strp == NULL);
|
|
if (!JS_XDRUint32(xdr, &null))
|
|
return JS_FALSE;
|
|
if (null) {
|
|
*strp = NULL;
|
|
return JS_TRUE;
|
|
}
|
|
return JS_XDRString(xdr, strp);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRDouble(JSXDRState *xdr, jsdouble **dp)
|
|
{
|
|
jsdouble d;
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
d = **dp;
|
|
#if IS_BIG_ENDIAN
|
|
if (!JS_XDRUint32(xdr, (uint32 *)&d + 1) ||
|
|
!JS_XDRUint32(xdr, (uint32 *)&d))
|
|
#else /* !IS_BIG_ENDIAN */
|
|
if (!JS_XDRUint32(xdr, (uint32 *)&d) ||
|
|
!JS_XDRUint32(xdr, (uint32 *)&d + 1))
|
|
#endif /* IS_BIG_ENDIAN */
|
|
return JS_FALSE;
|
|
if (xdr->mode == JSXDR_DECODE) {
|
|
*dp = JS_NewDouble(xdr->cx, d);
|
|
if (!*dp)
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_XDRValue(JSXDRState *xdr, jsval *vp)
|
|
{
|
|
uint32 type = JSVAL_TAG(*vp);
|
|
if (!JS_XDRUint32(xdr, &type))
|
|
return JS_FALSE;
|
|
|
|
switch (type) {
|
|
case JSVAL_STRING: {
|
|
JSString *str = JSVAL_TO_STRING(*vp);
|
|
if (!JS_XDRString(xdr, &str))
|
|
return JS_FALSE;
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
*vp = STRING_TO_JSVAL(str);
|
|
break;
|
|
}
|
|
case JSVAL_DOUBLE: {
|
|
jsdouble *dp;
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
dp = JSVAL_TO_DOUBLE(*vp);
|
|
if (!JS_XDRDouble(xdr, &dp))
|
|
return JS_FALSE;
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
*vp = DOUBLE_TO_JSVAL(dp);
|
|
break;
|
|
}
|
|
case JSVAL_OBJECT: {
|
|
JSObject *obj;
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
obj = JSVAL_TO_OBJECT(*vp);
|
|
if (!js_XDRObject(xdr, &obj))
|
|
return JS_FALSE;
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
*vp = OBJECT_TO_JSVAL(obj);
|
|
break;
|
|
}
|
|
case JSVAL_BOOLEAN: {
|
|
uint32 bool;
|
|
if (xdr->mode == JSXDR_ENCODE)
|
|
bool = (uint32)JSVAL_TO_BOOLEAN(*vp);
|
|
if (!JS_XDRUint32(xdr, &bool))
|
|
return JS_FALSE;
|
|
if (xdr->mode == JSXDR_DECODE)
|
|
*vp = BOOLEAN_TO_JSVAL((JSBool)bool);
|
|
break;
|
|
}
|
|
case JSVAL_VOID:
|
|
if (!JS_XDRUint32(xdr, (uint32 *)vp))
|
|
return JS_FALSE;
|
|
break;
|
|
default:
|
|
JS_ReportError(xdr->cx, "unknown jsval type %#lx for XDR", type);
|
|
return JS_FALSE;
|
|
}
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_XDRDestroy(JSXDRState *xdr)
|
|
{
|
|
JSContext *cx = xdr->cx;
|
|
xdr->ops->finalize(xdr);
|
|
if (xdr->registry)
|
|
JS_free(cx, xdr->registry);
|
|
JS_free(cx, xdr);
|
|
}
|
|
|
|
#define REGISTRY_CHUNK 4
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_RegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp)
|
|
{
|
|
uintN nclasses;
|
|
JSClass **registry;
|
|
|
|
nclasses = xdr->nclasses;
|
|
if (nclasses == 0) {
|
|
registry = JS_malloc(xdr->cx, REGISTRY_CHUNK * sizeof(JSClass *));
|
|
} else if (nclasses % REGISTRY_CHUNK == 0) {
|
|
registry = JS_realloc(xdr->cx,
|
|
xdr->registry,
|
|
(nclasses + REGISTRY_CHUNK) * sizeof(JSClass *));
|
|
}
|
|
if (!registry)
|
|
return JS_FALSE;
|
|
registry[nclasses++] = clasp;
|
|
xdr->registry = registry;
|
|
xdr->nclasses = nclasses;
|
|
*idp = nclasses;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(uint32)
|
|
JS_FindClassIdByName(JSXDRState *xdr, const char *name)
|
|
{
|
|
uintN i;
|
|
|
|
for (i = 0; i < xdr->nclasses; i++) {
|
|
if (!strcmp(name, xdr->registry[i]->name))
|
|
return i+1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSClass *)
|
|
JS_FindClassById(JSXDRState *xdr, uint32 id)
|
|
{
|
|
if (id > xdr->nclasses)
|
|
return NULL;
|
|
return xdr->registry[id-1];
|
|
}
|