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;
+}