universalservice/include/uns.h

271 lines
7.6 KiB
C

#ifndef UNIVERSAL_SERVICE_H
#define UNIVERSAL_SERVICE_H
/* 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/>.
*/
#include <stddef.h>
/********************************
* Compatability stuff.
********************************/
#if __STDC_VERSION__ >= 199101L
# define UNS_INLINE inline
# include <stdint.h>
#else
# define UNS_INLINE
# define SIZE_MAX ((size_t)0 - 1)
#endif
#if defined(__GNUC__)
# define UNS_UNUSED __attribute__((unused))
#else
# define UNS_UNUSED
#endif
#if !defined(UNS_WORD) && __STDC_VERSION__ >= 199101L
# include <stdint.h>
# include <limits.h>
# define UNS_WORD uintptr_t
# define UNS_SIGNED_WORD intptr_t
# define UNS_WORD_MAX UINTPTR_MAX
# define UNS_SWORD_MAX INTPTR_MAX
# define UNS_SWORD_MIN INTPTR_MIN
#elif !defined(UNS_WORD)
# include <limits.h>
# define UNS_WORD unsigned long
# define UNS_WORD_MAX ULONG_MAX
# define UNS_SIGNED_WORD long
# define UNS_SWORD_MAX LONG_MAX
# define UNS_SWORD_MIN LONG_MIN
#endif
typedef UNS_WORD uns_word;
typedef UNS_SIGNED_WORD uns_sword;
/** Type for a pointer to a region.
*
* This struct doesn't exist and is used to make a unique pointer for
* type checking.
*
* This pointer must be what was returned from an allocation (i.e. it
* points to the first byte but before any hidden header data).
*/
typedef struct uns_ptr *Uns_ptr;
/** Flags for bytes regions. */
enum uns_bytes_type {
UNS_NOEXEC = 0
};
/** Flags for record regions. */
enum uns_record_type {
UNS_POINTERS_ONLY = 0
};
/** Type of a value in a record. */
enum uns_fld_type {
UNS_POINTER,
UNS_INTEGER,
/** Value is a weak pointer. */
UNS_WEAK
};
/** Container that boxes pointers for the runtime.
*
* Used for
*
* 1) Store roots of the GC,
* 2) Store GC pointers used in C and possibly not pointed-to anywhere
* else in GC space,
* 3) Preserve the location of a pointer after it moves.
*/
struct uns_ctr {
/** Pointer to a region in the heap, or NULL.
*
* All roots must point into the same heap, or to NULL.
* Multiple roots are allowed to point to the same region.
*/
void *p;
/** This field is managed by the library.
* This must be set to NULL when initializing the container and
* must not be touched while the container is in the root list.
*/
struct uns_ctr *prev;
/** This field is managed by the library.
* This must be set to NULL when initializing the container and
* must not be touched while the container is in the root list.
*/
struct uns_ctr *next;
};
/** Opaque struct that stores the state of a garbage collected heap.
*
* The only thing that is stable about the layout of this struct is
* that the first field is ALWAYS a pointer to the collector context.
*
* The context pointer will always be the same pointer from init to
* deinit.
*/
typedef struct uns_gc *Uns_GC;
/** Size of `struct uns_gc` for allocation. */
extern const size_t uns_gc_size;
/** Get the context pointer for the collector. */
UNS_UNUSED static UNS_INLINE void *uns_ctx(Uns_GC gc)
{
return *((void **)gc);
}
/** Set the context pointer for the collector. */
UNS_UNUSED static UNS_INLINE void uns_set_ctx(Uns_GC gc, void *ctx)
{
*((void **)gc) = ctx;
}
/** Zero the memory of the generic GC struct. This will leak
* memory if a collector was initialized and not deinitialized
* beforehand.
*
* This must be called when the structure is allocated.
*/
void uns_gc_zero(Uns_GC gc);
/** Free memory associated with the GC. It is OK to call this
* with a GC that has not been initialized.
*/
void uns_deinit(Uns_GC gc);
/** Set deinit function. */
void uns_set_deinit(Uns_GC gc, void(*deinit)(Uns_GC));
/** Set OOM handler. */
void uns_set_oom(Uns_GC gc, void(*oom)(Uns_GC));
/** Call OOM handler. */
void uns_on_oom(Uns_GC gc);
/** Set garbage collection function. */
void uns_set_collect(Uns_GC gc, int(*collect)(Uns_GC));
/** Set allocation function. */
void uns_set_alloc(Uns_GC gc,
void*(*alloc)(Uns_GC, size_t, enum uns_bytes_type));
/** Set allocation function. */
void uns_set_alloc_rec(Uns_GC gc,
void*(*alloc_rec)(Uns_GC, size_t, enum uns_record_type));
/** Set record setter function. */
void uns_set_set(Uns_GC gc, void(*set)(Uns_ptr, size_t, enum uns_fld_type,
void*));
/** Set record getter function. */
void uns_set_get(Uns_GC gc, void*(*get)(Uns_ptr, size_t,
enum uns_fld_type *));
/** Force a collection. */
int uns_collect(Uns_GC gc);
/** Allocate a region of bytes.
*
* This function will only return NULL if:
*
* 1. The type of region requested in `typ` is not supported, or
* 2. The collector ran out of memory and the out-of-memory handler
* did not exit the program.
*
* Results are undefined if the type is not supported, but all
* allocators can be compiled with a mode to abort in those cases.
*/
void *uns_alloc(Uns_GC gc, size_t bytes, enum uns_bytes_type typ);
/** Allocate a record.
*
* This function will only return NULL if:
*
* 1. The type of region requested in `typ` is not supported, or
* 2. The collector ran out of memory and the out-of-memory handler
* did not exit the program.
*
* `len` is the number of elements in the record.
*/
void *uns_alloc_rec(Uns_GC gc, size_t len, enum uns_record_type typ);
/** Free a region.
*
* This is a no-op if freeing is not supported by the collector, or
* if `p` is NULL.
*/
void uns_free(Uns_GC gc, Uns_ptr p);
/** Get a field from a record.
*
* ## Parameters
* - `notnull gc`: GC data.
* - `notnull rec`: Pointer to a record.
* - `ind`: Index of the field in the record. This is not the byte
* offset.
* - `out typ`: What type of field (integer, weak, etc.) was contained
* in the field. May be NULL.
*/
void *uns_get(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type *typ);
/** Set a field in a record.
*
* ## Parameters
* - `notnull gc`: GC Data.
* - `notnull rec`: Pointer to a record.
* - `ind`: Index of the field in the record. This is not the byte
* offset.
* - `typ`: The type of record to store. Behavior is undefined if the
* field type is not supported, either by the record or by the heap.
* - `val`: Value to store.
*/
void uns_set(Uns_GC gc, Uns_ptr rec, size_t ind, enum uns_fld_type typ,
void *val);
/** Add a root to the GC.
*
* ## Parameters
* - `notnull gc`: GC data.
* - `notnull in out root`: Pointer to the container containing the root.
* Do not modify any other field in the container, except for p, after
* this function returns.
*/
void uns_root_add(Uns_GC gc, struct uns_ctr *root);
/** Remove a root from the GC.
*
* ## Parameters
* - `notnull gc`: GC data.
* - `notnull in out root`: Pointer to the container containing the
* root to be removed. If the root is not a part of any heap (the
* prev and next pointers are NULL), then this function is a noop.
* This function does not modify p.
*/
void uns_root_remove(Uns_GC gc, struct uns_ctr *root);
/** Get the head of the roots list.
*/
struct uns_ctr *uns_roots(Uns_GC gc);
#endif