#include #include #include #include "encoding.h" #include "heap.h" #include "panic.h" #include "value.h" #include "value_slice.h" struct heap new_heap(value_slice gc_roots) { return (struct heap) { .buf = NULL, .active = { .size = 0 }, .standby = { .size = 0 }, .free_ptr = 0, .last_free = 0, .gc_roots = gc_roots, }; } static bool forwarded(struct heap *h, value *p) { if (!is_pointer(*p)) { return false; } value *q = to_pointer(*p); return h->active.buf <= q && q <= h->active.buf + h->active.size; } static size_t alloc_size(value *p) { return p[-1] >> 1; } static value *simple_alloc(struct heap *h, int64_t size) { h->active.buf[h->free_ptr] = size << 1; value *p = h->active.buf + h->free_ptr + 1; h->free_ptr += size; return p; } static value process_gc_value(struct heap *h, value val) { if (!is_pointer(val)) { return val; } value *p = to_pointer(val); if (forwarded(h, p)) { return *p; } size_t size = alloc_size(p); // Allocate space in the new buffer. value *q = simple_alloc(h, size); // Write forwarding pointer. value first_value = p[0]; p[0] = from_pointer(q); // Copy values, recursively modifying pointers. q[0] = process_gc_value(h, first_value); for (size_t i = 1; i < size; i++) { q[i] = process_gc_value(h, p[i]); } return from_pointer(q); } static void collect_garbage(struct heap *h) { if (h->free_ptr < 2 * h->last_free) { return; } // Swap the heaps. value_slice temp = h->active; h->active = h->standby; h->standby = temp; h->free_ptr = 0; for (size_t i = 0; i < h->gc_roots.size; i++ ) { h->gc_roots.buf[i] = process_gc_value(h, h->gc_roots.buf[i]); } } static value_slice xcalloc(size_t size) { value *p = calloc(size, sizeof(value)); if (!p) { panicf("Out of memory!\n"); } return (value_slice) { .size = size, .buf = p }; } static void more_space(struct heap *h, int64_t size_hint) { size_t new_size = h->active.size * 2 + size_hint; value_slice new_buf = xcalloc(2 * new_size); memcpy(new_buf.buf, h->active.buf, h->active.size * sizeof(value)); free(h->buf); h->buf = new_buf.buf; h->active = value_slice2(new_buf, 0, new_size); h->standby = value_slice1(new_buf, new_size); } value *alloc(struct heap *h, int64_t size) { if (size <= 0) { panicf("Alloc of zero size!\n"); } collect_garbage(h); if (h->free_ptr + size >= h->active.size) { more_space(h, size); } value *p = simple_alloc(h, size); memset(p, 0, size * sizeof(value)); return p; }