#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 * . */ #include /******************************** * Compatability stuff. ********************************/ #if __STDC_VERSION__ >= 199101L # define UNS_INLINE inline # include #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 # include # 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 # 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