Prep: wc and grep — 12c · 13c¶
Session: Fri Sep 25, 1h30 · Exercises: 12c_wc · 13c_grep · Prep time: ~30 min · Lecture: Buffers, Bytes, and Line-Oriented I/O
What you will build¶
Two filters, each last session's copy loop plus one idea. wc streams input through a fixed buffer and prints lines, words, and bytes per file, with a total row for several; all it remembers between bytes is whether it is inside a word. grep reads lines through ulib::Lines, prints those in which a fixed byte pattern occurs, prefixes hits with name: only for two or more files, and reports through its exit status: 0 matched, 1 nothing matched, 2 something went wrong.
Concepts you need¶
- Streaming with O(1) state — count a word at the whitespace-to-word transition; one
booland three counters suffice at any file size. Buffers & Bytes §7 - Bytes, not characters — a line is a
\nbyte, a word is a run of non-whitespace bytes,écounts as 2; arguments and file content are&[u8]. Buffers & Bytes §5 - A line iterator that never allocates —
Lines::new(fd, &mut buf)borrows your buffer;next_line()returns each line without its\n. Buffers & Bytes §6 · ulib and Commands §API surface - Three substring-search edge cases — the empty pattern occurs everywhere; a longer one cannot, and subtracting
usizelengths first panics; the last legal start is haystack length minus pattern length, so the range is inclusive. Buffers & Bytes §7 - Exit status is output — "nothing matched" is a successful no, which makes
grep -q x f && …work; thei32your program returns is that status. Buffers & Bytes §7
Read before class¶
| What | Time |
|---|---|
| Buffers & Bytes §5–§7 | 15 min |
| Practice Problem 3 and Problem 4, answers closed | 10 min |
| ulib and Commands: API surface · Portability rules | 5 min |
Mental model¶
Count the runs of digits in a byte stream, remembering one bit:
// "a1b22c333" -> 3 runs
let mut runs = 0;
let mut in_run = false; // the one bit carried across bytes and reads
for &b in chunk {
match (b.is_ascii_digit(), in_run) {
(true, false) => { runs += 1; in_run = true; } // a run begins: count it
(true, true) => {} // inside a counted run
(false, _) => in_run = false, // run over
}
}
Split the input into a1b2 and 2c333: still 3, because in_run summarizes every byte already seen, so chunk boundaries are invisible, and end of input needs no special case since a run is counted when it starts. The UART driver and shell tokenizer you write later are this machine with a different predicate.
Check yourself¶
printf 'a b' | wcprints what, and why not 1 line?Answer
0 2 4. A line is a newline byte and there is none; two spaces are one separator;bwas counted when it began.grepprints nothing and every file opened. Exit status, and why not 0?Answer
1, a successful run answering no. Returning 0 either way would break&&chains; 2 means something went wrong.- Searching for
xinabcx, which start positions must you try? Now search forabcdefghinabc.Answer
0 through 3, and 3 is 4 − 1, so the range is0..=n. Then 3 − 8 onusizepanics: rule out a longer pattern before subtracting, and the empty pattern before that.
What "done" looks like¶
oslings run is green, then oslings submit before you leave. Not green? Submit anyway (substantial credit), then finish by Monday 11:59 pm and submit again.
Extra credit today¶
14c_head (+0.5): parse -n COUNT by hand with checked_mul, print lines through Lines, and stop reading once you have enough; next_line reads lazily, so the stopping belongs in the loop bound. Buffers & Bytes §7
If you finish early¶
Rustlings iterators, lifetimes, strings and 100 Exercises chapter 6, Ticket Management cover the borrow behind next_line. Next Thursday needs QEMU installed; check the setup page now.