Initial commit
This commit is contained in:
99
include/refcount/allocator.h
Normal file
99
include/refcount/allocator.h
Normal 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
91
include/refcount/list.h
Normal 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
249
include/refcount/refcount.h
Normal 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
|
Reference in New Issue
Block a user