Mercurial > lbo > hg > ylisp
changeset 185:db386ec98dc3
base/ptr: Add yptr_rc_t implementation
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 29 Sep 2019 14:42:18 +0200 |
parents | 263256062455 |
children | 60e493dce3a8 |
files | src/base/CMakeLists.txt src/base/ptr.c src/base/ptr.h src/base/ptr_test.c |
diffstat | 4 files changed, 134 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/base/CMakeLists.txt Sun Sep 29 14:14:04 2019 +0200 +++ b/src/base/CMakeLists.txt Sun Sep 29 14:42:18 2019 +0200 @@ -7,10 +7,16 @@ endif () ADD_LIBRARY(base STATIC + ptr.c str.c vec.c ) +# ptr test. +ADD_EXECUTABLE(ptr_test ptr_test.c) +TARGET_LINK_LIBRARIES(ptr_test base) +YADD_TEST(ptr_test) + # str test. ADD_EXECUTABLE(str_test str_test.c) TARGET_LINK_LIBRARIES(str_test base)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/base/ptr.c Sun Sep 29 14:42:18 2019 +0200 @@ -0,0 +1,43 @@ +#include "ptr.h" + +#include <assert.h> +#include <stdint.h> + +struct yptr_rc_counter { + yptr_delete_handler_t dh; + int32_t typesize; + int32_t count; +}; + +yptr_rc_t yptr_rc_new(void* ptr, size_t size, + yptr_delete_handler_t deletehandler) { + yptr_rc_t rcp; + rcp.ptr = ptr; + rcp.count = malloc(sizeof(struct yptr_rc_counter)); + rcp.count->dh = deletehandler; + rcp.count->typesize = size; + rcp.count->count = 1; + return rcp; +} + +void* yptr_rc_deref(yptr_rc_t rcp) { return rcp.ptr; } + +yptr_rc_t yptr_rc_clone(yptr_rc_t rcp) { + rcp.count->count++; + return rcp; +} + +void yptr_rc_delete(yptr_rc_t rcp) { + rcp.count->count--; + assert(rcp.count->count >= 0); + if (rcp.count->count == 0) { + if (rcp.count->dh) + (rcp.count->dh)(rcp.ptr); + free(rcp.ptr); + free(rcp.count); + } +} + +size_t yptr_rc_refs(yptr_rc_t rcp) { + return rcp.count->count; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/base/ptr.h Sun Sep 29 14:42:18 2019 +0200 @@ -0,0 +1,66 @@ +#ifndef base_ptr_h +#define base_ptr_h + +#include <stdlib.h> + +/** + * @file ptr.h + * @addtogroup base-ptr + * @{ + * @brief Smart pointers for C. + */ + +/// Called when an object needs to be freed. +typedef void (*yptr_delete_handler_t)(void*); + +struct yptr_rc_counter; + +/** + * @brief A reference-counted smart-pointer. + */ +typedef struct { + /// ptr points to the referenced object. It must point to dynamic memory and + /// is freed when there are no references left. + void* ptr; + + /// Internal, do not rely on this. + struct yptr_rc_counter* count; +} yptr_rc_t; + +/** + * @brief Move a pointer into a smart pointer. + */ +#define YPTR_RC(rawp, deletehandler) \ + (assert(rawp), yptr_rc_new(rawp, sizeof(*rawp), deletehandler)) + +/** + * @brief Move a pointer into a smart pointer. It is recommended to use YPTR_RC + * instead. + * + * `deletehandler` is called when the last reference is deleted, if it is not NULL. + */ +yptr_rc_t yptr_rc_new(void* ptr, size_t size, + yptr_delete_handler_t deletehandler); + +/** + * @brief Dereference a reference-counted pointer. + */ +#define YPTR_DEREF(rcptr, typ) (typ*)(yptr_rc_deref(rcptr)) + +void* yptr_rc_deref(yptr_rc_t rcp); + +/// @brief Create an additional reference. +yptr_rc_t yptr_rc_clone(yptr_rc_t rcp); + +/// @brief Remove an existing reference. Frees all memory if rcp is the last +/// existing reference. +void yptr_rc_delete(yptr_rc_t rcp); + +/// @brief How many references exist currently. Never returns 0. +size_t yptr_rc_refs(yptr_rc_t rcp); + +/** + * @} + */ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/base/ptr_test.c Sun Sep 29 14:42:18 2019 +0200 @@ -0,0 +1,19 @@ +#include "ptr.h" + +#include <assert.h> + +void test_ptr_rcptr_basic(void) { + char* somestring = calloc(16, 1); + yptr_rc_t ptr = YPTR_RC(somestring, NULL); + assert(1 == yptr_rc_refs(ptr)); + yptr_rc_t ptr2 = yptr_rc_clone(ptr); + assert(2 == yptr_rc_refs(ptr)); + yptr_rc_delete(ptr2); + assert(1 == yptr_rc_refs(ptr)); + yptr_rc_delete(ptr); +} + +int main(void) { + test_ptr_rcptr_basic(); + return 0; +}