Rung 2 — libprep: A Reusable C Container Library

Headline. A small, tested, sanitizer-clean C library that ships five data structures with a coherent API — the first artifact where you are designing, not just coding.

Month target. M3 (September 2026). Publish by the last day of M3.


What You Build

Repo name: libprep (the name is a wink — these are the containers you keep re-implementing in studies). Header-optional layout so the library can be used as a single-header drop-in OR built as a static archive.

Five data structures, each in its own translation unit:

  1. iv_vec — dynamic array of void* (or a type-generic macro variant). Push, pop, insert, remove, reserve, shrink-to-fit. Amortized O(1) push.

  2. iv_str — growable string builder. append, appendf (printf-style), cstr, len, clear. Guarantees NUL termination.

  3. iv_map — hashmap using open addressing with Robin Hood or linear probing. String keys, void* values. put, get, del, iter. Load-factor bounded resize.

  4. iv_heap — binary heap. Configurable min or max via comparator function pointer. push, pop, peek, heapify from array.

  5. iv_deque — double-ended queue backed by a ring buffer with power-of-two capacity. push_front, push_back, pop_front, pop_back.

Required infrastructure:

  • Test suite: one .c per data structure, using a tiny assertion macro (roll your own IV_ASSERT in ~20 LOC). No external test framework — dogfooding your ability to write test infrastructure is part of the rung.

  • Coverage: gcov + lcov producing an HTML report, checked into docs/coverage/ or hosted on GitHub Pages. Target ≥ 90% line coverage.

  • Sanitizer runs: ASan and UBSan clean on every push via GitHub Actions.

  • Fuzzing (optional but recommended): one libFuzzer or AFL++ harness for iv_map — 5000 iterations run in CI as a smoke test.

  • README with a runnable example for each of the five containers, plus an “ownership rules” section that states, in one paragraph per container, who owns the memory and when.

Target size: <phone_number_or_numberic_id_or_random_id_4>-2500 LOC of C, one README.md, five example programs in examples/.


Why This Rung, Why Now

M2 has taught you the language deep-dive: strict aliasing, undefined behavior, restrict, const correctness, translation units, linkage. Rung 2 forces you to use that knowledge to make interface decisions. Every container demands an answer to questions you never had to ask in Python: who owns the memory? What happens on failure — return NULL or abort? Are elements copied by value or referenced? Is iteration invalidated by mutation? These are not exotic questions — they are the exact questions the maintainer of any real C library gets asked in code review.

You ship in M3 because Rung 3 (NeetCode-75 in C) is much easier if you already have your own iv_map and iv_deque to lean on. Rung 2 becomes infrastructure for Rung 3.


Acceptance Criteria

  • 5 data structures, each with a public header, an implementation file, and a test file

  • ≥ 90% line coverage measured by gcov/lcov, report visible in repo

  • Zero ASan and zero UBSan errors under the full test suite

  • README has a copy-pasteable ≤ 15-line example for each container

  • Ownership semantics documented explicitly for every container (who frees what, when)

  • Every public function has a doc comment stating: pre-conditions, post-conditions, complexity, failure mode

  • Posted to r/C_Programming with title “libprep — my first C library, feedback welcome” and you engaged with at least 5 comments

  • Tagged v0.1.0 release with a CHANGELOG entry


Where to Publish

  • GitHub: pinned on profile. Add topics: c, data-structures, c99, library.

  • Reddit: r/C_Programming for a feedback thread. Do NOT post to r/programming yet — it’s a small-library rung, not a HN piece.

  • Hacker News: skip. Rung 5 and Rung 6 are your HN candidates. Do not burn HN attention on a first library.

  • Lobsters: optional; only if you have an invite.

  • Your GitHub profile README: add a “Portfolio” section and link this repo with the one-line pitch.


Signal to Recruiter / Employer

“This person can design and ship a small library. They think about API discipline, ownership discipline, and testing discipline. They are not merely a person who can compile C — they are a person who could contribute to a C codebase without breaking it.”

Rung 2 also makes you legible to hobbyist C communities. If a maintainer on r/C_Programming gives you a piece of feedback and you incorporate it in a follow-up commit, you have just performed a rehearsal of the OSS PR loop that Rung 7 will demand.


Common Failure Modes

  1. Generic macros that no one can read. You try to write a template-like #define VEC(T) and end up with 400-line macro expansions that segfault under ASan. Detection: if gcc -E output for a test file exceeds 3000 lines, you overreached — back off to void* with typedef aliases.

  2. Silent allocation failure. iv_vec_push calls realloc, realloc returns NULL, and you crash 40 lines later at a use-site. Detection: grep the code for every malloc/realloc/calloc — each must have an explicit NULL check with a documented behavior.

  3. Iterator invalidation left undocumented. Mutating during iteration is UB in your library but you never say so. Detection: every iter function must have a comment starting “Invalidated by: …”.

  4. Coverage number faked. You count lines by running the test binary once and calling it done. Detection: gcov output shows uncovered branches in error paths — those branches must be exercised (via a fault-injection macro if needed).



Estimated Hours

  • iv_vec: 6h

  • iv_str: 4h

  • iv_map: 12h (this is the hard one; open addressing + resize + iter is genuinely tricky)

  • iv_heap: 5h

  • iv_deque: 5h

  • Test suite + coverage tooling: 8h

  • Sanitizer + CI wiring: 4h

  • README + examples + polish: 8h

  • Reddit feedback iteration: 4h

Total: ~55 hours across M2-M3. Two months at ~28h/month is comfortable at 10-12h/week.


Prior-Art / Inspirations to Study First

  • nothings/stb (single-header libs by Sean Barrett) — read stb_ds.h line by line. It is the reference for a small, elegant C container header.

  • troydhanson/uthash — the hashmap-as-macros approach. Even if you don’t copy it, read the docs for the ownership discussion.

  • redis/dict.c in the Redis source — industrial-strength hashmap in production. Skim it, don’t copy it, notice how much of it is comments and invariants.


Return to README.md · Previous: 01_rung_1_toolchain_journal.md · Next: 03_rung_3_neetcode75_in_c.md