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