Add refc.h
This commit is contained in:
138
refc.h
Normal file
138
refc.h
Normal 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
|
||||
Reference in New Issue
Block a user