#include #include #include #include #include #include "vector.h" #define RC_CHK 0xabadcafe struct rc { uint32_t check; _Atomic size_t refs; _Atomic size_t weak_refs; vec_t* _rc_restrict weaks; //vec type `rc_weak_t*` uint8_t ptr[]; }; struct rc_weak { _Atomic rc_bool is_valid; const struct rc* _rc_attr((may_alias)) _ptr; }; _rc_attr((noreturn)) void panic(const char* string, ...) { va_list args; va_start(args, string); fprintf(stderr, "(librc) Fatal error: "); vfprintf(stderr, string, args); va_end(args); fprintf(stderr, "\n"); abort(); } _rc_attr((noinline, noreturn, cold)) static void _fail_invptr() { panic("Invalid RC pointer"); } _rc_attr((noinline, noreturn, cold)) static void _fail_malloc(size_t sz) { panic("malloc() failed to allocate %d bytes (%d + sizeof(struct rc))", sz+sizeof(struct rc), sz); } _rc_attr((artificial, const, always_inline)) inline static const struct rc* ptr_crev(const void* ptr) { if (!ptr) _fail_invptr(); return (const struct rc*)(((const uint8_t*)ptr) - sizeof(struct rc)); } _rc_attr((artificial, const, always_inline)) inline static rc_t ptr_rev(void* ptr) { if (!ptr) _fail_invptr(); return (rc_t)(((uint8_t*)ptr) - sizeof(struct rc)); } _rc_attr((artificial, const, always_inline)) inline static rc_t ptr_ucrev(const void* ptr) { if (!ptr) _fail_invptr(); return (rc_t)(((uintptr_t)ptr) - sizeof(struct rc)); } _rc_attr((nonnull)) inline static void rc_checkptr(const struct rc* rc) { if (rc->check != RC_CHK) _fail_invptr(); } _rc_attr((malloc, alloc_size(1), returns_nonnull)) void* restrict rc_malloc(size_t sz) { register rc_t rc = malloc(sz + sizeof(struct rc)); if (!rc) _fail_malloc(sz); rc->check = RC_CHK; rc->refs = 1; rc->weak_refs =0; rc->weaks = vec_new(); return (void*) rc->ptr; } _rc_gmut_fn void rc_free(rc_ptr_t ptr) { register rc_t rc = ptr_rev(ptr); rc_checkptr(rc); if (!--rc->refs) { for(size_t i=0;iweaks->len;i++) { vec_tnptr(rc_weak_t, rc->weaks->buffer[i])->is_valid = RC_FALSE; } free(rc->weaks); free(rc); } } _rc_mut_fn _rc_attr((returns_nonnull)) rc_ptr_t rc_clone(rc_const_ptr_t ptr) { register rc_t rc = ptr_ucrev(ptr); rc_checkptr(rc); ++rc->refs; return rc->ptr; } _rc_fn size_t rc_refs(rc_const_ptr_t ptr) { register const struct rc* rc = ptr_crev(ptr); rc_checkptr(rc); return rc->refs; } _rc_fn rc_bool rc_is_valid(rc_const_ptr_t ptr) { register const struct rc* rc = ptr_crev(ptr); return rc->check == RC_CHK && rc->refs>0; } _rc_alloc_fn _rc_gmut_fn rc_weak_t* _rc_restrict rc_downgrade(rc_const_ptr_t ptr) { register rc_t rc = ptr_ucrev(ptr); rc_weak_t* weak = calloc(sizeof(rc_weak_t), 1); //TODO: push `weak` to rc->weaks weak->_ptr = rc; weak->is_valid = RC_TRUE; rc->weak_refs++; return weak; } _rc_gmut_fn void rc_weak_free(rc_weak_t* _rc_restrict ptr) { if (ptr->is_valid) { struct rc* _rc_attr((may_alias)) rc = (struct rc* _rc_attr((may_alias)))ptr->_ptr; rc_checkptr(rc); //TODO: remove `weak` from `rc->weaks` rc->weak_refs--; } free(ptr); } _rc_mut_fn rc_ptr_t rc_weak_upgrade(const rc_weak_t * _rc_restrict weak) { if (weak->is_valid) { struct rc* _rc_attr((may_alias)) rc = (struct rc* _rc_attr((may_alias)))weak->_ptr; if (rc->check == RC_CHK && rc->refs>0) { rc->refs++; return rc->ptr; } } return NULL; }