universalservice/uns.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;
}