From 583975459fac6b641c2d4d5a2e1ce477c0ac0ff1 Mon Sep 17 00:00:00 2001 From: Elaina Claus Date: Tue, 13 Jan 2026 21:46:05 -0500 Subject: [PATCH] first commit, initial layout and some ideas I want to implement, included my linked list impl --- .gitignore | 7 ++ .vscode/c_cpp_properties.json | 16 +++++ .vscode/tasks.json | 37 +++++++++++ Makefile | 3 + README.md | 122 ++++++++++++++++++++++++++++++++++ include/nickel/arena.h | 0 include/nickel/argparse.h | 0 include/nickel/bitset.h | 0 include/nickel/buf.h | 0 include/nickel/ctassert.h | 0 include/nickel/defer.h | 0 include/nickel/hashmap.h | 0 include/nickel/ini.h | 0 include/nickel/io.h | 0 include/nickel/linkedlist.h | 62 +++++++++++++++++ include/nickel/log.h | 0 include/nickel/refcnt.h | 0 include/nickel/result.h | 0 include/nickel/ring_spsc.h | 0 include/nickel/span.h | 0 include/nickel/threadpool.h | 0 include/nickel/timeutil.h | 0 include/nickel/version.h | 0 include/nickel/xalloc.h | 0 make/config.mk | 7 ++ make/rules.mk | 48 +++++++++++++ make/toolchain.mk | 31 +++++++++ src/nickel/linkedlist.c | 67 +++++++++++++++++++ tests/test_linkedlist | Bin 0 -> 55736 bytes tests/test_linkedlist.c | 87 ++++++++++++++++++++++++ 30 files changed, 487 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/tasks.json create mode 100644 Makefile create mode 100644 README.md create mode 100644 include/nickel/arena.h create mode 100644 include/nickel/argparse.h create mode 100644 include/nickel/bitset.h create mode 100644 include/nickel/buf.h create mode 100644 include/nickel/ctassert.h create mode 100644 include/nickel/defer.h create mode 100644 include/nickel/hashmap.h create mode 100644 include/nickel/ini.h create mode 100644 include/nickel/io.h create mode 100644 include/nickel/linkedlist.h create mode 100644 include/nickel/log.h create mode 100644 include/nickel/refcnt.h create mode 100644 include/nickel/result.h create mode 100644 include/nickel/ring_spsc.h create mode 100644 include/nickel/span.h create mode 100644 include/nickel/threadpool.h create mode 100644 include/nickel/timeutil.h create mode 100644 include/nickel/version.h create mode 100644 include/nickel/xalloc.h create mode 100644 make/config.mk create mode 100644 make/rules.mk create mode 100644 make/toolchain.mk create mode 100644 src/nickel/linkedlist.c create mode 100755 tests/test_linkedlist create mode 100644 tests/test_linkedlist.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..950b750 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build/ +*.o +*.a +*.d +*.log +*.tmp +.DS_Store \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..d06787a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc-14", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..5137304 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,37 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc-14 build active file", + "command": "/usr/bin/gcc-14", + "args": [ + "-fdiagnostics-color=always", + "-g3", + "-O0", + "-std=c11", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wconversion", + "-Wstrict-prototypes", + "-Wmissing-prototypes", + "-D_POSIX_C_SOURCE=200809L", + "-Iinclude", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}", + "./build/lib/libnickel.a" + ], + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..20c68e6 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +include make/config.mk +include make/toolchain.mk +include make/rules.mk \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f008e4 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +### Core “make C nicer” utilities + +1. **`xmalloc/xcalloc/xrealloc/xstrdup` (header: `xalloc.h`)** + + * Teaches: error handling patterns, `errno`, consistent OOM behavior. + +2. **Arena allocator (header: `arena.h`)** + + * `arena_init`, `arena_alloc`, `arena_reset`, `arena_free` + * Teaches: alignment, bump allocators, lifetime-based allocation. + +3. **Dynamic buffer / string builder (header: `buf.h`)** + + * `buf_reserve`, `buf_append`, `buf_appendf` + * Teaches: amortized growth, `vsnprintf`, safe formatting. + +4. **Slice + spans (header: `span.h`)** + + * `typedef struct { uint8_t* p; size_t n; } span_u8;` etc. + * Teaches: safer APIs than raw pointers, length-carrying. + +5. **Hash map (header: `hashmap.h`)** (string→void* or string→u64) + + * Teaches: hashing, open addressing, tombstones, resizing. + +6. **Bitset (header: `bitset.h`)** + + * `bitset_set/clear/test`, `bitset_find_first_zero` + * Teaches: word operations, popcount/ctz if you want. + +### C11 correctness features + +7. **Compile-time assertions & type checks (header: `ctassert.h`)** + + * Wrap `_Static_assert`, plus some `_Generic` helpers. + * Teaches: compile-time constraints, “making invalid states unrepresentable”. + +8. **`_Generic` logging macros (header: `log.h`)** + + * `LOG_INFO("x=%d", x)` with file/line, optional type-based formatting helpers. + * Teaches: variadic macros, `_Generic`, ergonomics. + +9. **Defer/cleanup pattern (header: `defer.h`)** + + * Macro that runs cleanup at scope end (via `goto` pattern). + * Teaches: structured cleanup in C, error paths. + +10. **Optional result type (header: `result.h`)** + +* `typedef struct { int ok; int err; T value; } result_T;` pattern. +* Teaches: explicit error handling, no hidden global state. + +### Concurrency & atomics (C11’s “real” new power) + +11. **Atomic reference counting (header: `refcnt.h`)** + +* `ref_inc/ref_dec` with destructor callback. +* Teaches: `stdatomic.h`, memory ordering (start with seq_cst, then learn relax/acq_rel). + +12. **Lock-free SPSC ring buffer (header: `ring_spsc.h`)** + +* Single-producer/single-consumer queue. +* Teaches: atomics, cache friendliness, correctness reasoning. + +13. **Thread pool (header: `threadpool.h`)** using `threads.h` + +* `tp_init`, `tp_submit`, `tp_join`, `tp_destroy` +* Teaches: `thrd_t`, `mtx_t`, `cnd_t`, work queues. + +14. **Once-init & singletons (header: `once.h`)** + +* Use `once_flag` / `call_once` (or implement if platform lacks). +* Teaches: init races, safe global setup. + +### Parsing / CLI tools (you’ll actually use these) + +15. **Argument parser (header: `argparse.h`)** + +* `--long`, `-s`, combined short flags, `--key=value` +* Teaches: string parsing, API design, test cases. + +16. **INI parser (header: `ini.h`)** + +* Minimal: sections, key=value, comments. +* Teaches: parsing state machines, callbacks. + +17. **CSV reader/writer (header: `csv.h`)** + +* Correct quoting/escaping. +* Teaches: edge cases, streaming parsing. + +18. **Path utilities (header: `path.h`)** + +* `path_join`, `path_dirname`, `path_basename`, normalize `..` and `.` +* Teaches: portability pitfalls, careful string ops. + +### Systems-ish building blocks + +19. **File mapping / buffered reader (header: `io.h`)** + +* Portable-ish wrapper for “read whole file”, “iter lines”, “atomic write via temp+rename”. +* Teaches: robust file I/O patterns, error handling. + +20. **Timing + profiling helpers (header: `timeutil.h`)** + +* `now_ns()`, `stopwatch`, `scope_timer` macro +* Teaches: `timespec_get`, measurement pitfalls, microbenchmark hygiene. + +--- + +## A suggested “crash course” order (so it builds) + +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) + +--- + +## Two capstone tool ideas that use a lot of the above + +* **`logscan`**: fast log parser + filter + histogram (buf + argparse + csv + hashmap + timeutil) +* **`dedupe`**: file deduplicator by hashing chunks (io + hashmap + arena + threadpool) + +--- diff --git a/include/nickel/arena.h b/include/nickel/arena.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/argparse.h b/include/nickel/argparse.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/bitset.h b/include/nickel/bitset.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/buf.h b/include/nickel/buf.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/ctassert.h b/include/nickel/ctassert.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/defer.h b/include/nickel/defer.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/hashmap.h b/include/nickel/hashmap.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/ini.h b/include/nickel/ini.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/io.h b/include/nickel/io.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/linkedlist.h b/include/nickel/linkedlist.h new file mode 100644 index 0000000..5256d0c --- /dev/null +++ b/include/nickel/linkedlist.h @@ -0,0 +1,62 @@ +#include +#include + +#ifndef __NICKEL_LINKEDLIST_H_ +#define __NICKEL_LINKEDLIST_H_ + +typedef struct ni_list_node { + struct ni_list_node* next; + struct ni_list_node* prev; +} ni_list_node; + +// sentinel type structure for head + count +typedef struct ni_list { + ni_list_node head; + size_t count; +} ni_list; + +// convenience ptr wrapper struct for a 'generic' list of pointers to things +typedef struct ni_list_ptr { + ni_list_node link; + void* ptr; +} ni_list_ptr; + +// List functions +void ni_list__init(ni_list* l); +bool ni_list__is_empty(ni_list* l); + +void ni_list__insert_after(ni_list* l, ni_list_node* entry, ni_list_node* n); +void ni_list__insert_before(ni_list* l, ni_list_node* entry, ni_list_node* n); + +void ni_list__push_front(ni_list* l, ni_list_node* n); +void ni_list__push_back(ni_list* l, ni_list_node* n); + +void ni_list__remove(ni_list* l, ni_list_node* n); + +static inline ni_list_node* ni_list__get_front(ni_list* l) { + // if l is_empty() front == NULL else return the first actual data baring node + return ni_list__is_empty(l) ? NULL : l->head.next; +} + +static inline ni_list_node* ni_list__get_back(ni_list* l) { + // if l is_empty() front == NULL else return the last node in the list + return ni_list__is_empty(l) ? NULL : l->head.prev; +} + +/* Iter Helpers */ + +#define NI_LIST__FOREACH(lptr) \ + for (ni_list_node* it = ni_list__get_front(lptr); it != &(lptr)->head; it = it->next) + +#define NI_LIST_FOREACH_SAFE(tmp, lptr) \ + for (ni_list_node* it = ni_list__get_front(lptr), *tmp = it->next; \ + it != &(lptr)->head; \ + it = tmp, tmp = it-> next) + +// create a pointer to struct that contains a ni_list_node member +// since we know the structure size at compile time we can do some math +// to retreive the base address of the structure that contains our list node. +#define NI_LIST_CONTAINER_OF(ptr, type, member) \ + ((type*)((uint8_t*)(ptr) - offsetof(type, member))) + +#endif /* __NICKEL_LINKEDLIST_H_ */ \ No newline at end of file diff --git a/include/nickel/log.h b/include/nickel/log.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/refcnt.h b/include/nickel/refcnt.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/result.h b/include/nickel/result.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/ring_spsc.h b/include/nickel/ring_spsc.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/span.h b/include/nickel/span.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/threadpool.h b/include/nickel/threadpool.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/timeutil.h b/include/nickel/timeutil.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/version.h b/include/nickel/version.h new file mode 100644 index 0000000..e69de29 diff --git a/include/nickel/xalloc.h b/include/nickel/xalloc.h new file mode 100644 index 0000000..e69de29 diff --git a/make/config.mk b/make/config.mk new file mode 100644 index 0000000..3ec0ec8 --- /dev/null +++ b/make/config.mk @@ -0,0 +1,7 @@ +# Project identity +PROJ := nickel +BUILD_DIR := build +INC_DIRS := include + +# Default build mode: release | debug | asan | ubsan +MODE ?= debug diff --git a/make/rules.mk b/make/rules.mk new file mode 100644 index 0000000..fcc2429 --- /dev/null +++ b/make/rules.mk @@ -0,0 +1,48 @@ +LIB := $(BUILD_DIR)/lib/libnickel.a +BIN_DIR := $(BUILD_DIR)/bin +OBJ_DIR := $(BUILD_DIR)/obj + +NICKEL_SRCS := $(wildcard src/nickel/*.c) +NICKEL_OBJS := $(patsubst src/%.c,$(OBJ_DIR)/%.o,$(NICKEL_SRCS)) + +TEST_SRCS := $(wildcard tests/*.c) +TEST_BIN := $(BIN_DIR)/tests + +TOOL_BINS := \ + #$(BIN_DIR)/logscan \ + #$(BIN_DIR)/dedupe + +.PHONY: all clean test tools examples + +all: $(LIB) tools $(TEST_BIN) + +$(LIB): $(NICKEL_OBJS) + @mkdir -p $(dir $@) + $(AR) rcs $@ $^ + $(RANLIB) $@ + +$(OBJ_DIR)/%.o: src/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +# Tests link against libnickel.a +$(TEST_BIN): $(TEST_SRCS) $(LIB) + @mkdir -p $(BIN_DIR) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TEST_SRCS) $(LIB) $(LDLIBS) + +# Tools (one main.c each; expand as needed) +#$(BIN_DIR)/logscan: tools/logscan/main.c $(LIB) +# @mkdir -p $(BIN_DIR) +# $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS) + +#$(BIN_DIR)/dedupe: tools/dedupe/main.c $(LIB) +# @mkdir -p $(BIN_DIR) +# $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS) + +test: $(TEST_BIN) + $(TEST_BIN) + +tools: $(TOOL_BINS) + +clean: + rm -rf $(BUILD_DIR) diff --git a/make/toolchain.mk b/make/toolchain.mk new file mode 100644 index 0000000..e2f014e --- /dev/null +++ b/make/toolchain.mk @@ -0,0 +1,31 @@ +CC ?= cc +AR ?= ar +RANLIB ?= ranlib + +CSTD := -std=c11 +WARN := -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wstrict-prototypes -Wmissing-prototypes +DEFS := -D_POSIX_C_SOURCE=200809L + +INCS := $(addprefix -I,$(INC_DIRS)) + +CFLAGS_COMMON := $(CSTD) $(WARN) $(DEFS) $(INCS) +LDFLAGS_COMMON := +LDLIBS_COMMON := + +ifeq ($(MODE),release) + CFLAGS_MODE := -O2 -DNDEBUG +else ifeq ($(MODE),debug) + CFLAGS_MODE := -O0 -g3 +else ifeq ($(MODE),asan) + CFLAGS_MODE := -O1 -g3 -fsanitize=address -fno-omit-frame-pointer + LDFLAGS_MODE := -fsanitize=address +else ifeq ($(MODE),ubsan) + CFLAGS_MODE := -O1 -g3 -fsanitize=undefined -fno-omit-frame-pointer + LDFLAGS_MODE := -fsanitize=undefined +else + $(error Unknown MODE=$(MODE)) +endif + +CFLAGS := $(CFLAGS_COMMON) $(CFLAGS_MODE) +LDFLAGS := $(LDFLAGS_COMMON) $(LDFLAGS_MODE) +LDLIBS := $(LDLIBS_COMMON) \ No newline at end of file diff --git a/src/nickel/linkedlist.c b/src/nickel/linkedlist.c new file mode 100644 index 0000000..bd35c92 --- /dev/null +++ b/src/nickel/linkedlist.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include "nickel/linkedlist.h" + +void ni_list__init(ni_list* l) +{ + l->head.next = &l->head; + l->head.prev = &l->head; + l->count = 0; +} + +bool ni_list__is_empty(ni_list* l) { + // only accept if head.next is head (circular head) + // as the indicator of empty-ness. + return l->head.next == &l->head; +} + +void ni_list__insert_after(ni_list* l, ni_list_node* entry, ni_list_node* n) { + // setup our incomming node as the next node after the insert point + n->next = entry->next; + n->prev = entry; + + // update the following node to point at our new next node + entry->next->prev = n; + + // update the insert point to point at the new node + entry->next = n; + l->count++; +} + +void ni_list__insert_before(ni_list* l, ni_list_node* entry, ni_list_node* n) { + // setup our incomming node as the next node before the insert point + n->prev = entry->prev; + n->next = entry; + + // update the previous node to point at our new next node + entry->prev->next = n; + + // update our insert point with the new prev + entry->prev = n; + l->count++; +} + +void ni_list__push_front(ni_list* l, ni_list_node* n) { + ni_list__insert_after(l, &l->head, n); +} + +void ni_list__push_back(ni_list* l, ni_list_node* n) { + ni_list__insert_before(l, &l->head, n); +} + +void ni_list__remove(ni_list* l, ni_list_node* n) { + // set node before our node to point back to the node after n. + n->prev->next = n->next; + + // set node after our node to point back to the node before n + n->next->prev = n->prev; + + // poison our removed node + n->next = NULL; + n->prev = NULL; + + l->count--; +} \ No newline at end of file diff --git a/tests/test_linkedlist b/tests/test_linkedlist new file mode 100755 index 0000000000000000000000000000000000000000..710b6bb38aac161e49e35275c8a6b34df4d7ad93 GIT binary patch literal 55736 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 jnA1fm_{!JCv%Zm2d2ahuKjbU_1PK# +#include +#include +#include +#include +#include + +#include "nickel/linkedlist.h" + +// helper function to format bytes out. +char* get_bytes_f(long bytes, char* str) { + if(str != NULL) { + if (bytes > 0) + { + if (bytes < 1024) { + sprintf(str, "%ld bytes", bytes); + } + if ((bytes >= 1024) & (bytes < 1024*1024)) { + sprintf(str, "%.3f Kbytes", ((double)bytes/(double)1024.00)); + } + if (bytes >= 1024*1024) { + sprintf(str, "%.3f Mbytes",(double)bytes/(double)(1024*1024)); + } + } + } + else { + return NULL; + } + return str; +} + +typedef struct test_data { + uint64_t data1; + uint64_t data2; + uint64_t data3; + uint64_t data4; + + ni_list_node link; +} test_data; + +int main (void) +{ + time_t time_now; + srand(time(&time_now)); + + ni_list data_instances; + ni_list__init(&data_instances); + + printf("list is allocated at %p\n", (void*)&data_instances); + size_t test_size = 10; + for(size_t i = 0; i < test_size; i++) { + test_data* t = malloc(sizeof(test_data)); + printf("n = %d allocated data...link at %p...\n", i, &t->link); + + t->data1 = rand() % UINT64_MAX; + t->data2 = rand() % UINT64_MAX; + t->data3 = rand() % UINT64_MAX; + t->data4 = rand() % UINT64_MAX; + + ni_list__push_back(&data_instances, &(t->link)); + + printf("write: n = %d: %ld %ld %ld %ld \n\tnext: %p\n\tprev: %p\n", + i, t->data1, t->data2, t->data3, t->data4, t->link.next, t->link.prev); + } + + printf("Inserted %d count entries into test list...\n", data_instances.count); + + { // scope for access to iter and list + size_t i = 0; + NI_LIST__FOREACH(&data_instances) { + test_data* t = NI_LIST_CONTAINER_OF(it, test_data, link); + printf("read: %d: %ld %ld %ld %ld \n\tnext: %p\n\tprev: %p\n", + i, t->data1, t->data2, t->data3, t->data4, (void*)it->next, (void*)it->prev); + i++; + } + } + + while (!ni_list__is_empty(&data_instances)) { + ni_list_node* tail = ni_list__get_back(&data_instances); + ni_list__remove(&data_instances, tail); + + test_data* t = NI_LIST_CONTAINER_OF(tail, test_data, link); + free(t); + } + + /* all done! */ +} \ No newline at end of file