182 lines
4.1 KiB
C
182 lines
4.1 KiB
C
/* 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
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* TODO: add support for freestanding */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#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;
|
|
}
|