#include "cvm_core.h"
#include <stdlib.h>
#include <string.h>
#define CVM_ARENA_BLOCK (64 * 1024)
#define GC_HEADER_SZ 8
static void push_block(Arena *a, size_t min) {
size_t size = CVM_ARENA_BLOCK;
if (min > size) size = min;
ArenaBlock *b = (ArenaBlock *)malloc(sizeof(ArenaBlock));
if (!b) abort();
b->data = (unsigned char *)malloc(size + GC_HEADER_SZ);
if (!b->data) abort();
b->size = size;
b->used = 0;
b->next = a->head;
a->head = b;
}
void arena_init(Arena *a) {
a->head = NULL;
a->block_size = CVM_ARENA_BLOCK;
a->gc_bytes = 0;
a->gc_threshold = 65536;
}
void arena_destroy(Arena *a) {
ArenaBlock *b = a->head;
while (b) { ArenaBlock *n = b->next; free(b->data); free(b); b = n; }
a->head = NULL; a->gc_bytes = 0;
}
void arena_reset(Arena *a) { arena_destroy(a); a->gc_threshold = 65536; }
void *arena_alloc(Arena *a, size_t n) {
size_t align = (n + 7) & ~(size_t)7;
if (align == 0) align = 8;
size_t total = align + GC_HEADER_SZ;
if (a->head) {
ArenaBlock *b = a->head;
if (b->used + total <= b->size + GC_HEADER_SZ) {
uint32_t *hdr = (uint32_t *)(b->data + b->used);
hdr[0] = (uint32_t)align;
hdr[1] = 0;
void *p = (void *)(hdr + 2);
b->used += total;
a->gc_bytes += align;
return p;
}
}
push_block(a, total);
ArenaBlock *b = a->head;
uint32_t *hdr = (uint32_t *)b->data;
hdr[0] = (uint32_t)align;
hdr[1] = 0;
b->used = total;
a->gc_bytes += align;
return (void *)(hdr + 2);
}
char *arena_strndup(Arena *a, const char *s, size_t n) { char *p = (char *)arena_alloc(a, n + 1); if (n) memcpy(p, s, n); p[n] = '\0'; return p; }
char *arena_strdup(Arena *a, const char *s) { return arena_strndup(a, s, strlen(s)); }
void gc_collect(Runtime *rt) { if(rt->arena.head) rt->arena.gc_bytes=0; }
#if 0
for (ArenaBlock *b = head; b; b = b->next) {
if ((unsigned char *)p >= b->data && (unsigned char *)p < b->data + b->used) return 1;
}
return 0;
}
static uint32_t *gc_hdr(void *p) { return (uint32_t *)p - 2; }
static void *gc_fwd(void *p) { uint32_t *h = gc_hdr(p); return (void *)(uintptr_t)h[1]; }
static void gc_set_fwd(void *p, void *np) { uint32_t *h = gc_hdr(p); h[1] = (uint32_t)(uintptr_t)np; }
static void gtrace_value(Runtime*,Value*,ArenaBlock*,Arena*);
static void gtrace_env(Runtime*,Env*,ArenaBlock*,Arena*);
static void *gc_copy_obj(void *p, Arena *to, ArenaBlock *head) {
void *f = gc_fwd(p);
if (f) return f;
uint32_t *h = gc_hdr(p);
size_t sz = h[0] & 0x7FFFFFFF;
void *np = arena_alloc(to, sz);
memcpy(np, p, sz);
gc_set_fwd(p, np);
return np;
}
static void gtrace_ptr(Runtime *rt, void **pp, ArenaBlock *head, Arena *to) {
void *p = *pp;
if (!p || !gc_owned(head, p)) return;
*pp = gc_copy_obj(p, to, head);
}
static void gtrace_value(Runtime *rt, Value *v, ArenaBlock *head, Arena *to) {
switch (v->type) {
case VAL_STR: gtrace_ptr(rt, (void **)&v->as.str, head, to); break;
case VAL_ARRAY: {
void *old = v->as.arr;
if (!old || !gc_owned(head, old)) break;
void *np = gc_copy_obj(old, to, head);
v->as.arr = np;
Arr *arr = (Arr *)np;
for (int i = 0; i < arr_len(arr); i++) {
ArrEntry *e = arr_at(arr, i);
if (e) gtrace_value(rt, &e->value, head, to);
}
break;
}
case VAL_CLOSURE:
gtrace_ptr(rt, &v->as.closure, head, to);
break;
default: break;
}
}
static void gtrace_env(Runtime *rt, Env *e, ArenaBlock *head, Arena *to) {
if (!e) return;
Map *m = &e->vars;
for (int i = 0; i < m->cap; i++) {
for (MapNode *n = m->buckets[i]; n; n = n->next) {
gtrace_ptr(rt, (void **)&n->key, head, to);
Value **vp = (Value **)&n->value;
gtrace_ptr(rt, (void **)vp, head, to);
if (*vp) gtrace_value(rt, *vp, head, to);
}
}
gtrace_env(rt, e->parent, head, to);
}
void gc_collect(Runtime *rt) {
if (!rt->arena.head) return;
rt->arena.gc_bytes = 0;
}
#endif
#if 0
#endif