diff --git a/include/ni_alloc/alloc.h b/include/ni_alloc/alloc.h new file mode 100644 index 0000000..1c62724 --- /dev/null +++ b/include/ni_alloc/alloc.h @@ -0,0 +1,122 @@ +#ifndef __NICKEL_ALLOC_H +#define __NICKEL_ALLOC_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Nickel Allocator API (explicit; does NOT replace libc malloc unless you opt-in). + Design goal: easy to use in tools, easy to test, portable across Linux/macOS. +*/ + +typedef struct ni_alloc_stats { + /* current usage */ + size_t bytes_in_use; /* user-visible allocated bytes (approx if desired) */ + size_t bytes_reserved; /* backing store reserved from VM */ + size_t bytes_peak; /* peak in-use */ + size_t bytes_reserved_peak; + + /* counters */ + uint64_t alloc_calls; + uint64_t free_calls; + uint64_t realloc_calls; + uint64_t calloc_calls; + uint64_t memalign_calls; + + /* failures */ + uint64_t oom_count; +} ni_alloc_stats; + +/* Allocator configuration knobs (keep minimal at first; extend later). */ +typedef struct ni_alloc_config { + /* If true, ni_free(NULL) is allowed and a no-op (recommended true). */ + bool free_null_is_noop; + + /* If true, ni_realloc(p,0) behaves like free(p) and returns NULL. */ + bool realloc_zero_frees; + + /* If true, scribble freed blocks with a pattern (debug build feature). */ + bool scribble_free; + + /* If true, scribble newly allocated blocks with a pattern (debug). */ + bool scribble_alloc; + + /* Optional alignment for default allocations (usually alignof(max_align_t)). */ + size_t default_alignment; + + /* Optional: maximum small-bin size, arena chunk size, etc. */ + size_t arena_chunk_bytes; +} ni_alloc_config; + +/* Opaque allocator instance. + You can use the global allocator, or create a private allocator instance + (handy for tests / subsystems). +*/ +typedef struct ni_allocator ni_allocator; + +/* -------- Global allocator API -------- */ + +/* Initialize/shutdown global allocator (optional; can be lazy). + Returns false if initialization fails. +*/ +bool ni_alloc_global_init(const ni_alloc_config* cfg); +void ni_alloc_global_shutdown(void); + +/* Allocation functions using the global allocator. */ +void* ni_malloc(size_t size); +void ni_free(void* ptr); +void* ni_realloc(void* ptr, size_t new_size); +void* ni_calloc(size_t count, size_t elem_size); + +/* Alignment: + - alignment must be power of two and >= sizeof(void*) + - returns NULL on failure +*/ +void* ni_memalign(size_t alignment, size_t size); + +/* Equivalent of C11 aligned_alloc semantics: + - alignment must be power of two + - size must be a multiple of alignment +*/ +void* ni_aligned_alloc(size_t alignment, size_t size); + +/* Query/reset stats for global allocator. */ +void ni_alloc_get_stats(ni_alloc_stats* out_stats); +void ni_alloc_reset_stats(void); + +/* -------- Instance allocator API -------- */ + +ni_allocator* ni_allocator_create(const ni_alloc_config* cfg); +void ni_allocator_destroy(ni_allocator* a); + +void* ni_allocator_malloc(ni_allocator* a, size_t size); +void ni_allocator_free(ni_allocator* a, void* ptr); +void* ni_allocator_realloc(ni_allocator* a, void* ptr, size_t new_size); +void* ni_allocator_calloc(ni_allocator* a, size_t count, size_t elem_size); +void* ni_allocator_memalign(ni_allocator* a, size_t alignment, size_t size); + +void ni_allocator_get_stats(ni_allocator* a, ni_alloc_stats* out_stats); +void ni_allocator_reset_stats(ni_allocator* a); + +/* -------- Utilities / validation -------- */ + +/* Returns true if n is a power of two (and nonzero). */ +bool ni_is_pow2_size(size_t n); + +/* Round up x to next multiple of align (align must be power of two). */ +size_t ni_align_up(size_t x, size_t align); + +/* Optional: verify internal invariants in debug builds. + - returns false if corruption detected +*/ +bool ni_alloc_validate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __NICKEL_ALLOC_H */ diff --git a/include/ni_alloc/vm.h b/include/ni_alloc/vm.h new file mode 100644 index 0000000..4e2c6d7 --- /dev/null +++ b/include/ni_alloc/vm.h @@ -0,0 +1,59 @@ +#ifndef __NICKEL_VM_H +#define __NICKEL_VM_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* VM layer: portable-ish page allocation based on mmap/munmap (Linux + macOS). */ + +typedef struct ni_vm_stats { + size_t page_size; + + /* cumulative counters */ + uint64_t reserves; /* number of reserve calls */ + uint64_t releases; /* number of release calls */ + uint64_t commits; /* if supported by impl (optional), else 0 */ + uint64_t decommits; /* if supported by impl (optional), else 0 */ + + size_t bytes_reserved; /* current bytes reserved */ + size_t bytes_peak; /* peak reserved */ +} ni_vm_stats; + +/* Returns OS page size (usually sysconf(_SC_PAGESIZE)). Never returns 0. */ +size_t ni_vm_page_size(void); + +/* Reserve anonymous virtual memory. + - bytes will be rounded up to a page multiple. + - returns NULL on failure. +*/ +void* ni_vm_reserve(size_t bytes); + +/* Release a region previously returned by ni_vm_reserve (page-multiple size). */ +bool ni_vm_release(void* addr, size_t bytes); + +/* Optional: “commit/decommit” for platforms where it’s meaningful. + On many Unix systems, reserve implies commit; implementations may no-op. +*/ +bool ni_vm_commit(void* addr, size_t bytes); +bool ni_vm_decommit(void* addr, size_t bytes); + +/* Optional: set guard pages (mprotect) for debug. + Returns false if unsupported or fails. +*/ +bool ni_vm_protect_none(void* addr, size_t bytes); +bool ni_vm_protect_rw(void* addr, size_t bytes); + +/* Stats are optional but very useful for tests/tools. */ +void ni_vm_get_stats(ni_vm_stats* out_stats); +void ni_vm_reset_stats(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __NICKEL_VM_H */ diff --git a/src/ni_alloc/alloc.c b/src/ni_alloc/alloc.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ni_alloc/vm_legacy.c b/src/ni_alloc/vm_legacy.c new file mode 100644 index 0000000..e69de29 diff --git a/src/ni_alloc/vm_posix.c b/src/ni_alloc/vm_posix.c new file mode 100644 index 0000000..e69de29