Skip to content

Commit cedac04

Browse files
committed
head: read directly from input buffer in print_n_lines
1 parent f5e7499 commit cedac04

2 files changed

Lines changed: 21 additions & 94 deletions

File tree

src/uu/head/src/head.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// spell-checker:ignore (vars) memrchr
77

88
use clap::ArgMatches;
9-
use memchr::memrchr_iter;
9+
use memchr::{memchr_iter, memrchr_iter};
1010
use std::ffi::OsString;
1111
use std::fs::File;
1212
use std::io::{self, BufWriter, Read, Seek, SeekFrom, Write};
@@ -31,7 +31,6 @@ mod parse;
3131
mod take;
3232
use take::copy_all_but_n_bytes;
3333
use take::copy_all_but_n_lines;
34-
use take::take_lines;
3534

3635
#[derive(Error, Debug)]
3736
enum HeadError {
@@ -203,29 +202,37 @@ fn print_n_lines(
203202
n: u64,
204203
separator: u8,
205204
) -> Result<u64, HeadFileError> {
206-
// Read the first `n` lines from the `input` reader.
207-
let mut reader = take_lines(input, n, separator);
208-
209-
// Write those bytes to `stdout`.
210205
let stdout = io::stdout();
211206
let stdout = stdout.lock();
212207
let mut writer = BufWriter::with_capacity(BUF_SIZE, stdout);
213208

214-
let mut bytes_written = 0;
215-
let mut buf = [0; BUF_SIZE];
216-
loop {
217-
let bytes_read = reader.read(&mut buf).map_err(HeadFileError::Read)?;
209+
let mut bytes_written: u64 = 0;
210+
let mut remaining = n;
211+
while remaining > 0 {
212+
let chunk = input.fill_buf().map_err(HeadFileError::Read)?;
218213

219-
if bytes_read == 0 {
214+
if chunk.is_empty() {
220215
break;
221216
}
222217

218+
let mut take_len = chunk.len(); // default: take everything
219+
let mut separators_seen: u64 = 0;
220+
for separator_idx in memchr_iter(separator, chunk) {
221+
separators_seen += 1;
222+
if separators_seen == remaining {
223+
take_len = separator_idx + 1; // include the separator itself
224+
break;
225+
}
226+
}
227+
remaining -= separators_seen;
228+
223229
writer
224-
.write_all(&buf[..bytes_read])
230+
.write_all(&chunk[..take_len])
225231
.map_err(wrap_in_stdout_error)
226232
.map_err(HeadFileError::WriteStdout)?;
227233

228-
bytes_written += bytes_read as u64;
234+
input.consume(take_len);
235+
bytes_written += take_len as u64;
229236
}
230237

231238
// Make sure we finish writing everything to the target before

src/uu/head/src/take.rs

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -303,62 +303,11 @@ pub fn copy_all_but_n_lines<R: Read, W: Write>(
303303
Ok(total_bytes_copied)
304304
}
305305

306-
/// Like `std::io::Take`, but for lines instead of bytes.
307-
///
308-
/// This struct is generally created by calling [`take_lines`] on a
309-
/// reader. Please see the documentation of [`take_lines`] for more
310-
/// details.
311-
pub struct TakeLines<T> {
312-
inner: T,
313-
limit: u64,
314-
separator: u8,
315-
}
316-
317-
impl<T: Read> Read for TakeLines<T> {
318-
/// Read bytes from a buffer up to the requested number of lines.
319-
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
320-
if self.limit == 0 {
321-
return Ok(0);
322-
}
323-
match self.inner.read(buf) {
324-
Ok(0) => Ok(0),
325-
Ok(n) => {
326-
for i in memchr_iter(self.separator, &buf[..n]) {
327-
self.limit -= 1;
328-
if self.limit == 0 {
329-
return Ok(i + 1);
330-
}
331-
}
332-
Ok(n)
333-
}
334-
Err(e) => Err(e),
335-
}
336-
}
337-
}
338-
339-
/// Create an adaptor that will read at most `limit` lines from a given reader.
340-
///
341-
/// This function returns a new instance of `Read` that will read at
342-
/// most `limit` lines, after which it will always return EOF
343-
/// (`Ok(0)`).
344-
///
345-
/// The `separator` defines the character to interpret as the line
346-
/// ending. For the usual notion of "line", set this to `b'\n'`.
347-
pub fn take_lines<R>(reader: R, limit: u64, separator: u8) -> TakeLines<R> {
348-
TakeLines {
349-
inner: reader,
350-
limit,
351-
separator,
352-
}
353-
}
354-
355306
#[cfg(test)]
356307
mod tests {
357308

358-
use std::io::{BufRead, BufReader};
359-
360309
use crate::take::{
361-
TakeAllBuffer, TakeAllLinesBuffer, copy_all_but_n_bytes, copy_all_but_n_lines, take_lines,
310+
TakeAllBuffer, TakeAllLinesBuffer, copy_all_but_n_bytes, copy_all_but_n_lines,
362311
};
363312

364313
#[test]
@@ -635,33 +584,4 @@ mod tests {
635584
assert_eq!(bytes_copied, 2);
636585
assert_eq!(output_reader.get_ref()[..], input_buffer.as_bytes()[0..2]);
637586
}
638-
639-
#[test]
640-
fn test_zero_lines() {
641-
let input_reader = std::io::Cursor::new("a\nb\nc\n");
642-
let output_reader = BufReader::new(take_lines(input_reader, 0, b'\n'));
643-
let mut iter = output_reader.lines().map(|l| l.unwrap());
644-
assert_eq!(None, iter.next());
645-
}
646-
647-
#[test]
648-
fn test_fewer_lines() {
649-
let input_reader = std::io::Cursor::new("a\nb\nc\n");
650-
let output_reader = BufReader::new(take_lines(input_reader, 2, b'\n'));
651-
let mut iter = output_reader.lines().map(|l| l.unwrap());
652-
assert_eq!(Some(String::from("a")), iter.next());
653-
assert_eq!(Some(String::from("b")), iter.next());
654-
assert_eq!(None, iter.next());
655-
}
656-
657-
#[test]
658-
fn test_more_lines() {
659-
let input_reader = std::io::Cursor::new("a\nb\nc\n");
660-
let output_reader = BufReader::new(take_lines(input_reader, 4, b'\n'));
661-
let mut iter = output_reader.lines().map(|l| l.unwrap());
662-
assert_eq!(Some(String::from("a")), iter.next());
663-
assert_eq!(Some(String::from("b")), iter.next());
664-
assert_eq!(Some(String::from("c")), iter.next());
665-
assert_eq!(None, iter.next());
666-
}
667587
}

0 commit comments

Comments
 (0)