Compare commits
12 Commits
e6e4cdd961
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7afc3b0637 | |||
| cbc8d0a7c5 | |||
| 3e346312e2 | |||
| 1b980b2720 | |||
| 2c51ab5d78 | |||
| cbd01e7a47 | |||
| 30273486f1 | |||
| eab45f3f58 | |||
| 507c274887 | |||
| b0db2effc7 | |||
| 11f1a7e7c3 | |||
| d078546eab |
92
README.md
92
README.md
@@ -1,4 +1,6 @@
|
|||||||
### Core “make C nicer” utilities
|
# Nickel lib overview/roadmap
|
||||||
|
|
||||||
|
## Core “make C nicer” utilities
|
||||||
|
|
||||||
1. **`xmalloc/xcalloc/xrealloc/xstrdup` (header: `xalloc.h`)**
|
1. **`xmalloc/xcalloc/xrealloc/xstrdup` (header: `xalloc.h`)**
|
||||||
|
|
||||||
@@ -28,89 +30,93 @@
|
|||||||
* `bitset_set/clear/test`, `bitset_find_first_zero`
|
* `bitset_set/clear/test`, `bitset_find_first_zero`
|
||||||
* Teaches: word operations, popcount/ctz if you want.
|
* Teaches: word operations, popcount/ctz if you want.
|
||||||
|
|
||||||
### C11 correctness features
|
7. **Intrusive double linked list (header: `linkedlist.h`)**
|
||||||
|
|
||||||
7. **Compile-time assertions & type checks (header: `ctassert.h`)**
|
* An implementation of a double linked list using a sentinel pattern
|
||||||
|
|
||||||
|
## C11 correctness features
|
||||||
|
|
||||||
|
1. **Compile-time assertions & type checks (header: `ctassert.h`)**
|
||||||
|
|
||||||
* Wrap `_Static_assert`, plus some `_Generic` helpers.
|
* Wrap `_Static_assert`, plus some `_Generic` helpers.
|
||||||
* Teaches: compile-time constraints, “making invalid states unrepresentable”.
|
* Teaches: compile-time constraints, “making invalid states unrepresentable”.
|
||||||
|
|
||||||
8. **`_Generic` logging macros (header: `log.h`)**
|
2. **`_Generic` logging macros (header: `log.h`)**
|
||||||
|
|
||||||
* `LOG_INFO("x=%d", x)` with file/line, optional type-based formatting helpers.
|
* `LOG_INFO("x=%d", x)` with file/line, optional type-based formatting helpers.
|
||||||
* Teaches: variadic macros, `_Generic`, ergonomics.
|
* Teaches: variadic macros, `_Generic`, ergonomics.
|
||||||
|
|
||||||
9. **Defer/cleanup pattern (header: `defer.h`)**
|
3. **Defer/cleanup pattern (header: `defer.h`)**
|
||||||
|
|
||||||
* Macro that runs cleanup at scope end (via `goto` pattern).
|
* Macro that runs cleanup at scope end (via `goto` pattern).
|
||||||
* Teaches: structured cleanup in C, error paths.
|
* Teaches: structured cleanup in C, error paths.
|
||||||
|
|
||||||
10. **Optional result type (header: `result.h`)**
|
4. **Optional result type (header: `result.h`)**
|
||||||
|
|
||||||
* `typedef struct { int ok; int err; T value; } result_T;` pattern.
|
* `typedef struct { int ok; int err; T value; } result_T;` pattern.
|
||||||
* Teaches: explicit error handling, no hidden global state.
|
* Teaches: explicit error handling, no hidden global state.
|
||||||
|
|
||||||
### Concurrency & atomics (C11’s “real” new power)
|
## Concurrency & atomics
|
||||||
|
|
||||||
11. **Atomic reference counting (header: `refcnt.h`)**
|
1. **Atomic reference counting (header: `refcnt.h`)**
|
||||||
|
|
||||||
* `ref_inc/ref_dec` with destructor callback.
|
* `ref_inc/ref_dec` with destructor callback.
|
||||||
* Teaches: `stdatomic.h`, memory ordering (start with seq_cst, then learn relax/acq_rel).
|
* Teaches: `stdatomic.h`, memory ordering (start with seq_cst, then learn relax/acq_rel).
|
||||||
|
|
||||||
12. **Lock-free SPSC ring buffer (header: `ring_spsc.h`)**
|
2. **Lock-free SPSC ring buffer (header: `ring_spsc.h`)**
|
||||||
|
|
||||||
* Single-producer/single-consumer queue.
|
* Single-producer/single-consumer queue.
|
||||||
* Teaches: atomics, cache friendliness, correctness reasoning.
|
* Teaches: atomics, cache friendliness, correctness reasoning.
|
||||||
|
|
||||||
13. **Thread pool (header: `threadpool.h`)** using `threads.h`
|
3. **Thread pool (header: `threadpool.h`)** using `threads.h`
|
||||||
|
|
||||||
* `tp_init`, `tp_submit`, `tp_join`, `tp_destroy`
|
* `tp_init`, `tp_submit`, `tp_join`, `tp_destroy`
|
||||||
* Teaches: `thrd_t`, `mtx_t`, `cnd_t`, work queues.
|
* Teaches: `thrd_t`, `mtx_t`, `cnd_t`, work queues.
|
||||||
|
|
||||||
14. **Once-init & singletons (header: `once.h`)**
|
4. **Once-init & singletons (header: `once.h`)**
|
||||||
|
|
||||||
* Use `once_flag` / `call_once` (or implement if platform lacks).
|
* Use `once_flag` / `call_once` (or implement if platform lacks).
|
||||||
* Teaches: init races, safe global setup.
|
* Teaches: init races, safe global setup.
|
||||||
|
|
||||||
### Parsing / CLI tools (you’ll actually use these)
|
## Parsing / CLI tools
|
||||||
|
|
||||||
15. **Argument parser (header: `argparse.h`)**
|
1. **Argument parser (header: `argparse.h`)**
|
||||||
|
|
||||||
* `--long`, `-s`, combined short flags, `--key=value`
|
* `--long`, `-s`, combined short flags, `--key=value`
|
||||||
* Teaches: string parsing, API design, test cases.
|
* Teaches: string parsing, API design, test cases.
|
||||||
|
|
||||||
16. **INI parser (header: `ini.h`)**
|
2. **INI parser (header: `ini.h`)**
|
||||||
|
|
||||||
* Minimal: sections, key=value, comments.
|
* Minimal: sections, key=value, comments.
|
||||||
* Teaches: parsing state machines, callbacks.
|
* Teaches: parsing state machines, callbacks.
|
||||||
|
|
||||||
17. **CSV reader/writer (header: `csv.h`)**
|
3. **CSV reader/writer (header: `csv.h`)**
|
||||||
|
|
||||||
* Correct quoting/escaping.
|
* Correct quoting/escaping.
|
||||||
* Teaches: edge cases, streaming parsing.
|
* Teaches: edge cases, streaming parsing.
|
||||||
|
|
||||||
18. **Path utilities (header: `path.h`)**
|
4. **Path utilities (header: `path.h`)**
|
||||||
|
|
||||||
* `path_join`, `path_dirname`, `path_basename`, normalize `..` and `.`
|
* `path_join`, `path_dirname`, `path_basename`, normalize `..` and `.`
|
||||||
* Teaches: portability pitfalls, careful string ops.
|
* Teaches: portability pitfalls, careful string ops.
|
||||||
|
|
||||||
### Systems-ish building blocks
|
## Systems-ish building blocks
|
||||||
|
|
||||||
19. **File mapping / buffered reader (header: `io.h`)**
|
1. **File mapping / buffered reader (header: `io.h`)**
|
||||||
|
|
||||||
* Portable-ish wrapper for “read whole file”, “iter lines”, “atomic write via temp+rename”.
|
* Portable-ish wrapper for “read whole file”, “iter lines”, “atomic write via temp+rename”.
|
||||||
* Teaches: robust file I/O patterns, error handling.
|
* Teaches: robust file I/O patterns, error handling.
|
||||||
|
|
||||||
20. **Timing + profiling helpers (header: `timeutil.h`)**
|
2. **Timing + profiling helpers (header: `timeutil.h`)**
|
||||||
|
|
||||||
* `now_ns()`, `stopwatch`, `scope_timer` macro
|
* `now_ns()`, `stopwatch`, `scope_timer` macro
|
||||||
* Teaches: `timespec_get`, measurement pitfalls, microbenchmark hygiene.
|
* Teaches: `timespec_get`, measurement pitfalls, microbenchmark hygiene.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## A suggested “crash course” order (so it builds)
|
## suggested order
|
||||||
|
|
||||||
1–3 (xalloc/arena/buf) → 7–10 (static assert, generic, cleanup, results) → 15 (argparse) → 19 (io) → 16–18 (parsers) → 11–14 (atomics/threads) → 5–6 (hashmap/bitset) → 20 (timing) → 12/13 (ring + threadpool, as capstone)
|
xalloc/arena/buf -> static assert, generic, cleanup, results -> argparse -> io -> parsers -> atomics/threads -> hashmap/bitset-> timing -> ring + threadpool, as capstone
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
135
include/ni_alloc/alloc.h
Normal file
135
include/ni_alloc/alloc.h
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#ifndef __NICKEL_ALLOC_H
|
||||||
|
#define __NICKEL_ALLOC_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define ENABLE_NICKEL_ALLOC_SHIM
|
||||||
|
|
||||||
|
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 ENABLE_NICKEL_ALLOC_SHIM
|
||||||
|
/* Allocation functions using the global allocator.
|
||||||
|
define symbols pointing at our versions
|
||||||
|
*/
|
||||||
|
void* malloc(size_t size) { ni_malloc(size); }
|
||||||
|
void free(void* ptr) { ni_free(ptr); }
|
||||||
|
void* realloc(void* ptr, size_t new_size) { ni_realloc(ptr, new_size); }
|
||||||
|
void* calloc(size_t count, size_t elem_size) { ni_calloc(count, elem_size); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_ALLOC_H */
|
||||||
59
include/ni_alloc/vm.h
Normal file
59
include/ni_alloc/vm.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef __NICKEL_VM_H
|
||||||
|
#define __NICKEL_VM_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
29
include/nickel.h
Normal file
29
include/nickel.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef __NICKEL_H_
|
||||||
|
#define __NICKEL_H_
|
||||||
|
|
||||||
|
/* common includes across the project */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* includes from internal to the project */
|
||||||
|
// ...
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// any thing we need in the top level here
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_ARENA_H_
|
||||||
|
#define __NICKEL_ARENA_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_ARENA_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_ARGPARSE_H_
|
||||||
|
#define __NICKEL_ARGPARSE_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_ARGPARSE_H_ */
|
||||||
14
include/nickel/bitman.h
Normal file
14
include/nickel/bitman.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_BITMAN_H_
|
||||||
|
#define __NICKEL_BITMAN_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_BITMAN_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_BUF_H_
|
||||||
|
#define __NICKEL_BUF_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_BUF_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_CTASSERT_H_
|
||||||
|
#define __NICKEL_CTASSERT_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_CTASSERT_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_DEFER_H_
|
||||||
|
#define __NICKEL_DEFER_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_DEFER_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_HASHMAP_H_
|
||||||
|
#define __NICKEL_HASHMAP_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_HASHMAP_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_INI_H_
|
||||||
|
#define __NICKEL_INI_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_INI_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_IO_H_
|
||||||
|
#define __NICKEL_IO_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_IO_H_ */
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#ifndef __NICKEL_LINKEDLIST_H_
|
#ifndef __NICKEL_LINKEDLIST_H_
|
||||||
#define __NICKEL_LINKEDLIST_H_
|
#define __NICKEL_LINKEDLIST_H_
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include "nickel.h"
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
#ifdef __cplusplus
|
||||||
#include <stdbool.h>
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct ni_list_node {
|
typedef struct ni_list_node {
|
||||||
struct ni_list_node* next;
|
struct ni_list_node* next;
|
||||||
@@ -65,4 +66,8 @@ static inline ni_list_node* ni_list__get_back(ni_list* l) {
|
|||||||
#define NI_LIST_CONTAINER_OF(ptr, type, member) \
|
#define NI_LIST_CONTAINER_OF(ptr, type, member) \
|
||||||
((type*)((uint8_t*)(ptr) - offsetof(type, member)))
|
((type*)((uint8_t*)(ptr) - offsetof(type, member)))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __NICKEL_LINKEDLIST_H_ */
|
#endif /* __NICKEL_LINKEDLIST_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_LOG_H_
|
||||||
|
#define __NICKEL_LOG_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_LOG_H_ */
|
||||||
31
include/nickel/oops.h
Normal file
31
include/nickel/oops.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef __NICKEL_OOPS_H
|
||||||
|
#define __NICKEL_OOPS_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: C23 adds [[noreturn]] (& [[]] function attribute tags)
|
||||||
|
// which are standardized, the below depends on compiler support.
|
||||||
|
#if defined(__GNUC__) || defined(__CLANG__)
|
||||||
|
#define NORETURN __attribute__ ((__noreturn__))
|
||||||
|
#else
|
||||||
|
#define NORETURN
|
||||||
|
#endif /* __GNUC__ || __CLANG__*/
|
||||||
|
|
||||||
|
void nickel_oops_handler(const char *format, ...) NORETURN;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// clean up our noreturn define
|
||||||
|
#undef NORETURN
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_REFCNT_H_
|
||||||
|
#define __NICKEL_REFCNT_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_REFCNT_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_RESULT_H_
|
||||||
|
#define __NICKEL_RESULT_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_RESULT_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_RINGSPSC_H_
|
||||||
|
#define __NICKEL_RINGSPSC_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_RINGSPSC_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_SPAN_H_
|
||||||
|
#define __NICKEL_SPAN_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_SPAN_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_THREADPOOL_H_
|
||||||
|
#define __NICKEL_THREADPOOL_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_THREADPOOL_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_TIMEUTIL_H_
|
||||||
|
#define __NICKEL_TIMEUTIL_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_TIMEUTIL_H_ */
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __NICKEL_VERSION_H_
|
||||||
|
#define __NICKEL_VERSION_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* header contents */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_VERSION_H_ */
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef __NICKEL_XALLOC_H_
|
||||||
|
#define __NICKEL_XALLOC_H_
|
||||||
|
|
||||||
|
#include "nickel.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void* ni_xmalloc(size_t size);
|
||||||
|
|
||||||
|
void* ni_xcalloc(size_t nmemb,size_t size);
|
||||||
|
void* ni_xrealloc(void* ptr, size_t size);
|
||||||
|
|
||||||
|
char* ni_xstrdup(const char *s);
|
||||||
|
char* ni_xstrndup(const char s[], size_t n);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NICKEL_XALLOC_H_ */
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
|
ALLOC_LIB := $(BUILD_DIR)/lib/libnia.a
|
||||||
LIB := $(BUILD_DIR)/lib/libnickel.a
|
LIB := $(BUILD_DIR)/lib/libnickel.a
|
||||||
BIN_DIR := $(BUILD_DIR)/bin
|
BIN_DIR := $(BUILD_DIR)/bin
|
||||||
OBJ_DIR := $(BUILD_DIR)/obj
|
OBJ_DIR := $(BUILD_DIR)/obj
|
||||||
|
|
||||||
|
NI_ALLOC_SRCS := $(wildcard src/nickel/ni_alloc/*.c)
|
||||||
|
NI_ALLOC_OBJS := $(patsubst src/ni_alloc/%.c,$(OBJ_DIR)/ni_alloc/%.o,$(NI_ALLOC_SRCS))
|
||||||
|
|
||||||
NICKEL_SRCS := $(wildcard src/nickel/*.c)
|
NICKEL_SRCS := $(wildcard src/nickel/*.c)
|
||||||
NICKEL_OBJS := $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(NICKEL_SRCS))
|
NICKEL_OBJS := $(patsubst src/nickel/%.c,$(OBJ_DIR)/nickel/%.o,$(NICKEL_SRCS))
|
||||||
|
|
||||||
TEST_SRCS := $(wildcard tests/*.c)
|
TEST_SRCS := $(wildcard tests/*.c)
|
||||||
TEST_BIN := $(BIN_DIR)/tests
|
TEST_BIN := $(BIN_DIR)/tests
|
||||||
@@ -14,7 +18,12 @@ TOOL_BINS := \
|
|||||||
|
|
||||||
.PHONY: all clean test tools examples
|
.PHONY: all clean test tools examples
|
||||||
|
|
||||||
all: $(LIB) tools $(TEST_BIN)
|
all: $(ALLOC_LIB) $(LIB) tools $(TEST_BIN)
|
||||||
|
|
||||||
|
$(ALLOC_LIB): $(NI_ALLOC_OBJS)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(AR) rcs $@ $^
|
||||||
|
$(RANLIB) $@
|
||||||
|
|
||||||
$(LIB): $(NICKEL_OBJS)
|
$(LIB): $(NICKEL_OBJS)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@@ -26,9 +35,9 @@ $(OBJ_DIR)/%.o: src/%.c
|
|||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# Tests link against libnickel.a
|
# Tests link against libnickel.a
|
||||||
$(TEST_BIN): $(TEST_SRCS) $(LIB)
|
$(TEST_BIN): $(TEST_SRCS) $(ALLOC_LIB) $(LIB)
|
||||||
@mkdir -p $(BIN_DIR)
|
@mkdir -p $(BIN_DIR)
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TEST_SRCS) $(LIB) $(LDLIBS)
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TEST_SRCS) $(ALLOC_LIB) $(LIB) $(LDLIBS)
|
||||||
|
|
||||||
# Tools (one main.c each; expand as needed)
|
# Tools (one main.c each; expand as needed)
|
||||||
#$(BIN_DIR)/logscan: tools/logscan/main.c $(LIB)
|
#$(BIN_DIR)/logscan: tools/logscan/main.c $(LIB)
|
||||||
@@ -39,10 +48,10 @@ $(TEST_BIN): $(TEST_SRCS) $(LIB)
|
|||||||
# @mkdir -p $(BIN_DIR)
|
# @mkdir -p $(BIN_DIR)
|
||||||
# $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
|
# $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
|
||||||
|
|
||||||
|
tools: $(TOOL_BINS)
|
||||||
|
|
||||||
test: $(TEST_BIN)
|
test: $(TEST_BIN)
|
||||||
$(TEST_BIN)
|
$(TEST_BIN)
|
||||||
|
|
||||||
tools: $(TOOL_BINS)
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|||||||
58
src/ni_alloc/alloc.c
Normal file
58
src/ni_alloc/alloc.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include "ni_alloc/alloc.h"
|
||||||
|
|
||||||
|
/* -------- 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);
|
||||||
29
src/ni_alloc/vm_posix.c
Normal file
29
src/ni_alloc/vm_posix.c
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "ni_alloc/vm.h"
|
||||||
|
|
||||||
|
/* 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);
|
||||||
64
src/nickel/xalloc.c
Normal file
64
src/nickel/xalloc.c
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "nickel/xalloc.h"
|
||||||
|
|
||||||
|
void* ni_xmalloc(size_t size) {
|
||||||
|
void* retptr = malloc(size);
|
||||||
|
|
||||||
|
if (retptr != NULL) {
|
||||||
|
return retptr;
|
||||||
|
} else {
|
||||||
|
perror("xmalloc failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ni_xcalloc(size_t nmemb,size_t size) {
|
||||||
|
void* retptr = calloc(nmemb, size);
|
||||||
|
|
||||||
|
if (retptr != NULL) {
|
||||||
|
return retptr;
|
||||||
|
} else {
|
||||||
|
perror("xcalloc failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ni_xrealloc(void* ptr, size_t size) {
|
||||||
|
void* retptr = realloc(ptr, size);
|
||||||
|
|
||||||
|
if (retptr != NULL) {
|
||||||
|
return retptr;
|
||||||
|
} else {
|
||||||
|
perror("xrealloc failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ni_xstrdup(const char *s) {
|
||||||
|
void* dupstr = strdup(s);
|
||||||
|
|
||||||
|
if (dupstr != NULL) {
|
||||||
|
return dupstr;
|
||||||
|
} else {
|
||||||
|
perror("xdupstr failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ni_xstrndup(const char s[], size_t n) {
|
||||||
|
void* dupstr = strndup(s, n);
|
||||||
|
|
||||||
|
if (dupstr != NULL) {
|
||||||
|
return dupstr;
|
||||||
|
} else {
|
||||||
|
perror("xdupstr failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user