#define _DEFAULT_SOURCE #include #include #include #include #include #include #include #ifndef MAP_UNINITIALIZED #define MAP_UNINITIALIZED 0 #endif void* dpa_mem_space_start; __attribute__((constructor,used)) static void init(void){ assert((dpa_mem_space_start=mmap(0, DPA_MEM_SPACE_SIZE*2, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE|MAP_UNINITIALIZED, -1, 0)) != MAP_FAILED); } void* dpa_internal_allocate_pages(size_t size){ (void)size; return 0; } void dpa_internal_free_pages(void* page, size_t size){ assert(mmap(page, size, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE|MAP_UNINITIALIZED, -1, 0) != MAP_FAILED); assert(mmap((char*)page+DPA_MEM_SPACE_SIZE, size, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE|MAP_UNINITIALIZED, -1, 0) != MAP_FAILED); } // max_align_t is usually 16 or 32 (RISC-V) aligned, this will waste a bit of memory... struct metadata { alignas(max_align_t) struct dpa_refcount refcount; }; void* dpa_malloc(size_t size){ if(!size) return 0; struct metadata* mem = dpa_mem_impl_realloc(0, sizeof(*mem)+size); if(!mem) return 0; memset(mem, 0, sizeof(*mem)); dpa_refcount_increment(&mem->refcount); return mem + 1; } void* dpa_calloc(size_t size){ void* mem = dpa_malloc(size); if(!mem) return 0; memset(mem, 0, size); return mem; } bool dpa_realloc(void** data, size_t size){ if(!data) return false; if(!*data && !size) return true; void*restrict a = *data; uintptr_t ua = (char*)a - (char*)dpa_mem_space_start; if(ua >= DPA_MEM_SPACE_SIZE * 2){ size_t old_size = a ? malloc_usable_size(a) : 0; if(size){ void* mem = dpa_malloc(size); if(!mem) return false; memcpy(a, mem, old_size < size ? old_size : size); *data = mem; }else{ *data = 0; } free(a); return true; } if(ua >= DPA_MEM_SPACE_SIZE) a = (char*)a - DPA_MEM_SPACE_SIZE; struct metadata*restrict src = a; src -= 1; if(dpa_refcount_is_last(&src->refcount)) return false; struct metadata* mem = dpa_mem_impl_realloc(src, sizeof(*mem) + size); if(!mem) return false; *data = mem + 1; return true; } void* dpa_dup(const void* data, size_t size){ if(!data || !size) return 0; void* mem = dpa_malloc(size); if(!mem) return 0; memcpy(mem, data, size); return mem; } void* dpa_dupref_rw(void* data, size_t size){ if(dpa_mem_is_mine(data)){ dpa_ref(data); return data; }else{ return dpa_dup(data, size); } } const void* dpa_dupref_ro(const void* data, size_t size){ if(dpa_is_readonly(data)){ dpa_ref(data); return data; }else{ return dpa_readonly(dpa_dup(data, size)); } } void dpa_ref(const void* a){ uintptr_t ua = (char*)a - (char*)dpa_mem_space_start; if(ua < DPA_MEM_SPACE_SIZE * 2) return; if(ua >= DPA_MEM_SPACE_SIZE) a = (char*)a - DPA_MEM_SPACE_SIZE; struct metadata*restrict meta = dpa_mem_impl_find_allocation_start((void*)a); dpa_refcount_increment(&meta->refcount); } void dpa_put(const void* a){ uintptr_t ua = (char*)a - (char*)dpa_mem_space_start; if(ua < DPA_MEM_SPACE_SIZE * 2) return; if(ua >= DPA_MEM_SPACE_SIZE) a = (char*)a - DPA_MEM_SPACE_SIZE; struct metadata*restrict meta = dpa_mem_impl_find_allocation_start((void*)a); if(!dpa_refcount_decrement(&meta->refcount)) dpa_mem_impl_realloc(meta, 0); } void dpa_destroy(const void* data){ if(!dpa_mem_is_mine(data)){ free((void*)data); return; } struct metadata*restrict src = (void*)data; dpa_mem_impl_realloc(src-1, 0); }