271 lines
7.6 KiB
C
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
|