Files
fastwc/STYLEGUIDE.md
T
huntedbytheirs 6aa461f053 bench: rub it in — monsters, stdin wins, busybox retired
Suites split into benchmarks/files/{lines,words} and
benchmarks/stdin/piping, with the monsters bolted onto the lines suite:
100M lines raced against coreutils (~2x win), 1B lines solo (~6-8s,
11 GB in one pass). Stdin redirects from regular files are now mmap'd
in count_stream, so the stdin suite wins too — up to 12.00x on words.

The ratio column now reports how many times faster fastwc is, not how
much of GNU's time it used. busybox was removed from the suite: it
stopped being a challenge and started being a participation trophy.
GNU wc's lone win — 1M lines by one millisecond on hand-tuned AVX-512
assembly — is now a historical footnote, and the README says so.
2026-08-29 15:52:24 -04:00

4.1 KiB

fastwc Style Guide

The style is the law. GNU wc can afford to look like a committee designed it — it was. We're one file, one purpose, one opinion.

The language

  • C99 with POSIX. _POSIX_C_SOURCE 200809L. We target the standard, not a compiler vendor's mood.
  • Zero warnings with -Wall -Wextra. A warning is a bug you haven't fixed yet. Fix it.
  • No compiler-specific extensions unless they're guarded and the benchmark gets faster because of it. __builtin_popcountll is the one exception — it's the price of admission on every compiler that matters, and the benchmark proves it.

Format

  • 4 spaces. Not tabs, not 8 spaces. GNU uses tabs because GNU was written in 1985 by people who had strong opinions about typewriters.
  • 80 columns. If your line is longer, your idea is longer than it needs to be.
  • Braces on the next line (Allman). Everywhere. Functions, blocks, switch cases. Consistency is the only religion here, and we're devout.
  • Snake case for everything. count_stream, ws_tab, nread. No CamelCase — that's for languages that need help knowing what a word is.

Structure

  • One file: src/main.c. See CONTRIBUTING.md rule 6. This is not a suggestion.
  • Everything is static. The binary exports nothing. We're not a library, we're a verdict.
  • Types end in _t. counts_t, and not much else. GNU has a type for everything and a committee to name it. We have counts, and we count.
  • No globals except what's truly process-wide. flags and ws_tab live at file scope because they're the program's identity. Everything else gets passed around like it's hot.

The hot path

count_stream(), count_newlines(), count_words(), and the SIMD kernels (count_lw_avx512, count_lw_avx2, count_lw_sse2) are the product. The rest of the file is just the packaging.

  • Fixed stride. No per-token function calls. No isspace() in a loop — that's what the lookup table is for.
  • No allocation, no locks, no syscalls in the counting loop. The kernels are pure; parallel slices need no locks. The fread (or the single mmap) is the only syscall, and it's not yours to add to.
  • Branchless where it costs nothing. A predictable branch is fine; a mispredicted one is a lie you told the CPU.
  • Regular files are mapped, not streamed. The kernel hands us the pages; we count them, split across cores for anything past 8 MiB. This includes stdin redirects from regular files. Real pipes, ttys, and the -m path fall back to the static, 128 KiB buffer that never grows.

Counts

  • long long for everything that counts. Files can be bigger than your opinions. Signed, because overflow should scream, not wrap.
  • Lines are '\n's. Not "lines of text," not "records." A file without a trailing newline has fewer newlines than it has lines, and that's correct. GNU agrees with us on this one, so it must be right.
  • Words are whitespace-delimited runs. The lookup table decides what whitespace is; the popcount decides how many words; you don't get an opinion.

Errors

  • fastwc: <file>: <message> on stderr, and keep going. GNU wc fails gracefully across multiple files, and we'll credit them that one thing. Then we'll be faster at it.
  • Exit 1 if anything failed, 0 otherwise. No drama, no EXIT_FAILURE poetry.

Comments

  • Only when the code can't speak for itself. The SWAR newline counter in count_newlines() gets a comment because (x ^ nl) & cl is a sentence in a language you don't speak yet. i++ does not get a comment.
  • No commented-out code. The dead don't get to live in the file.
  • No TODO without a date. A TODO without a date is a promise you plan to break.

What the style is not

  • It is not GNU's style. GNU's style was written by a committee, ratified by a foundation, and translated into fourteen languages.
  • It is not "whatever your editor defaults to." Your editor has opinions. So do we. Ours are right.
  • It is not negotiable in review. If a PR violates the style, the PR is a rewrite request with extra steps.