wordstat — C to Rust port

Rust port documented with evidence. 9 observations · 1 inference · 1 open unknown · 6 screenshots. Source ↗

Port plan

PORT.md — wordstat (C → Rust)

A deliberately small port that exercises the whole PortCode workflow on a real original: pin, golden harness, differential fuzz, parity, screenshots and the generated site. Every claim cites an evidence id from the ledger below (E-xxxx).

1. Source

2. Scope

3. Module map

SourceLOCRustStrategyEvidenceStatus
main arg loop15wordstat_core::runrewriteE-0002, E-0006done
tokenizer + counting25wordstat_core::countrewriteE-0004, E-0010done
qsort(cmp) + report15wordstat_core::runrewrite (sort_by count desc, word asc)E-0002done
atoilibcwordstat_core::atoirewrite (saturating)E-0009done

4. Dependency map

Source depRust replacementWhy / risk
libc isalnum/toloweru8::is_ascii_alphanumeric/to_ascii_lowercaseidentical in the C locale; see E-0011
printf("%.3f")format!("{:.3}")both round the exact binary value; checked by fuzz

5. Hard problems

ProblemApproachAlternatives
Original quirks (negative --top, value-less flag becomes the path, 255-byte word cap)reproduce, unit-test each"fix" them — rejected, fidelity goal is bit-exact

6. Golden cases (tests/golden/*.txt, produced by tests/run_cases.sh original/wordstat)

CaseCommandEvidencePort status
defaultwordstat inputs/sample.txtE-0002 / E-0003PASS
top3--top 3E-0008PASS
minlen5--min-len 5 --top 4E-0004 / E-0005PASS
emptyempty fileE-0008PASS
missingnonexistent file → stderr, exit 1E-0006 / E-0007PASS
usageno args → usage, exit 2E-0008PASS

7. Milestones

8. Open unknowns

9. Decision log

Evidence by feature

cli.default

observationE-0002High
original: report for inputs/sample.txt (exit 0)
sha256 219ec2e9f812f9cb
via screenshot original/wordstat inputs/sample.txt
E-0002: original: report for inputs/sample.txt (exit 0)
E-0002 — screenshots/orig-default.svg
observationE-0003High
port: identical report for inputs/sample.txt (exit 0)
sha256 219ec2e9f812f9cb
via screenshot port/target/release/wordstat inputs/sample.txt
E-0003: port: identical report for inputs/sample.txt (exit 0)
E-0003 — screenshots/port-default.svg

cli.errors

observationE-0006High
original: missing file -> stderr message, exit 1
sha256 cf0f0634eda459c4
via screenshot original/wordstat inputs/does-not-exist.txt
E-0006: original: missing file -> stderr message, exit 1
E-0006 — screenshots/orig-missing.svg
observationE-0007High
port: same stderr message and exit code 1
sha256 cf0f0634eda459c4
via screenshot port/target/release/wordstat inputs/does-not-exist.txt
E-0007: port: same stderr message and exit code 1
E-0007 — screenshots/port-missing.svg

cli.locale

unknownE-0011Low
The original calls isalnum()/tolower(), which are locale-dependent for bytes >= 128; all goldens were captured in the C locale. The port assumes ASCII only.
via read original/wordstat.c

cli.options

observationE-0004High
original: --min-len 5 --top 4
sha256 40036497b5dcbcba
via screenshot original/wordstat --min-len 5 --top 4 inputs/sample.txt
E-0004: original: --min-len 5 --top 4
E-0004 — screenshots/orig-minlen5.svg
observationE-0005High
port: identical output for --min-len 5 --top 4
sha256 40036497b5dcbcba
via screenshot port/target/release/wordstat --min-len 5 --top 4 inputs/sample.txt
E-0005: port: identical output for --min-len 5 --top 4
E-0005 — screenshots/port-minlen5.svg
inferenceE-0010Medium
A word is kept to at most 255 bytes (char buf[256], wordstat.c:31); longer runs still count once. Read from source; the fuzz includes 250-300 byte words and agrees.
via read original/wordstat.c

original.build

observationE-0001High
original/wordstat is Mach-O (34232 bytes), sha256 ec3f59ff898efa2dbd2af59a7a75322219423368fb9d251f52394089017acf63, version 1.0
sha256 ec3f59ff898efa2d
via pin

parity.fuzz

observationE-0009High
differential fuzz: 400/400 identical (stdout, stderr and exit code byte-identical)
via tests/fuzz.py 400 random files, seed 1234, NUL/0xFF bytes, 250-300 byte words, odd flags

parity.golden

observationE-0008High
parity: ok=6 diff=0 missing=0 extra=0 RESULT: PASS
via portcode port parity tests/golden /tmp/ws_port_out

Open unknowns

unknownE-0011Low
The original calls isalnum()/tolower(), which are locale-dependent for bytes >= 128; all goldens were captured in the C locale. The port assumes ASCII only.
via read original/wordstat.c
orig-default.svg
orig-default.svg
orig-minlen5.svg
orig-minlen5.svg
orig-missing.svg
orig-missing.svg
port-default.svg
port-default.svg
port-minlen5.svg
port-minlen5.svg
port-missing.svg
port-missing.svg