Add refc.h

This commit is contained in:
2026-01-18 01:40:21 +00:00
parent d2c063fdbb
commit c2d38af177

138
refc.h Normal file
View File

@@ -0,0 +1,138 @@
#ifndef REFC_H
#define REFC_H
#include <stddef.h>
#include <stdbool.h>
// #define REFC_IMPLEMENTATION
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*refc_destructor_fn)(void* obj, size_t size, void* user);
typedef struct refc_obj {
void* ptr;
} refc_obj;
refc_obj refc_make(size_t size, refc_destructor_fn dtor, void* user);
refc_obj refc_make_zero(size_t size, refc_destructor_fn dtor, void* user);
refc_obj refc_acquire(refc_obj o);
void refc_drop(refc_obj* o);
refc_obj refc_take(refc_obj* o);
void* refc_ptr(refc_obj o);
unsigned refc_count(refc_obj o);
size_t refc_size(refc_obj o);
bool refc_unique(refc_obj o);
bool refc_is_null(refc_obj o);
void refc_cleanup(refc_obj* o);
#if defined(__GNUC__) || defined(__clang__)
#define REFC_SCOPED __attribute__((cleanup(refc_cleanup)))
#else
#define REFC_SCOPED
#endif
#ifdef __cplusplus
}
#endif
#ifdef REFC_IMPLEMENTATION
#include <stdlib.h>
#include <string.h>
#include <stdalign.h>
#include <stdatomic.h>
typedef struct refc_block {
_Alignas(max_align_t) atomic_uint ref;
size_t size;
refc_destructor_fn dtor;
void* user;
unsigned char data[];
} refc_block;
static inline refc_block* refc_blk(const void* p) {
return (refc_block*)((unsigned char*)p - offsetof(refc_block, data));
}
refc_obj refc_make(size_t size, refc_destructor_fn dtor, void* user) {
refc_obj o = {0};
if (size > (size_t)-1 - sizeof(refc_block)) return o;
refc_block* b = (refc_block*)malloc(sizeof(refc_block) + size);
if (!b) return o;
atomic_init(&b->ref, 1u);
b->size = size;
b->dtor = dtor;
b->user = user;
o.ptr = (void*)b->data;
return o;
}
refc_obj refc_make_zero(size_t size, refc_destructor_fn dtor, void* user) {
refc_obj o = refc_make(size, dtor, user);
if (o.ptr && size) memset(o.ptr, 0, size);
return o;
}
refc_obj refc_acquire(refc_obj o) {
if (!o.ptr) return o;
refc_block* b = refc_blk(o.ptr);
atomic_fetch_add_explicit(&b->ref, 1u, memory_order_relaxed);
return o;
}
void refc_drop(refc_obj* o) {
if (!o || !o->ptr) return;
refc_block* b = refc_blk(o->ptr);
o->ptr = NULL;
if (atomic_fetch_sub_explicit(&b->ref, 1u, memory_order_acq_rel) == 1u) {
atomic_thread_fence(memory_order_acquire);
if (b->dtor) b->dtor((void*)b->data, b->size, b->user);
free(b);
}
}
refc_obj refc_take(refc_obj* o) {
refc_obj out = {0};
if (!o) return out;
out = *o;
o->ptr = NULL;
return out;
}
void* refc_ptr(refc_obj o) {
return o.ptr;
}
unsigned refc_count(refc_obj o) {
if (!o.ptr) return 0u;
const refc_block* b = refc_blk(o.ptr);
return atomic_load_explicit(&b->ref, memory_order_relaxed);
}
size_t refc_size(refc_obj o) {
if (!o.ptr) return 0u;
const refc_block* b = refc_blk(o.ptr);
return b->size;
}
bool refc_unique(refc_obj o) {
return refc_count(o) == 1u;
}
bool refc_is_null(refc_obj o) {
return o.ptr == NULL;
}
void refc_cleanup(refc_obj* o) {
refc_drop(o);
}
#endif
#endif