Initial commit

This commit is contained in:
2025-08-28 04:59:23 -07:00
commit 5ac6aaf900
14 changed files with 4983 additions and 0 deletions

View File

@ -0,0 +1,99 @@
/**
* Replaceable memory allocator.
*/
#ifndef INCLUDED_REFCOUNT_ALLOCATOR_H
#define INCLUDED_REFCOUNT_ALLOCATOR_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* A set of the three main C standard allocators. Each member function should
* behave the same as its standard library counterpart. That is, for example,
* free should allow NULL as an input.
*/
typedef struct RefcountAllocator {
void *(*malloc)(size_t); //!< malloc implementation for the allocator.
void *(*realloc)(void *,
size_t); //!< realloc implementation for the allocator.
void (*free)(void *); //!< free implementation for the allocator.
} RefcountAllocator;
extern const RefcountAllocator *refcount_global_allocator;
/**
* Allocate memory using a #RefcountAllocator.
* @param alloc The allocator to use
* @param size The number of bytes to allocate
* @return The newly allocate pointer, or NULL if allocation failed
*/
static inline void *refcount_allocator_malloc(const RefcountAllocator *alloc,
size_t size) {
return alloc->malloc(size);
}
/**
* Reallocate memory using a #RefcountAllocator.
* @param alloc The allocator to use
* @param old_ptr The pointer to reallocate
* @param size The number of bytes to allocate
* @return The resized pointer, a newly allocated pointer, or NULL if
* allocation failed
*/
static inline void *refcount_allocator_realloc(const RefcountAllocator *alloc,
void *old_ptr, size_t size) {
return alloc->realloc(old_ptr, size);
}
/**
* Free memory using a #RefcountAllocator.
* @param alloc The allocator to use
* @param ptr The pointer to free
*/
static inline void refcount_allocator_free(const RefcountAllocator *alloc,
void *ptr) {
alloc->free(ptr);
}
/**
* Allocate memory using the global #RefcountAllocator.
* @param size The number of bytes to allocate
* @return The newly allocate pointer, or NULL if allocation failed
*
* @see refcount_allocator_malloc
*/
static inline void *refcount_malloc(size_t size) {
return refcount_allocator_malloc(refcount_global_allocator, size);
}
/**
* Reallocate memory using the global #RefcountAllocator.
* @param old_ptr The pointer to reallocate
* @param size The number of bytes to allocate
* @return The resized pointer, a newly allocated pointer, or NULL if
* allocation failed
*
* @see refcount_allocator_realloc
*/
static inline void *refcount_realloc(void *old_ptr, size_t size) {
return refcount_allocator_realloc(refcount_global_allocator, old_ptr, size);
}
/**
* Free memory using the global #RefcountAllocator.
* @param ptr The pointer to free
*
* @see refcount_allocator_free
*/
static inline void refcount_free(void *ptr) {
refcount_allocator_free(refcount_global_allocator, ptr);
}
#ifdef __cplusplus
}
#endif
#endif

91
include/refcount/list.h Normal file
View File

@ -0,0 +1,91 @@
/**
* Doubly linked list implementation.
*/
#ifndef INCLUDED_REFCOUNT_LIST_H
#define INCLUDED_REFCOUNT_LIST_H
#include <refcount/allocator.h>
#include <stddef.h> // for NULL
#ifdef __cplusplus
extern "C" {
#endif
/**
* Linked list type with double linkage. To create an empty list, set it to
* NULL. This is not an opaque type and consumers of this file are free to
* access its fields.
*/
typedef struct RefcountList {
struct RefcountList *next; //!< The next link in the list, or NULL.
struct RefcountList *prev; //!< The previous link in the list, or NULL.
void *data; //!< The data of this link.
} RefcountList;
/**
* Callback to used to free list elements.
* @see refcount_list_free
*/
typedef void (*refcount_list_destroy_callback_t)(void *);
/**
* Callback to used when looping over the list with extra data.
* @see refcount_list_free_with_data
*/
typedef void (*refcount_list_foreach_callback_t)(void *, void *);
/**
* Callback used when copying elements from one list to another.
* @see refcount_list_copy
*/
typedef void *(*refcount_list_copy_callback_t)(void *);
RefcountList *refcount_list_build(int count, ...);
RefcountList *refcount_list_copy(RefcountList *list,
refcount_list_copy_callback_t callback);
RefcountList *refcount_list_reverse(RefcountList *list);
void refcount_list_free(RefcountList *list,
refcount_list_destroy_callback_t callback);
void refcount_list_free_with_data(RefcountList *list,
refcount_list_foreach_callback_t callback,
void *data);
size_t refcount_list_length(RefcountList *list);
/**
* Return the first element in a #RefcountList.
* @param list The list
* @return The first object in the list
*/
static inline void *refcount_list_peek(RefcountList *list) {
if (!list) {
return NULL;
}
return list->data;
}
RefcountList *refcount_list_drop(RefcountList *list, size_t n);
void *refcount_list_nth(RefcountList *list, size_t n);
RefcountList *refcount_list_pop(RefcountList *list,
refcount_list_destroy_callback_t callback);
RefcountList *refcount_list_remove(RefcountList *list, RefcountList *link,
refcount_list_destroy_callback_t callback);
RefcountList *refcount_list_push(RefcountList *list, void *element);
RefcountList *refcount_list_tail(RefcountList *list);
RefcountList *refcount_list_join(RefcountList *list1, RefcountList *list2);
RefcountList *refcount_list_push_back(RefcountList *list, void *element);
#ifdef __cplusplus
}
#endif
#endif

249
include/refcount/refcount.h Normal file
View File

@ -0,0 +1,249 @@
/**
* Reference counting subroutines.
*/
#ifndef INCLUDED_REFCOUNT_REFCOUNT_H
#define INCLUDED_REFCOUNT_REFCOUNT_H
#include <refcount/list.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Callback for listing all references held by the passed object. The function
* should take an objects and return a #RefcountList of references held by that
* object.
*/
typedef RefcountList *(*refcount_held_refs_callback_t)(void *);
/**
* Callback for freeing an object after its reference count drops to 0. It is
* safe the free the object from this callback.
*/
typedef void (*refcount_destroy_callback_t)(void *);
/**
* Context for reference counting a specific type of object. This should be
* considered opaque.
*/
typedef struct RefcountContext {
size_t entry_offset; //!< Offset to the #RefcountEntry member.
refcount_held_refs_callback_t
held_refs_callback; //!< Callback to list an object's held references.
refcount_destroy_callback_t
destroy_callback; //!< Callback to free an object
//!< after its reference count
//!< dropped to 0.
RefcountList *static_objects; //!< List of static objects registered with
//!< this context.
RefcountList *gc_roots; //!< List of garbage collector roots.
} RefcountContext;
/**
* Enumeration of operations that may cause the debug breakpoint to be called.
*/
typedef enum {
REFCOUNT_OPER_DEINIT_STATIC,
REFCOUNT_OPER_UNREF,
REFCOUNT_OPER_FLOAT,
} RefcountOperation;
/**
* Type for a function to be called when some kind of error occurs. It should
* take a #RefcountOperation, a #RefcountContext pointer, and the object on
* which the error occurred.
*/
typedef void (*refcount_debug_callback_t)(RefcountOperation, RefcountContext *,
void *);
extern refcount_debug_callback_t refcount_debug_breakpoint;
extern RefcountContext *refcount_default_context;
RefcountContext *
refcount_make_context(size_t entry_offset,
refcount_held_refs_callback_t held_refs_callback,
refcount_destroy_callback_t destroy_callback);
void refcount_context_destroy(RefcountContext *ctx);
/**
* Opaque struct holding reference data for an object. This should be included
* as a non-pointer member of the struct you are trying to track, e.g.
* @code
* struct A {
* int my_num;
* RefcountEntry refcount;
* char *my_string;
* };
* @endcode
*/
typedef struct RefcountEntry {
bool is_static; //!< Whether the object is static.
union {
struct {
uint64_t ref_count; //!< The object's reference count.
RefcountList *gc_root; //!< The object's GC root, or NULL.
};
RefcountList *static_entry; //!< The object's static list entry, never
//!< NULL for registed statics.
};
} RefcountEntry;
/**
* Return a pointer to the entry associated with an object.
* @param ctx The #RefcountContext
* @param obj The object
* @return A pointer to the objects #RefcountEntry
*/
#define REFCOUNT_OBJECT_ENTRY(ctx, obj) \
((RefcountEntry *) (((void *) (obj)) + (ctx)->entry_offset))
/**
* Test whether an object is static or not. The object must have been
* initialized before this, otherwise the results are undefined.
* @param ctx The #RefcountContext
* @param obj The object to test
* @return Whether or not the object is static
*/
static inline bool refcount_context_is_static(RefcountContext *ctx, void *obj) {
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->is_static;
}
/**
* Return the number of references an object currently has. This is not the
* number of references the object itself holds. The object must have been
* initialized before this, otherwise the results are undefined.
* @param ctx The #RefcountContext
* @param obj The object
* @return The number of references the object has
*/
static inline size_t refcount_context_num_refs(RefcountContext *ctx,
void *obj) {
return REFCOUNT_OBJECT_ENTRY(ctx, obj)->ref_count;
}
void refcount_context_init_obj(RefcountContext *ctx, void *obj);
void refcount_context_init_static(RefcountContext *ctx, void *obj);
void refcount_context_deinit_static(RefcountContext *ctx, void *obj);
/**
* Unregister a static object in a context. This is suitable to be passed as a
* callback to #refcount_list_free_with_data.
* @param ctx The #RefcountContext
* @param obj The object to unregister
*/
static inline void refcount_context_deinit_static_as_callback(void *obj,
void *ctx) {
refcount_context_deinit_static(ctx, obj);
}
void *refcount_context_ref(RefcountContext *ctx, void *obj);
void *refcount_context_unref(RefcountContext *ctx, void *obj);
/**
* Decrement the reference count of an object. This is suitable to be passed as
* a callback to #refcount_list_free_with_data.
* @param ctx The #RefcountContext
* @param obj The object
*/
static inline void refcount_context_unref_as_callback(void *obj, void *ctx) {
refcount_context_unref(ctx, obj);
}
void *refcount_context_float(RefcountContext *ctx, void *obj);
/**
* Same as #refcount_context_is_static, but only operates on the global
* context.
* @param obj The object to test
* @return Whether or not the object is static
*/
static inline bool refcount_is_static(void *obj) {
return refcount_context_is_static(refcount_default_context, obj);
}
/**
* Same as #refcount_context_num_refs, but only operates on the global
* context.
* @param obj The object
* @return The number of references the object has
*/
static inline size_t refcount_num_refs(void *obj) {
return refcount_context_num_refs(refcount_default_context, obj);
}
/**
* Same as #refcount_context_init_obj, but only operates on the global
* context.
* @param obj The object to initialize
*/
static inline void refcount_init_obj(void *obj) {
refcount_context_init_obj(refcount_default_context, obj);
}
/**
* Same as #refcount_context_init_static, but only operates on the global
* context.
* @param obj The object to register
*/
static inline void refcount_init_static(void *obj) {
refcount_context_init_static(refcount_default_context, obj);
}
/**
* Same as #refcount_context_deinit_static, but only operates on the global
* context.
* @param obj The object to unregister
*/
static inline void refcount_deinit_static(void *obj) {
refcount_context_deinit_static(refcount_default_context, obj);
}
/**
* Same as #refcount_context_ref, but only operates on the global context.
* @param obj The object to reference
* @return The input object
*/
static inline void *refcount_ref(void *obj) {
return refcount_context_ref(refcount_default_context, obj);
}
/**
* Same as #refcount_context_unref, but only operates on the global context.
* @param obj The object
* @return NULL if the object's reference counter fell to 0, otherwise the
* object
*/
static inline void *refcount_unref(void *obj) {
return refcount_context_unref(refcount_default_context, obj);
}
/**
* Call #refcount_unref on an object. This is suitable to pass to
* refcount_list_free.
* @param obj The object
*/
static inline void refcount_unref_as_callback(void *obj) {
refcount_unref(obj);
}
/**
* Same as #refcount_context_float, but only operates on the global context.
* @param obj The object
* @return The input object
*/
static inline void *refcount_float(void *obj) {
return refcount_context_float(refcount_default_context, obj);
}
#ifdef __cplusplus
}
#endif
#endif