466 lines
13 KiB
C
466 lines
13 KiB
C
/***********************************************************************/
|
|
/* */
|
|
/* Objective Caml */
|
|
/* */
|
|
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
|
|
/* */
|
|
/* Copyright 1996 Institut National de Recherche en Informatique et */
|
|
/* en Automatique. Distributed only by permission. */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/* $Id$ */
|
|
|
|
/* Structured input, compact format */
|
|
|
|
#include <string.h>
|
|
#include "alloc.h"
|
|
#include "fail.h"
|
|
#include "gc.h"
|
|
#include "intext.h"
|
|
#include "io.h"
|
|
#include "memory.h"
|
|
#include "mlvalues.h"
|
|
#include "misc.h"
|
|
#include "reverse.h"
|
|
|
|
static unsigned char * intern_src;
|
|
/* Reading pointer in block holding input data. */
|
|
|
|
static unsigned char * intern_input;
|
|
/* Pointer to beginning of block holding input data.
|
|
Meaningful only if intern_input_malloced = 1. */
|
|
|
|
static int intern_input_malloced;
|
|
/* 1 if intern_input was allocated by stat_alloc()
|
|
and needs stat_free() on error, 0 otherwise. */
|
|
|
|
static header_t * intern_dest;
|
|
/* Writing pointer in destination block */
|
|
|
|
static header_t * intern_extra_block;
|
|
/* If non-NULL, point to new heap chunk allocated with alloc_for_heap. */
|
|
|
|
static asize_t obj_counter;
|
|
/* Count how many objects seen so far */
|
|
|
|
static value * intern_obj_table;
|
|
/* The pointers to objects already seen */
|
|
|
|
static unsigned int intern_color;
|
|
/* Color to assign to newly created headers */
|
|
|
|
static header_t intern_header;
|
|
/* Original header of the destination block.
|
|
Meaningful only if intern_extra_block is NULL. */
|
|
|
|
static value intern_block;
|
|
/* Point to the heap block allocated as destination block.
|
|
Meaningful only if intern_extra_block is NULL. */
|
|
|
|
#define Sign_extend_shift ((sizeof(long) - 1) * 8)
|
|
#define Sign_extend(x) (((long)(x) << Sign_extend_shift) >> Sign_extend_shift)
|
|
|
|
#define read8u() (*intern_src++)
|
|
#define read8s() Sign_extend(*intern_src++)
|
|
#define read16u() \
|
|
(intern_src += 2, \
|
|
(intern_src[-2] << 8) + intern_src[-1])
|
|
#define read16s() \
|
|
(intern_src += 2, \
|
|
(Sign_extend(intern_src[-2]) << 8) + intern_src[-1])
|
|
#define read32u() \
|
|
(intern_src += 4, \
|
|
(intern_src[-4] << 24) + (intern_src[-3] << 16) + \
|
|
(intern_src[-2] << 8) + intern_src[-1])
|
|
#define read32s() \
|
|
(intern_src += 4, \
|
|
(Sign_extend(intern_src[-4]) << 24) + (intern_src[-3] << 16) + \
|
|
(intern_src[-2] << 8) + intern_src[-1])
|
|
|
|
#ifdef ARCH_SIXTYFOUR
|
|
static long read64s(void)
|
|
{
|
|
long res;
|
|
int i;
|
|
res = 0;
|
|
for (i = 0; i < 8; i++) res = (res << 8) + intern_src[i];
|
|
intern_src += 8;
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
#define readblock(dest,len) \
|
|
(bcopy(intern_src, dest, len), intern_src += len)
|
|
|
|
static void intern_cleanup(void)
|
|
{
|
|
if (intern_input_malloced) stat_free(intern_input);
|
|
if (intern_obj_table != NULL) stat_free(intern_obj_table);
|
|
if (intern_extra_block != NULL) {
|
|
/* free newly allocated heap chunk */
|
|
free_for_heap(intern_extra_block);
|
|
} else {
|
|
/* restore original header for heap block, otherwise GC is confused */
|
|
Hd_val(intern_block) = intern_header;
|
|
}
|
|
}
|
|
|
|
static void intern_rec(value *dest)
|
|
{
|
|
unsigned int code;
|
|
tag_t tag;
|
|
mlsize_t size, len, ofs_ind;
|
|
value v, clos;
|
|
asize_t ofs;
|
|
header_t header;
|
|
char cksum[16];
|
|
|
|
tailcall:
|
|
code = read8u();
|
|
if (code >= PREFIX_SMALL_INT) {
|
|
if (code >= PREFIX_SMALL_BLOCK) {
|
|
/* Small block */
|
|
tag = code & 0xF;
|
|
size = (code >> 4) & 0x7;
|
|
read_block:
|
|
if (size == 0) {
|
|
v = Atom(tag);
|
|
} else {
|
|
v = Val_hp(intern_dest);
|
|
*dest = v;
|
|
if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v;
|
|
dest = (value *) (intern_dest + 1);
|
|
*intern_dest = Make_header(size, tag, intern_color);
|
|
intern_dest += 1 + size;
|
|
for(/*nothing*/; size > 1; size--, dest++)
|
|
intern_rec(dest);
|
|
goto tailcall;
|
|
}
|
|
} else {
|
|
/* Small integer */
|
|
v = Val_int(code & 0x3F);
|
|
}
|
|
} else {
|
|
if (code >= PREFIX_SMALL_STRING) {
|
|
/* Small string */
|
|
len = (code & 0x1F);
|
|
read_string:
|
|
size = (len + sizeof(value)) / sizeof(value);
|
|
v = Val_hp(intern_dest);
|
|
if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v;
|
|
*intern_dest = Make_header(size, String_tag, intern_color);
|
|
intern_dest += 1 + size;
|
|
Field(v, size - 1) = 0;
|
|
ofs_ind = Bsize_wsize(size) - 1;
|
|
Byte(v, ofs_ind) = ofs_ind - len;
|
|
readblock(String_val(v), len);
|
|
} else {
|
|
switch(code) {
|
|
case CODE_INT8:
|
|
v = Val_long(read8s());
|
|
break;
|
|
case CODE_INT16:
|
|
v = Val_long(read16s());
|
|
break;
|
|
case CODE_INT32:
|
|
v = Val_long(read32s());
|
|
break;
|
|
case CODE_INT64:
|
|
#ifdef ARCH_SIXTYFOUR
|
|
v = Val_long(read64s());
|
|
break;
|
|
#else
|
|
intern_cleanup();
|
|
failwith("input_value: integer too large");
|
|
break;
|
|
#endif
|
|
case CODE_SHARED8:
|
|
ofs = read8u();
|
|
read_shared:
|
|
Assert(ofs > 0 && ofs <= obj_counter && intern_obj_table != NULL);
|
|
v = intern_obj_table[obj_counter - ofs];
|
|
break;
|
|
case CODE_SHARED16:
|
|
ofs = read16u();
|
|
goto read_shared;
|
|
case CODE_SHARED32:
|
|
ofs = read32u();
|
|
goto read_shared;
|
|
case CODE_BLOCK32:
|
|
header = (header_t) read32u();
|
|
tag = Tag_hd(header);
|
|
size = Wosize_hd(header);
|
|
goto read_block;
|
|
case CODE_STRING8:
|
|
len = read8u();
|
|
goto read_string;
|
|
case CODE_STRING32:
|
|
len = read32u();
|
|
goto read_string;
|
|
case CODE_DOUBLE_LITTLE:
|
|
case CODE_DOUBLE_BIG:
|
|
if (sizeof(double) != 8) {
|
|
intern_cleanup();
|
|
invalid_argument("input_value: non-standard floats");
|
|
}
|
|
v = Val_hp(intern_dest);
|
|
if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v;
|
|
*intern_dest = Make_header(Double_wosize, Double_tag, intern_color);
|
|
intern_dest += 1 + Double_wosize;
|
|
readblock((char *) v, 8);
|
|
if (code != CODE_DOUBLE_NATIVE) Reverse_double(v);
|
|
break;
|
|
case CODE_DOUBLE_ARRAY8_LITTLE:
|
|
case CODE_DOUBLE_ARRAY8_BIG:
|
|
len = read8u();
|
|
read_double_array:
|
|
if (sizeof(double) != 8) {
|
|
intern_cleanup();
|
|
invalid_argument("input_value: non-standard floats");
|
|
}
|
|
size = len * Double_wosize;
|
|
v = Val_hp(intern_dest);
|
|
if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v;
|
|
*intern_dest = Make_header(size, Double_array_tag, intern_color);
|
|
intern_dest += 1 + size;
|
|
readblock((char *) v, len * 8);
|
|
if (code != CODE_DOUBLE_ARRAY8_NATIVE &&
|
|
code != CODE_DOUBLE_ARRAY32_NATIVE) {
|
|
mlsize_t i;
|
|
for (i = 0; i < len; i++) Reverse_double((value)((double *)v + i));
|
|
}
|
|
break;
|
|
case CODE_DOUBLE_ARRAY32_LITTLE:
|
|
case CODE_DOUBLE_ARRAY32_BIG:
|
|
len = read32u();
|
|
goto read_double_array;
|
|
case CODE_CODEPOINTER:
|
|
ofs = read32u();
|
|
readblock(cksum, 16);
|
|
if (memcmp(cksum, code_checksum(), 16) != 0) {
|
|
intern_cleanup();
|
|
failwith("input_value: code mismatch");
|
|
}
|
|
v = (value) (code_area_start + ofs);
|
|
break;
|
|
case CODE_INFIXPOINTER:
|
|
ofs = read32u();
|
|
intern_rec(&clos);
|
|
v = clos + ofs;
|
|
break;
|
|
default:
|
|
intern_cleanup();
|
|
failwith("input_value: ill-formed message");
|
|
}
|
|
}
|
|
}
|
|
*dest = v;
|
|
}
|
|
|
|
static void intern_alloc(mlsize_t whsize, mlsize_t num_objects)
|
|
{
|
|
mlsize_t wosize;
|
|
|
|
if (whsize == 0) {
|
|
intern_obj_table = NULL;
|
|
return;
|
|
}
|
|
wosize = Wosize_whsize(whsize);
|
|
if (wosize > Max_wosize) {
|
|
/* Round desired size up to next page */
|
|
asize_t request =
|
|
((Bsize_wsize(whsize) + Page_size - 1) >> Page_log) << Page_log;
|
|
intern_extra_block = alloc_for_heap(request);
|
|
if (intern_extra_block == NULL) raise_out_of_memory();
|
|
intern_color = allocation_color(intern_extra_block);
|
|
intern_dest = intern_extra_block;
|
|
} else {
|
|
intern_block = alloc(wosize, String_tag);
|
|
intern_header = Hd_val(intern_block);
|
|
intern_color = Color_hd(intern_header);
|
|
Assert (intern_color == White || intern_color == Black);
|
|
intern_dest = (header_t *) Hp_val(intern_block);
|
|
intern_extra_block = NULL;
|
|
}
|
|
obj_counter = 0;
|
|
if (num_objects > 0)
|
|
intern_obj_table = (value *) stat_alloc(num_objects * sizeof(value));
|
|
else
|
|
intern_obj_table = NULL;
|
|
}
|
|
|
|
static void intern_add_to_heap(mlsize_t whsize)
|
|
{
|
|
/* Add new heap chunk to heap if needed */
|
|
if (intern_extra_block != NULL) {
|
|
/* If heap chunk not filled totally, build free block at end */
|
|
asize_t request =
|
|
((Bsize_wsize(whsize) + Page_size - 1) >> Page_log) << Page_log;
|
|
header_t * end_extra_block = intern_extra_block + Wsize_bsize(request);
|
|
Assert(intern_dest <= end_extra_block);
|
|
if (intern_dest < end_extra_block)
|
|
*intern_dest =
|
|
Make_header(Wosize_whsize(end_extra_block - intern_dest), 0, White);
|
|
add_to_heap(intern_extra_block);
|
|
}
|
|
}
|
|
|
|
value input_val(struct channel *chan)
|
|
{
|
|
uint32 magic;
|
|
mlsize_t block_len, num_objects, size_32, size_64, whsize;
|
|
value res;
|
|
|
|
if (! channel_binary_mode(chan))
|
|
failwith("input_value: not a binary channel");
|
|
magic = getword(chan);
|
|
if (magic != Intext_magic_number) failwith("input_value: bad object");
|
|
block_len = getword(chan);
|
|
num_objects = getword(chan);
|
|
size_32 = getword(chan);
|
|
size_64 = getword(chan);
|
|
/* Read block from channel */
|
|
intern_input = (unsigned char *) stat_alloc(block_len);
|
|
intern_input_malloced = 1;
|
|
if (really_getblock(chan, (char *)intern_input, block_len) == 0) {
|
|
stat_free(intern_input);
|
|
failwith("input_value: truncated object");
|
|
}
|
|
intern_src = intern_input;
|
|
/* Allocate result */
|
|
#ifdef ARCH_SIXTYFOUR
|
|
whsize = size_64;
|
|
#else
|
|
whsize = size_32;
|
|
#endif
|
|
intern_alloc(whsize, num_objects);
|
|
/* Fill it in */
|
|
intern_rec(&res);
|
|
intern_add_to_heap(whsize);
|
|
/* Free everything */
|
|
stat_free(intern_input);
|
|
if (intern_obj_table != NULL) stat_free(intern_obj_table);
|
|
return res;
|
|
}
|
|
|
|
value input_value(value vchan) /* ML */
|
|
{
|
|
struct channel * chan = Channel(vchan);
|
|
value res = Val_unit;
|
|
|
|
Begin_root(res)
|
|
Lock(chan);
|
|
res = input_val(chan);
|
|
Unlock(chan);
|
|
End_roots();
|
|
return res;
|
|
}
|
|
|
|
value input_val_from_string(value str, long int ofs)
|
|
{
|
|
mlsize_t num_objects, size_32, size_64, whsize;
|
|
value obj;
|
|
|
|
intern_src = &Byte_u(str, ofs + 2*4);
|
|
intern_input_malloced = 0;
|
|
num_objects = read32u();
|
|
size_32 = read32u();
|
|
size_64 = read32u();
|
|
/* Allocate result */
|
|
#ifdef ARCH_SIXTYFOUR
|
|
whsize = size_64;
|
|
#else
|
|
whsize = size_32;
|
|
#endif
|
|
Begin_root(str);
|
|
intern_alloc(whsize, num_objects);
|
|
End_roots();
|
|
intern_src = &Byte_u(str, ofs + 5*4); /* If a GC occurred */
|
|
/* Fill it in */
|
|
intern_rec(&obj);
|
|
intern_add_to_heap(whsize);
|
|
/* Free everything */
|
|
if (intern_obj_table != NULL) stat_free(intern_obj_table);
|
|
return obj;
|
|
}
|
|
|
|
value input_value_from_string(value str, value ofs) /* ML */
|
|
{
|
|
return input_val_from_string(str, Long_val(ofs));
|
|
}
|
|
|
|
value input_value_from_malloc(char * data, long ofs)
|
|
{
|
|
mlsize_t num_objects, size_32, size_64, whsize;
|
|
value obj;
|
|
|
|
intern_input = (unsigned char *) data;
|
|
intern_src = intern_input + ofs + 2*4;
|
|
intern_input_malloced = 1;
|
|
num_objects = read32u();
|
|
size_32 = read32u();
|
|
size_64 = read32u();
|
|
/* Allocate result */
|
|
#ifdef ARCH_SIXTYFOUR
|
|
whsize = size_64;
|
|
#else
|
|
whsize = size_32;
|
|
#endif
|
|
intern_alloc(whsize, num_objects);
|
|
/* Fill it in */
|
|
intern_rec(&obj);
|
|
intern_add_to_heap(whsize);
|
|
/* Free everything */
|
|
stat_free(intern_input);
|
|
if (intern_obj_table != NULL) stat_free(intern_obj_table);
|
|
return obj;
|
|
}
|
|
|
|
value marshal_data_size(value buff, value ofs) /* ML */
|
|
{
|
|
uint32 magic;
|
|
mlsize_t block_len;
|
|
|
|
intern_src = &Byte_u(buff, Long_val(ofs));
|
|
intern_input_malloced = 0;
|
|
magic = read32u();
|
|
if (magic != Intext_magic_number) failwith("Marshal.data_size: bad object");
|
|
block_len = read32u();
|
|
return Val_long(block_len);
|
|
}
|
|
|
|
/* Return an MD5 checksum of the code area */
|
|
|
|
#ifdef NATIVE_CODE
|
|
|
|
#include "md5.h"
|
|
|
|
unsigned char * code_checksum()
|
|
{
|
|
static unsigned char checksum[16];
|
|
static int checksum_computed = 0;
|
|
|
|
if (! checksum_computed) {
|
|
struct MD5Context ctx;
|
|
MD5Init(&ctx);
|
|
MD5Update(&ctx,
|
|
(unsigned char *) code_area_start,
|
|
code_area_end - code_area_start);
|
|
MD5Final(checksum, &ctx);
|
|
checksum_computed = 1;
|
|
}
|
|
return checksum;
|
|
}
|
|
|
|
#else
|
|
|
|
#include "fix_code.h"
|
|
|
|
unsigned char * code_checksum(void)
|
|
{
|
|
return code_md5;
|
|
}
|
|
|
|
#endif
|