rosella/rosella.c

287 lines
5.7 KiB
C

#include <sys/time.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "gc.h"
#include "builtin.h"
#include "interp.h"
static void test_builtins(void);
static void test_weak_boxes_and_wills(void);
static void test_garbage_collection(bool keep_going);
static void print_value(value_t v);
static void print_gc_stats(void);
static inline void comma(void) { fputs(", ", stdout); }
static inline void nl(void) { putchar('\n'); }
void out_of_memory(void)
{
fprintf(stderr, "Out of memory!\n\n");
print_gc_stats();
abort();
}
int main(int argc, char **argv)
{
srand((unsigned int)time(NULL));
gc_init(1024, 256*1024*1024);
builtin_init();
interpreter_init();
test_builtins();
test_weak_boxes_and_wills();
test_garbage_collection(argc > 1);
return 0;
}
static void test_builtins(void)
{
print_value(lookup_builtin(BI_STRUCTURE)); nl(); nl();
print_value(lookup_builtin(BI_TEMPLATE)); nl(); nl();
print_value(lookup_builtin(BI_LAMBDA)); nl(); nl();
}
static void print_weak_box_results(value_t box)
{
value_t v, f;
print_value(box); comma();
get_next_finalizer(&v, &f);
print_value(v); comma(); print_value(f); nl();
}
static void test_weak_boxes_and_wills(void)
{
gc_root_t box_root, tmp_root;
register_gc_root(&box_root, NIL);
register_gc_root(&tmp_root, NIL);
tmp_root.value = cons(make_fixnum(1), cons(make_fixnum(2), NIL));
box_root.value = make_weak_box(tmp_root.value);
register_finalizer(tmp_root.value, make_fixnum(10));
print_weak_box_results(box_root.value);
collect_garbage(0);
print_weak_box_results(box_root.value);
tmp_root.value = NIL;
print_weak_box_results(box_root.value);
collect_garbage(0);
print_weak_box_results(box_root.value);
collect_garbage(0);
print_weak_box_results(box_root.value);
unregister_gc_root(&box_root);
unregister_gc_root(&tmp_root);
}
static void test_garbage_collection(bool keep_going)
{
gc_root_t root;
int count = 0;
register_gc_root(&root, NIL);
while (1)
{
int r = rand() & 0xffff;
if (r == 0)
root.value = make_fixnum(rand());
else
{
switch (r & 7)
{
case 0:
root.value = cons(make_fixnum(rand()), root.value);
break;
case 1:
root.value = cons(root.value, make_byte_string(256, '\0'));
break;
case 2:
root.value = make_box(root.value);
break;
case 3:
root.value = cons(root.value, cons(make_fixnum(-1), NIL));
get_pair(get_pair(root.value)->cdr)->cdr = root.value;
break;
case 4:
case 5:
case 6:
case 7:
{
value_t s = make_struct(NIL, 4);
_get_struct(s)->slots[r & 3] = root.value;
root.value = s;
}
break;
}
}
if (++count >= 50000000)
{
nl();
print_gc_stats();
gc_stats.collections = 0;
gc_stats.total_ns = 0;
gc_stats.total_freed = 0;
gc_stats.high_water = 0;
gc_stats.max_ns = 0;
count = 0;
if (!keep_going)
break;
}
}
unregister_gc_root(&root);
}
static bool all_print(const char *s, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
if (s[i] == '"' || !isprint(s[i]))
return false;
}
return true;
}
static void print_value(value_t v)
{
if (v == NIL)
{
fputs("nil", stdout);
}
else if (v == FALSE_VALUE)
{
fputs("#f", stdout);
}
else if (v == TRUE_VALUE)
{
fputs("#t", stdout);
}
else if (is_fixnum(v))
{
printf("%d", (int)get_fixnum(v));
}
else if (is_box(v))
{
fputs("#&", stdout);
print_value(_get_box(v)->value);
}
else if (is_pair(v))
{
putchar('(');
print_value(_get_pair(v)->car);
v = _get_pair(v)->cdr;
while (is_pair(v))
{
putchar(' ');
print_value(_get_pair(v)->car);
v = _get_pair(v)->cdr;
}
if (v != NIL)
{
fputs(" . ", stdout);
print_value(v);
}
putchar(')');
}
else if (is_vector(v))
{
fputs("#(", stdout);
for (size_t i = 0; i < _get_vector(v)->size; ++i)
{
if (i != 0) putchar(' ');
print_value(_get_vector(v)->elements[i]);
}
putchar(')');
}
else if (is_byte_string(v))
{
byte_string_t *str = _get_byte_string(v);
if (all_print(str->bytes, str->size))
{
putchar('"');
fwrite(str->bytes, str->size, 1, stdout);
putchar('"');
}
else
{
fputs("#B(", stdout);
for (size_t i = 0; i < str->size; ++i)
{
if (i != 0) putchar(' ');
printf("%d", (int)str->bytes[i]);
}
putchar(')');
}
}
else if (is_struct(v))
{
struct_t *meta = get_struct(_get_struct(v)->type);
byte_string_t *str = get_byte_string(meta->slots[0]);
fputs("#S(", stdout);
fwrite(str->bytes, str->size, 1, stdout);
for (size_t i = 0; i < _get_struct(v)->nslots; ++i)
{
putchar(' ');
print_value(_get_struct(v)->slots[i]);
}
putchar(')');
}
else if (is_weak_box(v))
{
fputs("#W&", stdout);
print_value(_get_weak_box(v)->value);
}
else
{
fputs("#<unknown>", stdout);
}
}
static void print_gc_stats(void)
{
const double total_time = gc_stats.total_ns / 1.0e9;
const double max_time = gc_stats.max_ns / 1.0e9;
fprintf(stderr, "%lld bytes freed in %0.6f sec => %0.3f MB/sec. (%d GCs.)\n",
gc_stats.total_freed,
total_time,
(gc_stats.total_freed / total_time) / (1024*1024),
gc_stats.collections);
fprintf(stderr, "Max GC time was %0.6f sec, avg. %0.6f sec; peak heap size was %d bytes.\n",
max_time, (total_time / gc_stats.collections), gc_stats.high_water);
}
/* vim:set sw=2 expandtab: */