first commit, initial layout and some ideas I want to implement, included my linked list impl
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
build/
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.d
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
.DS_Store
|
||||||
16
.vscode/c_cpp_properties.json
vendored
Normal file
16
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -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
|
||||||
|
}
|
||||||
37
.vscode/tasks.json
vendored
Normal file
37
.vscode/tasks.json
vendored
Normal file
@@ -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"
|
||||||
|
}
|
||||||
3
Makefile
Normal file
3
Makefile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
include make/config.mk
|
||||||
|
include make/toolchain.mk
|
||||||
|
include make/rules.mk
|
||||||
122
README.md
Normal file
122
README.md
Normal file
@@ -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)
|
||||||
|
|
||||||
|
---
|
||||||
0
include/nickel/arena.h
Normal file
0
include/nickel/arena.h
Normal file
0
include/nickel/argparse.h
Normal file
0
include/nickel/argparse.h
Normal file
0
include/nickel/bitset.h
Normal file
0
include/nickel/bitset.h
Normal file
0
include/nickel/buf.h
Normal file
0
include/nickel/buf.h
Normal file
0
include/nickel/ctassert.h
Normal file
0
include/nickel/ctassert.h
Normal file
0
include/nickel/defer.h
Normal file
0
include/nickel/defer.h
Normal file
0
include/nickel/hashmap.h
Normal file
0
include/nickel/hashmap.h
Normal file
0
include/nickel/ini.h
Normal file
0
include/nickel/ini.h
Normal file
0
include/nickel/io.h
Normal file
0
include/nickel/io.h
Normal file
62
include/nickel/linkedlist.h
Normal file
62
include/nickel/linkedlist.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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_ */
|
||||||
0
include/nickel/log.h
Normal file
0
include/nickel/log.h
Normal file
0
include/nickel/refcnt.h
Normal file
0
include/nickel/refcnt.h
Normal file
0
include/nickel/result.h
Normal file
0
include/nickel/result.h
Normal file
0
include/nickel/ring_spsc.h
Normal file
0
include/nickel/ring_spsc.h
Normal file
0
include/nickel/span.h
Normal file
0
include/nickel/span.h
Normal file
0
include/nickel/threadpool.h
Normal file
0
include/nickel/threadpool.h
Normal file
0
include/nickel/timeutil.h
Normal file
0
include/nickel/timeutil.h
Normal file
0
include/nickel/version.h
Normal file
0
include/nickel/version.h
Normal file
0
include/nickel/xalloc.h
Normal file
0
include/nickel/xalloc.h
Normal file
7
make/config.mk
Normal file
7
make/config.mk
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Project identity
|
||||||
|
PROJ := nickel
|
||||||
|
BUILD_DIR := build
|
||||||
|
INC_DIRS := include
|
||||||
|
|
||||||
|
# Default build mode: release | debug | asan | ubsan
|
||||||
|
MODE ?= debug
|
||||||
48
make/rules.mk
Normal file
48
make/rules.mk
Normal file
@@ -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)
|
||||||
31
make/toolchain.mk
Normal file
31
make/toolchain.mk
Normal file
@@ -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)
|
||||||
67
src/nickel/linkedlist.c
Normal file
67
src/nickel/linkedlist.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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--;
|
||||||
|
}
|
||||||
BIN
tests/test_linkedlist
Executable file
BIN
tests/test_linkedlist
Executable file
Binary file not shown.
87
tests/test_linkedlist.c
Normal file
87
tests/test_linkedlist.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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! */
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user