/* Copyright (C) 2024 Peter McGoron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see * . */ /* TODO: add support for freestanding */ #include #include #include #include "uns.h" /** Internal GC state. * * The exact structure is hidden from implementations. The function API * is stable(r). */ struct uns_gc { /** Context pointer used by specific collector implementations. */ void *ctx; /** Head of the roots linked list. */ struct uns_ctr *roots; /** Function called when OOM occurs. If not specified, the library * will print to standard error and exit. */ void (*oom)(struct uns_gc *); /** Implementations of allocator functions. */ void (*deinit)(Uns_GC); int (*collect)(Uns_GC); void *(*alloc)(Uns_GC, size_t, enum uns_bytes_type); void *(*alloc_rec)(Uns_GC, size_t, enum uns_record_type); void (*free)(Uns_GC, Uns_ptr); /** Generic implementation of record access. Values in a record may * have unused bits filled in so raw reads may not work correctly. * Values may also not be aligned correctly. The `get` and `set` * functions will handle all of this so the caller gets what is * expected. */ void (*set)(Uns_ptr, size_t, enum uns_fld_type, void *); void *(*get)(Uns_ptr, size_t, enum uns_fld_type *); }; const size_t uns_gc_size = sizeof(struct uns_gc); void uns_gc_zero(Uns_GC gc) { gc->ctx = NULL; gc->roots = NULL; gc->oom = NULL; gc->deinit = NULL; gc->collect = NULL; gc->alloc = NULL; gc->alloc_rec = NULL; gc->free = NULL; gc->set = NULL; gc->get = NULL; } /** Wrapper to define a setter for the generic state struct. */ #define SETTER(name, arg) \ void uns_set_##name(Uns_GC gc, arg) {gc->name = name;} SETTER(deinit, void(*deinit)(Uns_GC)) SETTER(oom, void(*oom)(Uns_GC)) SETTER(collect, int(*collect)(Uns_GC)) SETTER(alloc, void*(*alloc)(Uns_GC, size_t, enum uns_bytes_type)) SETTER(alloc_rec, void*(*alloc_rec)(Uns_GC, size_t, enum uns_record_type)) SETTER(free, void(*free)(Uns_GC, Uns_ptr)) SETTER(set, void(*set)(Uns_ptr, size_t, enum uns_fld_type, void*)) SETTER(get, void*(*get)(Uns_ptr, size_t, enum uns_fld_type *)) #undef SETTER void uns_deinit(Uns_GC gc) { if (gc->deinit) gc->deinit(gc); uns_gc_zero(gc); } int uns_collect(Uns_GC gc) { if (gc->collect) return gc->collect(gc); abort(); } void *uns_alloc(Uns_GC gc, size_t bytes, enum uns_bytes_type typ) { if (gc->alloc) return gc->alloc(gc, bytes, typ); abort(); } void *uns_alloc_rec(Uns_GC gc, size_t bytes, enum uns_record_type typ) { if (gc->alloc_rec) return gc->alloc_rec(gc, bytes, typ); abort(); } void uns_free(Uns_GC gc, Uns_ptr p) { if (gc->free) gc->free(gc, p); } void *uns_get(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type *typ) { if (gc->get) return gc->get(rec, ind, typ); else abort(); } void uns_set(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type typ, void *val) { if (gc->set) gc->set(rec, ind, typ, val); else abort(); } void uns_on_oom(Uns_GC gc) { if (gc->oom) { gc->oom(gc); } else { fprintf(stderr, "oom\n"); abort(); } } void uns_root_add(Uns_GC gc, struct uns_ctr *root) { root->prev = NULL; root->next = gc->roots; if (gc->roots) gc->roots->prev = root; gc->roots = root; } void uns_root_remove(Uns_GC gc, struct uns_ctr *root) { if (root->prev) { root->prev->next = root->next; } if (root->next) { root->next->prev = root->prev; } if (gc->roots == root) { assert(!root->prev); gc->roots = root->next; } root->prev = root->next = NULL; } struct uns_ctr *uns_roots(Uns_GC gc) { return gc->roots; }