ocaml/byterun/extern.c

905 lines
26 KiB
C

/**************************************************************************/
/* */
/* OCaml */
/* */
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
/* */
/* Copyright 1996 Institut National de Recherche en Informatique et */
/* en Automatique. */
/* */
/* All rights reserved. This file is distributed under the terms of */
/* the GNU Lesser General Public License version 2.1, with the */
/* special exception on linking described in the file LICENSE. */
/* */
/**************************************************************************/
/* Structured output */
/* The interface of this file is "caml/intext.h" */
#include <string.h>
#include "caml/alloc.h"
#include "caml/config.h"
#include "caml/custom.h"
#include "caml/fail.h"
#include "caml/gc.h"
#include "caml/intext.h"
#include "caml/io.h"
#include "caml/md5.h"
#include "caml/memory.h"
#include "caml/misc.h"
#include "caml/mlvalues.h"
#include "caml/reverse.h"
static uintnat obj_counter; /* Number of objects emitted so far */
static uintnat size_32; /* Size in words of 32-bit block for struct. */
static uintnat size_64; /* Size in words of 64-bit block for struct. */
/* Flags affecting marshaling */
enum {
NO_SHARING = 1, /* Flag to ignore sharing */
CLOSURES = 2, /* Flag to allow marshaling code pointers */
COMPAT_32 = 4 /* Flag to ensure that output can safely
be read back on a 32-bit platform */
};
static int extern_flags; /* logical or of some of the flags above */
/* Trail mechanism to undo forwarding pointers put inside objects */
struct trail_entry {
value obj; /* address of object + initial color in low 2 bits */
value field0; /* initial contents of field 0 */
};
struct trail_block {
struct trail_block * previous;
struct trail_entry entries[ENTRIES_PER_TRAIL_BLOCK];
};
static struct trail_block extern_trail_first;
static struct trail_block * extern_trail_block;
static struct trail_entry * extern_trail_cur, * extern_trail_limit;
/* Stack for pending values to marshal */
struct extern_item { value * v; mlsize_t count; };
#define EXTERN_STACK_INIT_SIZE 256
#define EXTERN_STACK_MAX_SIZE (1024*1024*100)
static struct extern_item extern_stack_init[EXTERN_STACK_INIT_SIZE];
static struct extern_item * extern_stack = extern_stack_init;
static struct extern_item * extern_stack_limit = extern_stack_init
+ EXTERN_STACK_INIT_SIZE;
/* Forward declarations */
CAMLnoreturn_start
static void extern_out_of_memory(void)
CAMLnoreturn_end;
CAMLnoreturn_start
static void extern_invalid_argument(char *msg)
CAMLnoreturn_end;
CAMLnoreturn_start
static void extern_failwith(char *msg)
CAMLnoreturn_end;
CAMLnoreturn_start
static void extern_stack_overflow(void)
CAMLnoreturn_end;
static struct code_fragment * extern_find_code(char *addr);
static void extern_replay_trail(void);
static void free_extern_output(void);
/* Free the extern stack if needed */
static void extern_free_stack(void)
{
if (extern_stack != extern_stack_init) {
free(extern_stack);
/* Reinitialize the globals for next time around */
extern_stack = extern_stack_init;
extern_stack_limit = extern_stack + EXTERN_STACK_INIT_SIZE;
}
}
static struct extern_item * extern_resize_stack(struct extern_item * sp)
{
asize_t newsize = 2 * (extern_stack_limit - extern_stack);
asize_t sp_offset = sp - extern_stack;
struct extern_item * newstack;
if (newsize >= EXTERN_STACK_MAX_SIZE) extern_stack_overflow();
if (extern_stack == extern_stack_init) {
newstack = malloc(sizeof(struct extern_item) * newsize);
if (newstack == NULL) extern_stack_overflow();
memcpy(newstack, extern_stack_init,
sizeof(struct extern_item) * EXTERN_STACK_INIT_SIZE);
} else {
newstack =
realloc(extern_stack, sizeof(struct extern_item) * newsize);
if (newstack == NULL) extern_stack_overflow();
}
extern_stack = newstack;
extern_stack_limit = newstack + newsize;
return newstack + sp_offset;
}
/* Initialize the trail */
static void init_extern_trail(void)
{
extern_trail_block = &extern_trail_first;
extern_trail_cur = extern_trail_block->entries;
extern_trail_limit = extern_trail_block->entries + ENTRIES_PER_TRAIL_BLOCK;
}
/* Replay the trail, undoing the in-place modifications
performed on objects */
static void extern_replay_trail(void)
{
struct trail_block * blk, * prevblk;
struct trail_entry * ent, * lim;
blk = extern_trail_block;
lim = extern_trail_cur;
while (1) {
for (ent = &(blk->entries[0]); ent < lim; ent++) {
value obj = ent->obj;
color_t colornum = obj & 3;
obj = obj & ~3;
Hd_val(obj) = Coloredhd_hd(Hd_val(obj), colornum);
Field(obj, 0) = ent->field0;
}
if (blk == &extern_trail_first) break;
prevblk = blk->previous;
free(blk);
blk = prevblk;
lim = &(blk->entries[ENTRIES_PER_TRAIL_BLOCK]);
}
/* Protect against a second call to extern_replay_trail */
extern_trail_block = &extern_trail_first;
extern_trail_cur = extern_trail_block->entries;
}
/* Set forwarding pointer on an object and add corresponding entry
to the trail. */
static void extern_record_location(value obj)
{
header_t hdr;
if (extern_flags & NO_SHARING) return;
if (extern_trail_cur == extern_trail_limit) {
struct trail_block * new_block = malloc(sizeof(struct trail_block));
if (new_block == NULL) extern_out_of_memory();
new_block->previous = extern_trail_block;
extern_trail_block = new_block;
extern_trail_cur = extern_trail_block->entries;
extern_trail_limit = extern_trail_block->entries + ENTRIES_PER_TRAIL_BLOCK;
}
hdr = Hd_val(obj);
extern_trail_cur->obj = obj | Colornum_hd(hdr);
extern_trail_cur->field0 = Field(obj, 0);
extern_trail_cur++;
Hd_val(obj) = Bluehd_hd(hdr);
Field(obj, 0) = (value) obj_counter;
obj_counter++;
}
/* To buffer the output */
static char * extern_userprovided_output;
static char * extern_ptr, * extern_limit;
struct output_block {
struct output_block * next;
char * end;
char data[SIZE_EXTERN_OUTPUT_BLOCK];
};
static struct output_block * extern_output_first, * extern_output_block;
static void init_extern_output(void)
{
extern_userprovided_output = NULL;
extern_output_first = malloc(sizeof(struct output_block));
if (extern_output_first == NULL) caml_raise_out_of_memory();
extern_output_block = extern_output_first;
extern_output_block->next = NULL;
extern_ptr = extern_output_block->data;
extern_limit = extern_output_block->data + SIZE_EXTERN_OUTPUT_BLOCK;
}
static void close_extern_output(void)
{
if (extern_userprovided_output == NULL){
extern_output_block->end = extern_ptr;
}
}
static void free_extern_output(void)
{
struct output_block * blk, * nextblk;
if (extern_userprovided_output != NULL) return;
for (blk = extern_output_first; blk != NULL; blk = nextblk) {
nextblk = blk->next;
free(blk);
}
extern_output_first = NULL;
extern_free_stack();
}
static void grow_extern_output(intnat required)
{
struct output_block * blk;
intnat extra;
if (extern_userprovided_output != NULL) {
extern_failwith("Marshal.to_buffer: buffer overflow");
}
extern_output_block->end = extern_ptr;
if (required <= SIZE_EXTERN_OUTPUT_BLOCK / 2)
extra = 0;
else
extra = required;
blk = malloc(sizeof(struct output_block) + extra);
if (blk == NULL) extern_out_of_memory();
extern_output_block->next = blk;
extern_output_block = blk;
extern_output_block->next = NULL;
extern_ptr = extern_output_block->data;
extern_limit = extern_output_block->data + SIZE_EXTERN_OUTPUT_BLOCK + extra;
}
static intnat extern_output_length(void)
{
struct output_block * blk;
intnat len;
if (extern_userprovided_output != NULL) {
return extern_ptr - extern_userprovided_output;
} else {
for (len = 0, blk = extern_output_first; blk != NULL; blk = blk->next)
len += blk->end - blk->data;
return len;
}
}
/* Exception raising, with cleanup */
static void extern_out_of_memory(void)
{
extern_replay_trail();
free_extern_output();
caml_raise_out_of_memory();
}
static void extern_invalid_argument(char *msg)
{
extern_replay_trail();
free_extern_output();
caml_invalid_argument(msg);
}
static void extern_failwith(char *msg)
{
extern_replay_trail();
free_extern_output();
caml_failwith(msg);
}
static void extern_stack_overflow(void)
{
caml_gc_message (0x04, "Stack overflow in marshaling value\n", 0);
extern_replay_trail();
free_extern_output();
caml_raise_out_of_memory();
}
/* Conversion to big-endian */
static inline void store16(char * dst, int n)
{
dst[0] = n >> 8; dst[1] = n;
}
static inline void store32(char * dst, intnat n)
{
dst[0] = n >> 24; dst[1] = n >> 16; dst[2] = n >> 8; dst[3] = n;
}
static inline void store64(char * dst, int64_t n)
{
dst[0] = n >> 56; dst[1] = n >> 48; dst[2] = n >> 40; dst[3] = n >> 32;
dst[4] = n >> 24; dst[5] = n >> 16; dst[6] = n >> 8; dst[7] = n;
}
/* Write characters, integers, and blocks in the output buffer */
static inline void write(int c)
{
if (extern_ptr >= extern_limit) grow_extern_output(1);
*extern_ptr++ = c;
}
static void writeblock(char * data, intnat len)
{
if (extern_ptr + len > extern_limit) grow_extern_output(len);
memcpy(extern_ptr, data, len);
extern_ptr += len;
}
static inline void writeblock_float8(double * data, intnat ndoubles)
{
#if ARCH_FLOAT_ENDIANNESS == 0x01234567 || ARCH_FLOAT_ENDIANNESS == 0x76543210
writeblock((char *) data, ndoubles * 8);
#else
caml_serialize_block_float_8(data, ndoubles);
#endif
}
static void writecode8(int code, intnat val)
{
if (extern_ptr + 2 > extern_limit) grow_extern_output(2);
extern_ptr[0] = code;
extern_ptr[1] = val;
extern_ptr += 2;
}
static void writecode16(int code, intnat val)
{
if (extern_ptr + 3 > extern_limit) grow_extern_output(3);
extern_ptr[0] = code;
store16(extern_ptr + 1, val);
extern_ptr += 3;
}
static void writecode32(int code, intnat val)
{
if (extern_ptr + 5 > extern_limit) grow_extern_output(5);
extern_ptr[0] = code;
store32(extern_ptr + 1, val);
extern_ptr += 5;
}
#ifdef ARCH_SIXTYFOUR
static void writecode64(int code, intnat val)
{
if (extern_ptr + 9 > extern_limit) grow_extern_output(9);
extern_ptr[0] = code;
store64(extern_ptr + 1, val);
extern_ptr += 9;
}
#endif
/* Marshal the given value in the output buffer */
static void extern_rec(value v)
{
struct code_fragment * cf;
struct extern_item * sp;
sp = extern_stack;
while(1) {
if (Is_long(v)) {
intnat n = Long_val(v);
if (n >= 0 && n < 0x40) {
write(PREFIX_SMALL_INT + n);
} else if (n >= -(1 << 7) && n < (1 << 7)) {
writecode8(CODE_INT8, n);
} else if (n >= -(1 << 15) && n < (1 << 15)) {
writecode16(CODE_INT16, n);
#ifdef ARCH_SIXTYFOUR
} else if (n < -((intnat)1 << 30) || n >= ((intnat)1 << 30)) {
if (extern_flags & COMPAT_32)
extern_failwith("output_value: integer cannot be read back on "
"32-bit platform");
writecode64(CODE_INT64, n);
#endif
} else
writecode32(CODE_INT32, n);
goto next_item;
}
if (Is_in_value_area(v)) {
header_t hd = Hd_val(v);
tag_t tag = Tag_hd(hd);
mlsize_t sz = Wosize_hd(hd);
if (tag == Forward_tag) {
value f = Forward_val (v);
if (Is_block (f)
&& (!Is_in_value_area(f) || Tag_val (f) == Forward_tag
|| Tag_val (f) == Lazy_tag || Tag_val (f) == Double_tag)){
/* Do not short-circuit the pointer. */
}else{
v = f;
continue;
}
}
/* Atoms are treated specially for two reasons: they are not allocated
in the externed block, and they are automatically shared. */
if (sz == 0) {
if (tag < 16) {
write(PREFIX_SMALL_BLOCK + tag);
} else {
writecode32(CODE_BLOCK32, hd);
}
goto next_item;
}
/* Check if already seen */
if (Color_hd(hd) == Caml_blue) {
uintnat d = obj_counter - (uintnat) Field(v, 0);
if (d < 0x100) {
writecode8(CODE_SHARED8, d);
} else if (d < 0x10000) {
writecode16(CODE_SHARED16, d);
#ifdef ARCH_SIXTYFOUR
} else if (d >= (uintnat)1 << 32) {
writecode64(CODE_SHARED64, d);
#endif
} else {
writecode32(CODE_SHARED32, d);
}
goto next_item;
}
/* Output the contents of the object */
switch(tag) {
case String_tag: {
mlsize_t len = caml_string_length(v);
if (len < 0x20) {
write(PREFIX_SMALL_STRING + len);
} else if (len < 0x100) {
writecode8(CODE_STRING8, len);
} else {
#ifdef ARCH_SIXTYFOUR
if (len > 0xFFFFFB && (extern_flags & COMPAT_32))
extern_failwith("output_value: string cannot be read back on "
"32-bit platform");
if (len < (uintnat)1 << 32)
writecode32(CODE_STRING32, len);
else
writecode64(CODE_STRING64, len);
#else
writecode32(CODE_STRING32, len);
#endif
}
writeblock(String_val(v), len);
size_32 += 1 + (len + 4) / 4;
size_64 += 1 + (len + 8) / 8;
extern_record_location(v);
break;
}
case Double_tag: {
if (sizeof(double) != 8)
extern_invalid_argument("output_value: non-standard floats");
write(CODE_DOUBLE_NATIVE);
writeblock_float8((double *) v, 1);
size_32 += 1 + 2;
size_64 += 1 + 1;
extern_record_location(v);
break;
}
case Double_array_tag: {
mlsize_t nfloats;
if (sizeof(double) != 8)
extern_invalid_argument("output_value: non-standard floats");
nfloats = Wosize_val(v) / Double_wosize;
if (nfloats < 0x100) {
writecode8(CODE_DOUBLE_ARRAY8_NATIVE, nfloats);
} else {
#ifdef ARCH_SIXTYFOUR
if (nfloats > 0x1FFFFF && (extern_flags & COMPAT_32))
extern_failwith("output_value: float array cannot be read back on "
"32-bit platform");
if (nfloats < (uintnat) 1 << 32)
writecode32(CODE_DOUBLE_ARRAY32_NATIVE, nfloats);
else
writecode64(CODE_DOUBLE_ARRAY64_NATIVE, nfloats);
#else
writecode32(CODE_DOUBLE_ARRAY32_NATIVE, nfloats);
#endif
}
writeblock_float8((double *) v, nfloats);
size_32 += 1 + nfloats * 2;
size_64 += 1 + nfloats;
extern_record_location(v);
break;
}
case Abstract_tag:
extern_invalid_argument("output_value: abstract value (Abstract)");
break;
case Infix_tag:
writecode32(CODE_INFIXPOINTER, Infix_offset_hd(hd));
v = v - Infix_offset_hd(hd); /* PR#5772 */
continue;
case Custom_tag: {
uintnat sz_32, sz_64;
char * ident = Custom_ops_val(v)->identifier;
void (*serialize)(value v, uintnat * bsize_32,
uintnat * bsize_64)
= Custom_ops_val(v)->serialize;
if (serialize == NULL)
extern_invalid_argument("output_value: abstract value (Custom)");
write(CODE_CUSTOM);
writeblock(ident, strlen(ident) + 1);
Custom_ops_val(v)->serialize(v, &sz_32, &sz_64);
size_32 += 2 + ((sz_32 + 3) >> 2); /* header + ops + data */
size_64 += 2 + ((sz_64 + 7) >> 3);
extern_record_location(v);
break;
}
default: {
value field0;
if (tag < 16 && sz < 8) {
write(PREFIX_SMALL_BLOCK + tag + (sz << 4));
} else {
#ifdef ARCH_SIXTYFOUR
if (sz > 0x3FFFFF && (extern_flags & COMPAT_32))
extern_failwith("output_value: array cannot be read back on "
"32-bit platform");
if (hd < (uintnat)1 << 32)
writecode32(CODE_BLOCK32, Whitehd_hd (hd));
else
writecode64(CODE_BLOCK64, Whitehd_hd (hd));
#else
writecode32(CODE_BLOCK32, Whitehd_hd (hd));
#endif
}
size_32 += 1 + sz;
size_64 += 1 + sz;
field0 = Field(v, 0);
extern_record_location(v);
/* Remember that we still have to serialize fields 1 ... sz - 1 */
if (sz > 1) {
sp++;
if (sp >= extern_stack_limit) sp = extern_resize_stack(sp);
sp->v = &Field(v,1);
sp->count = sz-1;
}
/* Continue serialization with the first field */
v = field0;
continue;
}
}
}
else if ((cf = extern_find_code((char *) v)) != NULL) {
if ((extern_flags & CLOSURES) == 0)
extern_invalid_argument("output_value: functional value");
writecode32(CODE_CODEPOINTER, (char *) v - cf->code_start);
writeblock((char *) cf->digest, 16);
} else {
extern_invalid_argument("output_value: abstract value (outside heap)");
}
next_item:
/* Pop one more item to marshal, if any */
if (sp == extern_stack) {
/* We are done. Cleanup the stack and leave the function */
extern_free_stack();
return;
}
v = *((sp->v)++);
if (--(sp->count) == 0) sp--;
}
/* Never reached as function leaves with return */
}
static int extern_flag_values[] = { NO_SHARING, CLOSURES, COMPAT_32 };
static intnat extern_value(value v, value flags,
/*out*/ char header[32],
/*out*/ int * header_len)
{
intnat res_len;
/* Parse flag list */
extern_flags = caml_convert_flag_list(flags, extern_flag_values);
/* Initializations */
init_extern_trail();
obj_counter = 0;
size_32 = 0;
size_64 = 0;
/* Marshal the object */
extern_rec(v);
/* Record end of output */
close_extern_output();
/* Undo the modifications done on externed blocks */
extern_replay_trail();
/* Write the header */
res_len = extern_output_length();
#ifdef ARCH_SIXTYFOUR
if (res_len >= ((intnat)1 << 32) ||
size_32 >= ((intnat)1 << 32) || size_64 >= ((intnat)1 << 32)) {
/* The object is too big for the small header format.
Fail if we are in compat32 mode, or use big header. */
if (extern_flags & COMPAT_32) {
free_extern_output();
caml_failwith("output_value: object too big to be read back on "
"32-bit platform");
}
store32(header, Intext_magic_number_big);
store32(header + 4, 0);
store64(header + 8, res_len);
store64(header + 16, obj_counter);
store64(header + 24, size_64);
*header_len = 32;
return res_len;
}
#endif
/* Use the small header format */
store32(header, Intext_magic_number_small);
store32(header + 4, res_len);
store32(header + 8, obj_counter);
store32(header + 12, size_32);
store32(header + 16, size_64);
*header_len = 20;
return res_len;
}
void caml_output_val(struct channel *chan, value v, value flags)
{
char header[32];
int header_len;
struct output_block * blk, * nextblk;
if (! caml_channel_binary_mode(chan))
caml_failwith("output_value: not a binary channel");
init_extern_output();
extern_value(v, flags, header, &header_len);
/* During [caml_really_putblock], concurrent [caml_output_val] operations
can take place (via signal handlers or context switching in systhreads),
and [extern_output_first] may change. So, save it in a local variable. */
blk = extern_output_first;
caml_really_putblock(chan, header, header_len);
while (blk != NULL) {
caml_really_putblock(chan, blk->data, blk->end - blk->data);
nextblk = blk->next;
free(blk);
blk = nextblk;
}
}
CAMLprim value caml_output_value(value vchan, value v, value flags)
{
CAMLparam3 (vchan, v, flags);
struct channel * channel = Channel(vchan);
Lock(channel);
caml_output_val(channel, v, flags);
Unlock(channel);
CAMLreturn (Val_unit);
}
CAMLprim value caml_output_value_to_string(value v, value flags)
{
char header[32];
int header_len;
intnat data_len, ofs;
value res;
struct output_block * blk, * nextblk;
init_extern_output();
data_len = extern_value(v, flags, header, &header_len);
/* PR#4030: it is prudent to save extern_output_first before allocating
the result, as in caml_output_val */
blk = extern_output_first;
res = caml_alloc_string(header_len + data_len);
ofs = 0;
memcpy(&Byte(res, ofs), header, header_len);
ofs += header_len;
while (blk != NULL) {
int n = blk->end - blk->data;
memcpy(&Byte(res, ofs), blk->data, n);
ofs += n;
nextblk = blk->next;
free(blk);
blk = nextblk;
}
return res;
}
CAMLexport intnat caml_output_value_to_block(value v, value flags,
char * buf, intnat len)
{
char header[32];
int header_len;
intnat data_len;
/* At this point we don't know the size of the header.
Guess that it is small, and fix up later if not. */
extern_userprovided_output = buf + 20;
extern_ptr = extern_userprovided_output;
extern_limit = buf + len;
data_len = extern_value(v, flags, header, &header_len);
if (header_len != 20) {
/* Bad guess! Need to shift the output to make room for big header.
Make sure there is room. */
if (header_len + data_len > len)
caml_failwith("Marshal.to_buffer: buffer overflow");
memmove(buf + header_len, buf + 20, data_len);
}
memcpy(buf, header, header_len);
return header_len + data_len;
}
CAMLprim value caml_output_value_to_buffer(value buf, value ofs, value len,
value v, value flags)
{
intnat l =
caml_output_value_to_block(v, flags,
&Byte(buf, Long_val(ofs)), Long_val(len));
return Val_long(l);
}
CAMLexport void caml_output_value_to_malloc(value v, value flags,
/*out*/ char ** buf,
/*out*/ intnat * len)
{
char header[32];
int header_len;
intnat data_len;
char * res;
struct output_block * blk;
init_extern_output();
data_len = extern_value(v, flags, header, &header_len);
res = malloc(header_len + data_len);
if (res == NULL) extern_out_of_memory();
*buf = res;
*len = header_len + data_len;
memcpy(res, header, header_len);
res += header_len;
for (blk = extern_output_first; blk != NULL; blk = blk->next) {
int n = blk->end - blk->data;
memcpy(res, blk->data, n);
res += n;
}
free_extern_output();
}
/* Functions for writing user-defined marshallers */
CAMLexport void caml_serialize_int_1(int i)
{
if (extern_ptr + 1 > extern_limit) grow_extern_output(1);
extern_ptr[0] = i;
extern_ptr += 1;
}
CAMLexport void caml_serialize_int_2(int i)
{
if (extern_ptr + 2 > extern_limit) grow_extern_output(2);
store16(extern_ptr, i);
extern_ptr += 2;
}
CAMLexport void caml_serialize_int_4(int32_t i)
{
if (extern_ptr + 4 > extern_limit) grow_extern_output(4);
store32(extern_ptr, i);
extern_ptr += 4;
}
CAMLexport void caml_serialize_int_8(int64_t i)
{
if (extern_ptr + 8 > extern_limit) grow_extern_output(8);
store64(extern_ptr, i);
extern_ptr += 8;
}
CAMLexport void caml_serialize_float_4(float f)
{
caml_serialize_block_4(&f, 1);
}
CAMLexport void caml_serialize_float_8(double f)
{
caml_serialize_block_float_8(&f, 1);
}
CAMLexport void caml_serialize_block_1(void * data, intnat len)
{
if (extern_ptr + len > extern_limit) grow_extern_output(len);
memcpy(extern_ptr, data, len);
extern_ptr += len;
}
CAMLexport void caml_serialize_block_2(void * data, intnat len)
{
if (extern_ptr + 2 * len > extern_limit) grow_extern_output(2 * len);
#ifndef ARCH_BIG_ENDIAN
{
unsigned char * p;
char * q;
for (p = data, q = extern_ptr; len > 0; len--, p += 2, q += 2)
Reverse_16(q, p);
extern_ptr = q;
}
#else
memcpy(extern_ptr, data, len * 2);
extern_ptr += len * 2;
#endif
}
CAMLexport void caml_serialize_block_4(void * data, intnat len)
{
if (extern_ptr + 4 * len > extern_limit) grow_extern_output(4 * len);
#ifndef ARCH_BIG_ENDIAN
{
unsigned char * p;
char * q;
for (p = data, q = extern_ptr; len > 0; len--, p += 4, q += 4)
Reverse_32(q, p);
extern_ptr = q;
}
#else
memcpy(extern_ptr, data, len * 4);
extern_ptr += len * 4;
#endif
}
CAMLexport void caml_serialize_block_8(void * data, intnat len)
{
if (extern_ptr + 8 * len > extern_limit) grow_extern_output(8 * len);
#ifndef ARCH_BIG_ENDIAN
{
unsigned char * p;
char * q;
for (p = data, q = extern_ptr; len > 0; len--, p += 8, q += 8)
Reverse_64(q, p);
extern_ptr = q;
}
#else
memcpy(extern_ptr, data, len * 8);
extern_ptr += len * 8;
#endif
}
CAMLexport void caml_serialize_block_float_8(void * data, intnat len)
{
if (extern_ptr + 8 * len > extern_limit) grow_extern_output(8 * len);
#if ARCH_FLOAT_ENDIANNESS == 0x01234567
memcpy(extern_ptr, data, len * 8);
extern_ptr += len * 8;
#elif ARCH_FLOAT_ENDIANNESS == 0x76543210
{
unsigned char * p;
char * q;
for (p = data, q = extern_ptr; len > 0; len--, p += 8, q += 8)
Reverse_64(q, p);
extern_ptr = q;
}
#else
{
unsigned char * p;
char * q;
for (p = data, q = extern_ptr; len > 0; len--, p += 8, q += 8)
Permute_64(q, 0x01234567, p, ARCH_FLOAT_ENDIANNESS);
extern_ptr = q;
}
#endif
}
/* Find where a code pointer comes from */
static struct code_fragment * extern_find_code(char *addr)
{
int i;
for (i = caml_code_fragments_table.size - 1; i >= 0; i--) {
struct code_fragment * cf = caml_code_fragments_table.contents[i];
if (! cf->digest_computed) {
caml_md5_block(cf->digest, cf->code_start, cf->code_end - cf->code_start);
cf->digest_computed = 1;
}
if (cf->code_start <= addr && addr < cf->code_end) return cf;
}
return NULL;
}