C Fundamentals
Algorithms Β· data structures Β· cryptography Β· systems β€” pure C11, zero deps
Loading...
Searching...
No Matches
C Fundamentals

<picture> <source media="(prefers-color-scheme: dark)" srcset="assets/hero-banner-dark.svg" > C Fundamentals </picture>

CI API docs Open source C11 1009 test assertions ASan + UBSan clean MIT License

πŸ“– API docs (Doxygen)

A reference implementation of the algorithms, data structures, cryptographic primitives, and systems calls every C programmer eventually writes by hand. Seven sorts, binary search, four crypto primitives (Caesar with chi-squared cracker, Vigenère, XOR, SHA-256), seven data structures (linked list, stack, queue, BST, heap / priority queue, trie, hash table), POSIX systems demos. Zero dependencies, 1009 test assertions, GCC + Clang clean on Linux + macOS, AddressSanitizer + UndefinedBehaviorSanitizer in CI.


<tt>/// WHAT'S INSIDE</tt>

Module Highlights
Sorting Selection Β· Bubble Β· Insertion Β· Quicksort (Lomuto) Β· Merge sort (top-down with reusable aux buffer) Β· Heap sort (in-place) Β· Radix sort (LSD, byte-by-byte counting)
Searching Binary search β€” iterative + recursive, size_t-safe midpoint, both int and string variants
Encryption Caesar cipher with chi-squared frequency-analysis cracker · Vigenère polyalphabetic cipher · XOR stream cipher · SHA-256 from scratch (FIPS 180-4, validated against NIST test vectors)
Data structures Singly-linked list Β· Stack (growable array) Β· Queue (circular buffer with O(1) push/pop) Β· Binary search tree Β· Min-heap / priority queue (with linear heap_build) Β· Trie (26-way prefix tree) Β· Open-addressing hash table (djb2, linear probing, tombstones, auto-resize)
Systems Cross-platform sysinfo (uname, sysctl, sysconf, getloadavg, statvfs) Β· signal-demo showing graceful SIGINT/SIGTERM handling via sigaction + volatile sig_atomic_t
Benchmarks Empirical comparison of all seven sorts on n={100, 1k, 10k} random integers, deterministic seed

<tt>/// QUICK START</tt>

git clone https://github.com/hatimhtm/c-fundamentals.git
cd c-fundamentals
make all # 8 binaries into build/
make test # 1009 assertions across 13 test suites
make smoke # run every CLI once with sample input
make asan # rebuild with AddressSanitizer + run tests
make ubsan # rebuild with UndefinedBehaviorSanitizer + run tests

Compiler swap: make clean && make all CC=clang. Custom flags: ‘make CFLAGS=’-O3 -march=native ...'(the defaults already include-Wall -Wextra -Werror -pedantic`).


<tt>/// SORTING</tt>

./build/sorting --algo=quick zebra ant mouse cat
./build/sorting --algo=heap apple zebra mango
echo -e "foo\nbar\nbaz" | ./build/sorting --algo=merge

Algorithms: selection Β· insertion Β· bubble Β· quick Β· merge Β· heap Β· radix (radix is integers-only, others have both *_ints and *_strings).

Sample benchmark output

| Algorithm | n=100 | n=1000 | n=10000 |
|------------|-----------|-----------|-----------|
| Selection | 0.01ms | 0.58ms | 72.73ms |
| Bubble | 0.02ms | 1.59ms | 251.43ms |
| Insertion | 0.00ms | 0.41ms | 20.51ms |
| Quicksort | 0.01ms | 0.07ms | 0.84ms |
| Merge | 0.04ms | 0.08ms | 0.82ms |

Run ./build/benchmark to regenerate; deterministic seed = 42.


<tt>/// SEARCHING</tt>

./build/bsearch 7 3 1 9 7 4 2
# Sorted: [1, 2, 3, 4, 7, 9]
# Target: 7
# Result: found at index 4

Iterative + recursive variants, tested for equivalence on every value 1..21 against a known sorted array.


<tt>/// ENCRYPTION</tt>

# Caesar
./build/crypto-cli -e 3 "Hello World" # Khoor Zruog
./build/crypto-cli -c "Khoor Zruog" # β†’ Guessed shift: 3 (chi-squared)
# Vigenère
./build/crypto-cli -v --key=lemon "attack at dawn" # lxfopv ef rnhr
# XOR (encrypt = decrypt; output is hex since it may contain non-printables)
./build/crypto-cli -x --key=secret "hello"
# SHA-256
./build/crypto-cli -s "abc"
# ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

The Caesar cracker uses chi-squared distance against standard English letter frequencies (not the naive "most-common-letter == E" approach). The SHA-256 implementation is straight from FIPS 180-4 β€” validated against NIST test vectors including the empty string, "abc", and the 56-byte boundary-padding example.


<tt>/// DATA STRUCTURES</tt>

Each structure is a self-contained library β€” no inter-module deps except where noted (heap sort delegates max-sift-down to data_structures/heap).

Structure API surface Notes
linked_list push_front/back, pop_front, find, reverse, size Singly-linked, owns nodes
cf_stack push, pop, peek, size, is_empty Growable array, doubles on full (renamed from stack to dodge POSIX stack_t)
queue enqueue, dequeue, peek, size Circular buffer, head + size for full/empty disambiguation
bst insert, contains, min, max, in_order traversal Plain binary search tree (no balancing)
heap insert, extract_min, peek_min, build_min (O(n)) Min-heap; also exposes max-sift-down for heap sort
trie insert, contains, starts_with 26-way prefix tree, lowercase a-z
hash_table set, get, remove, contains, size Open addressing, djb2, linear probing, tombstones, auto-resize at 0.75 load

CLI demos for linked_list and hash_table. The rest are exercised by tests:

./build/linked-list-demo 1 2 3 4 5
./build/hash-table-demo the cat sat on the mat

<tt>/// SYSTEMS</tt>

./build/sysinfo -v
# OS, hostname, release, arch
# User: name / home / shell / UID / GID
# CPU info (Apple sysctlbyname or Linux sysconf)
# Memory: hw.memsize / sysconf(_SC_PHYS_PAGES)
# Load average via getloadavg()
# Disk: total / used / available / percent via statvfs()
./build/signal-demo 30
# Demonstrates graceful SIGINT/SIGTERM/SIGHUP handling via sigaction.
# Press Ctrl-C to trigger graceful shutdown.

sysinfo branches on __APPLE__ for sysctlbyname (CPU brand, RAM); falls back to sysconf on Linux/BSD. signal-demo is a textbook example of the safe pattern: handler sets a volatile sig_atomic_t flag, main loop polls.


<tt>/// PROJECT LAYOUT</tt>

c-fundamentals/
β”œβ”€β”€ algorithms/
β”‚ β”œβ”€β”€ sorting/ 7 sorts + benchmark + sorts.h + main
β”‚ β”œβ”€β”€ searching/ binary_search.c/.h + main
β”‚ └── encryption/ caesar, vigenere, xor_cipher, sha256 + main (crypto-cli)
β”œβ”€β”€ data_structures/
β”‚ β”œβ”€β”€ linked_list/ library + demo
β”‚ β”œβ”€β”€ stack/ library (cf_stack_t)
β”‚ β”œβ”€β”€ queue/ library
β”‚ β”œβ”€β”€ bst/ library
β”‚ β”œβ”€β”€ heap/ library (also fuels heap sort)
β”‚ β”œβ”€β”€ trie/ library
β”‚ └── hash_table/ library + demo
β”œβ”€β”€ systems/
β”‚ β”œβ”€β”€ sysinfo/ POSIX + Apple sysctl
β”‚ └── signals/ signal-demo (sigaction, sig_atomic_t)
β”œβ”€β”€ tests/ 13 suites, 1009 assertions, hand-rolled macros (zero deps)
β”œβ”€β”€ .github/workflows/ GCC Γ— Clang Γ— Linux Γ— macOS + ASan/UBSan jobs
β”œβ”€β”€ assets/ hero banner SVGs
β”œβ”€β”€ Makefile 8 binaries + test + smoke + asan + ubsan + install
└── README.md

<tt>/// TESTING</tt>

Hand-rolled assertion macros β€” tests/test.h defines ASSERT, ASSERT_EQ_INT, ASSERT_EQ_STR, ASSERT_EQ_SIZE, ASSERT_TRUE/FALSE, ASSERT_NULL/NOT_NULL. Failures print location + message but do not abort, so a single make test run reports every failure in every suite at once.

$ make test
…
────────────────────
Total assertions: 1009
Failures: 0
βœ“ All tests passed.
Suite What it covers
test_sorts 7 algorithms Γ— 2 type variants + edge cases (empty / 1 / sorted / reversed)
test_search Iterative Β· recursive Β· empty Β· missing Β· strings Β· iter-vs-recursive equivalence
test_caesar Encrypt Β· decrypt Β· negative + large shifts Β· punctuation Β· round-trip Β· chi-squared crack
test_vigenere Textbook example Β· case preserved Β· round-trip Β· empty key Β· punctuation passthrough
test_xor Round-trip Β· zero-key identity Β· empty-key passthrough
test_sha256 NIST vectors: empty string, "abc", 56-byte boundary; hex format invariants
test_linked_list Create Β· push front/back Β· find Β· reverse Β· pop empty
test_stack LIFO order Β· peek Β· pop empty Β· resize stress to 100 elements
test_queue FIFO order Β· peek Β· circular wraparound + resize stress
test_bst Insert Β· contains Β· duplicate handling Β· min/max Β· in-order yields sorted
test_heap Min-priority extraction Β· peek Β· empty cases Β· O(n) build_min
test_trie Insert Β· contains Β· starts_with Β· rejects non-{a-z} input
test_hash_table Create Β· set/get Β· overwrite Β· remove Β· resize-rehash Β· slot reuse after remove

<tt>/// CI</tt>

Job Matrix
build {ubuntu-latest, macos-latest} Γ— {gcc, clang} β€” build, run tests, run smoke
sanitizers ubuntu-latest Γ— clang β€” full test suite under -fsanitize=address then -fsanitize=undefined; halts on the first UB it finds

Every push to main and every PR triggers all five jobs. Concurrency group cancels superseded runs.


<tt>/// CONTRIBUTING</tt>

Pull requests welcome. The code style is K&R-ish, two-space indent, snake_case, doc comments on every public function. Please:

  1. Keep -Wall -Wextra -Werror -pedantic clean on both GCC and Clang.
  2. Add tests for new code β€” at minimum, one positive case + one edge case.
  3. Run make asan && make ubsan before opening a PR.
  4. Don't introduce dependencies. If you need a hash function, write it inline.

<tt>/// LICENSE</tt>

[MIT](LICENSE) β€” use it, fork it, ship something with it.


Portfolio Book a call LinkedIn Email

///  OPEN FOR NEW WORK  ///  CONTRACT & FREELANCE  ///  REMOTE WORLDWIDE  ///