|
7 | 7 | // SPDX-License-Identifier: MIT |
8 | 8 | // |
9 | 9 |
|
10 | | -use std::io::{self, BufWriter, Read, Write}; |
| 10 | +use std::io::{self, BufRead, BufReader, BufWriter, Write}; |
11 | 11 | use std::path::{Path, PathBuf}; |
12 | 12 |
|
13 | 13 | use clap::Parser; |
@@ -72,57 +72,65 @@ fn find_last_blank(v: &[u8]) -> Option<usize> { |
72 | 72 |
|
73 | 73 | fn fold_file(args: &Args, pathname: &Path) -> io::Result<()> { |
74 | 74 | // open file, or stdin ("-" or no operand) |
75 | | - let mut file = input_stream_dashed(pathname)?; |
76 | | - let mut data = Vec::new(); |
77 | | - file.read_to_end(&mut data)?; |
| 75 | + let mut reader = BufReader::new(input_stream_dashed(pathname)?); |
78 | 76 |
|
79 | 77 | let width = args.width as usize; |
80 | 78 | let mut out = BufWriter::new(io::stdout()); |
81 | 79 | let mut line: Vec<u8> = Vec::new(); |
82 | 80 | let mut col: usize = 0; |
83 | 81 |
|
84 | | - for ch in mb_char_slices(&data) { |
85 | | - if ch == b"\n" { |
86 | | - line.extend_from_slice(ch); |
87 | | - out.write_all(&line)?; |
88 | | - line.clear(); |
89 | | - col = 0; |
90 | | - continue; |
| 82 | + // Process one input line at a time so large inputs are not buffered in |
| 83 | + // full. A <newline> never splits a multibyte character; the wrap state |
| 84 | + // (`line`/`col`) carries across reads and is flushed at each newline. |
| 85 | + let mut data: Vec<u8> = Vec::new(); |
| 86 | + loop { |
| 87 | + data.clear(); |
| 88 | + if reader.read_until(b'\n', &mut data)? == 0 { |
| 89 | + break; |
91 | 90 | } |
| 91 | + for ch in mb_char_slices(&data) { |
| 92 | + if ch == b"\n" { |
| 93 | + line.extend_from_slice(ch); |
| 94 | + out.write_all(&line)?; |
| 95 | + line.clear(); |
| 96 | + col = 0; |
| 97 | + continue; |
| 98 | + } |
92 | 99 |
|
93 | | - let mut next = char_advance(col, ch, args.bytes); |
94 | | - |
95 | | - // Insert breaks while appending this character would exceed the width |
96 | | - // and the line is non-empty (a single over-wide character on an empty |
97 | | - // line is emitted as-is; the spec leaves that case undefined). |
98 | | - while next > width && !line.is_empty() { |
99 | | - let mut folded = false; |
100 | | - if args.spaces { |
101 | | - if let Some(b) = find_last_blank(&line) { |
102 | | - out.write_all(&line[..=b])?; |
| 100 | + let mut next = char_advance(col, ch, args.bytes); |
| 101 | + |
| 102 | + // Insert breaks while appending this character would exceed the width |
| 103 | + // and the line is non-empty (a single over-wide character on an empty |
| 104 | + // line is emitted as-is; the spec leaves that case undefined). |
| 105 | + while next > width && !line.is_empty() { |
| 106 | + let mut folded = false; |
| 107 | + if args.spaces { |
| 108 | + if let Some(b) = find_last_blank(&line) { |
| 109 | + out.write_all(&line[..=b])?; |
| 110 | + out.write_all(b"\n")?; |
| 111 | + let remainder = line[b + 1..].to_vec(); |
| 112 | + line = remainder; |
| 113 | + // Recompute the column over the kept remainder (its |
| 114 | + // characters are complete: blanks are char boundaries). |
| 115 | + col = 0; |
| 116 | + for rc in mb_char_slices(&line) { |
| 117 | + col = char_advance(col, rc, args.bytes); |
| 118 | + } |
| 119 | + folded = true; |
| 120 | + } |
| 121 | + } |
| 122 | + if !folded { |
| 123 | + out.write_all(&line)?; |
103 | 124 | out.write_all(b"\n")?; |
104 | | - let remainder = line[b + 1..].to_vec(); |
105 | | - line = remainder; |
106 | | - // Recompute the column over the kept remainder (its |
107 | | - // characters are complete: blanks are char boundaries). |
| 125 | + line.clear(); |
108 | 126 | col = 0; |
109 | | - for rc in mb_char_slices(&line) { |
110 | | - col = char_advance(col, rc, args.bytes); |
111 | | - } |
112 | | - folded = true; |
113 | 127 | } |
| 128 | + next = char_advance(col, ch, args.bytes); |
114 | 129 | } |
115 | | - if !folded { |
116 | | - out.write_all(&line)?; |
117 | | - out.write_all(b"\n")?; |
118 | | - line.clear(); |
119 | | - col = 0; |
120 | | - } |
121 | | - next = char_advance(col, ch, args.bytes); |
122 | | - } |
123 | 130 |
|
124 | | - line.extend_from_slice(ch); |
125 | | - col = next; |
| 131 | + line.extend_from_slice(ch); |
| 132 | + col = next; |
| 133 | + } |
126 | 134 | } |
127 | 135 |
|
128 | 136 | // Trailing partial line (input without a final newline). |
|
0 commit comments