/* fmtscheme.c: SCHEME OBJECT FORMAT IMPLEMENTATION
*
* $Id: //info.ravenbrook.com/project/mps/custom/cet/main/code/fmtscheme.c#2 $
* Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license.
*/
#include <string.h>
#include "fmtscheme.h"
#include "testlib.h"
/* special objects */
static obj_t obj_true; /* #t, boolean true */
static obj_t obj_false; /* #f, boolean false */
/* MPS globals */
mps_arena_t scheme_arena; /* the arena */
mps_pool_t obj_pool; /* pool for ordinary Scheme objects */
mps_ap_t obj_ap; /* allocation point used to allocate objects */
/* make_* -- object constructors */
#define ALIGNMENT sizeof(mps_word_t)
/* Align size upwards to the next multiple of the word size. */
#define ALIGN_WORD(size) \
(((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
/* Align size upwards to the next multiple of the word size, and
* additionally ensure that it's big enough to store a forwarding
* pointer. Evaluates its argument twice. */
#define ALIGN_OBJ(size) \
(ALIGN_WORD(size) >= ALIGN_WORD(sizeof(fwd_s)) \
? ALIGN_WORD(size) \
: ALIGN_WORD(sizeof(fwd_s)))
obj_t scheme_make_bool(int condition)
{
return condition ? obj_true : obj_false;
}
obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(pair_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_pair");
obj = addr;
obj->pair.type = TYPE_PAIR;
CAR(obj) = car;
CDR(obj) = cdr;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_integer(mps_ap_t ap, long integer)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(integer_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_integer");
obj = addr;
obj->integer.type = TYPE_INTEGER;
obj->integer.integer = integer;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[])
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(offsetof(symbol_s, string) + length+1);
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_symbol");
obj = addr;
obj->symbol.type = TYPE_SYMBOL;
obj->symbol.length = length;
memcpy(obj->symbol.string, string, length+1);
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[])
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(offsetof(string_s, string) + length+1);
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_string");
obj = addr;
obj->string.type = TYPE_STRING;
obj->string.length = length;
if (string) memcpy(obj->string.string, string, length+1);
else memset(obj->string.string, 0, length+1);
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_special(mps_ap_t ap, char *string)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(special_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_special");
obj = addr;
obj->special.type = TYPE_SPECIAL;
obj->special.name = string;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_operator(mps_ap_t ap, char *name,
entry_t entry, obj_t arguments,
obj_t body, obj_t env, obj_t op_env)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(operator_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_operator");
obj = addr;
obj->operator.type = TYPE_OPERATOR;
obj->operator.name = name;
obj->operator.entry = entry;
obj->operator.arguments = arguments;
obj->operator.body = body;
obj->operator.env = env;
obj->operator.op_env = op_env;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream)
{
mps_addr_t port_ref;
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(port_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_port");
obj = addr;
obj->port.type = TYPE_PORT;
obj->port.name = name;
obj->port.stream = stream;
} while(!mps_commit(ap, addr, size));
port_ref = obj;
mps_finalize(scheme_arena, &port_ref);
return obj;
}
obj_t scheme_make_character(mps_ap_t ap, char c)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(sizeof(character_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_character");
obj = addr;
obj->character.type = TYPE_CHARACTER;
obj->character.c = c;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(offsetof(vector_s, vector) + length * sizeof(obj_t));
do {
size_t i;
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_vector");
obj = addr;
obj->vector.type = TYPE_VECTOR;
obj->vector.length = length;
for(i = 0; i < length; ++i)
obj->vector.vector[i] = fill;
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_buckets(mps_ap_t ap, size_t length)
{
obj_t obj;
mps_addr_t addr;
size_t size = ALIGN_OBJ(offsetof(buckets_s, bucket) + length * sizeof(obj->buckets.bucket[0]));
do {
size_t i;
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_buckets");
obj = addr;
obj->buckets.type = TYPE_BUCKETS;
obj->buckets.length = length;
obj->buckets.used = 0;
obj->buckets.deleted = 0;
for(i = 0; i < length; ++i) {
obj->buckets.bucket[i].key = NULL;
obj->buckets.bucket[i].value = NULL;
}
} while(!mps_commit(ap, addr, size));
return obj;
}
obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf, cmp_t cmpf)
{
obj_t obj;
mps_addr_t addr;
size_t l, size = ALIGN_OBJ(sizeof(table_s));
do {
mps_res_t res = mps_reserve(&addr, ap, size);
if (res != MPS_RES_OK) error("out of memory in make_table");
obj = addr;
obj->table.type = TYPE_TABLE;
obj->table.buckets = NULL;
} while(!mps_commit(ap, addr, size));
obj->table.hash = hashf;
obj->table.cmp = cmpf;
/* round up to next power of 2 */
for(l = 1; l < length; l *= 2);
obj->table.buckets = scheme_make_buckets(ap, l);
mps_ld_reset(&obj->table.ld, scheme_arena);
return obj;
}
/* MPS Format */
static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
{
#define FIX(ref) \
do { \
mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
mps_res_t res = MPS_FIX12(ss, &_addr); \
if (res != MPS_RES_OK) return res; \
(ref) = _addr; \
} while(0)
MPS_SCAN_BEGIN(ss) {
while (base < limit) {
obj_t obj = base;
switch (TYPE(obj)) {
case TYPE_PAIR:
case TYPE_PROMISE:
FIX(CAR(obj));
FIX(CDR(obj));
base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
break;
case TYPE_INTEGER:
base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
break;
case TYPE_SYMBOL:
base = (char *)base +
ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
break;
case TYPE_SPECIAL:
base = (char *)base + ALIGN_OBJ(sizeof(special_s));
break;
case TYPE_OPERATOR:
FIX(obj->operator.arguments);
FIX(obj->operator.body);
FIX(obj->operator.env);
FIX(obj->operator.op_env);
base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
break;
case TYPE_STRING:
base = (char *)base +
ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
break;
case TYPE_PORT:
FIX(obj->port.name);
base = (char *)base + ALIGN_OBJ(sizeof(port_s));
break;
case TYPE_CHARACTER:
base = (char *)base + ALIGN_OBJ(sizeof(character_s));
break;
case TYPE_VECTOR:
{
size_t i;
for (i = 0; i < obj->vector.length; ++i)
FIX(obj->vector.vector[i]);
}
base = (char *)base +
ALIGN_OBJ(offsetof(vector_s, vector) +
obj->vector.length * sizeof(obj->vector.vector[0]));
break;
case TYPE_BUCKETS:
{
size_t i;
for (i = 0; i < obj->buckets.length; ++i) {
FIX(obj->buckets.bucket[i].key);
FIX(obj->buckets.bucket[i].value);
}
}
base = (char *)base +
ALIGN_OBJ(offsetof(buckets_s, bucket) +
obj->buckets.length * sizeof(obj->buckets.bucket[0]));
break;
case TYPE_TABLE:
FIX(obj->table.buckets);
base = (char *)base + ALIGN_OBJ(sizeof(table_s));
break;
case TYPE_FWD2:
base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
break;
case TYPE_FWD:
base = (char *)base + ALIGN_WORD(obj->fwd.size);
break;
case TYPE_PAD1:
base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
break;
case TYPE_PAD:
base = (char *)base + ALIGN_WORD(obj->pad.size);
break;
default:
error("Unexpected object on the heap\n");
return MPS_RES_FAIL;
}
}
} MPS_SCAN_END(ss);
return MPS_RES_OK;
}
static mps_addr_t obj_skip(mps_addr_t base)
{
obj_t obj = base;
switch (TYPE(obj)) {
case TYPE_PAIR:
case TYPE_PROMISE:
base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
break;
case TYPE_INTEGER:
base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
break;
case TYPE_SYMBOL:
base = (char *)base +
ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
break;
case TYPE_SPECIAL:
base = (char *)base + ALIGN_OBJ(sizeof(special_s));
break;
case TYPE_OPERATOR:
base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
break;
case TYPE_STRING:
base = (char *)base +
ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
break;
case TYPE_PORT:
base = (char *)base + ALIGN_OBJ(sizeof(port_s));
break;
case TYPE_CHARACTER:
base = (char *)base + ALIGN_OBJ(sizeof(character_s));
break;
case TYPE_VECTOR:
base = (char *)base +
ALIGN_OBJ(offsetof(vector_s, vector) +
obj->vector.length * sizeof(obj->vector.vector[0]));
break;
case TYPE_BUCKETS:
base = (char *)base +
ALIGN_OBJ(offsetof(buckets_s, bucket) +
obj->buckets.length * sizeof(obj->buckets.bucket[0]));
break;
case TYPE_TABLE:
base = (char *)base + ALIGN_OBJ(sizeof(table_s));
break;
case TYPE_FWD2:
base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
break;
case TYPE_FWD:
base = (char *)base + ALIGN_WORD(obj->fwd.size);
break;
case TYPE_PAD:
base = (char *)base + ALIGN_WORD(obj->pad.size);
break;
case TYPE_PAD1:
base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
break;
default:
error("Unexpected object on the heap\n");
return NULL;
}
return base;
}
static mps_addr_t obj_isfwd(mps_addr_t addr)
{
obj_t obj = addr;
switch (TYPE(obj)) {
case TYPE_FWD2:
return obj->fwd2.fwd;
case TYPE_FWD:
return obj->fwd.fwd;
default:
return NULL;
}
}
static void obj_fwd(mps_addr_t old, mps_addr_t new)
{
obj_t obj = old;
mps_addr_t limit = obj_skip(old);
size_t size = (size_t)((char *)limit - (char *)old);
cdie(size >= ALIGN_WORD(sizeof(fwd2_s)), "bad size in obj_fwd");
if (size == ALIGN_WORD(sizeof(fwd2_s))) {
TYPE(obj) = TYPE_FWD2;
obj->fwd2.fwd = new;
} else {
TYPE(obj) = TYPE_FWD;
obj->fwd.fwd = new;
obj->fwd.size = size;
}
}
static void obj_pad(mps_addr_t addr, size_t size)
{
obj_t obj = addr;
cdie(size >= ALIGN_WORD(sizeof(pad1_s)), "bad size in obj_pad");
if (size == ALIGN_WORD(sizeof(pad1_s))) {
TYPE(obj) = TYPE_PAD1;
} else {
TYPE(obj) = TYPE_PAD;
obj->pad.size = size;
}
}
void scheme_fmt(mps_fmt_t *fmt)
{
mps_res_t res;
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT);
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan);
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip);
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd);
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd);
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad);
res = mps_fmt_create_k(fmt, scheme_arena, args);
} MPS_ARGS_END(args);
if (res != MPS_RES_OK) error("Couldn't create obj format");
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2016 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/