1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge pull request #2198 from jfinkels/tail-refactor

tail: simplify unbounded_tail() function
This commit is contained in:
Sylvestre Ledru 2021-05-12 08:35:45 +02:00 committed by GitHub
commit 8f24ec9414
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 63 deletions

View file

@ -0,0 +1,61 @@
//! A fixed-size ring buffer.
use std::collections::VecDeque;
/// A fixed-size ring buffer backed by a `VecDeque`.
///
/// If the ring buffer is not full, then calling the [`push_back`]
/// method appends elements, as in a [`VecDeque`]. If the ring buffer
/// is full, then calling [`push_back`] removes the element at the
/// front of the buffer (in a first-in, first-out manner) before
/// appending the new element to the back of the buffer.
///
/// Use [`from_iter`] to take the last `size` elements from an
/// iterator.
///
/// # Examples
///
/// After exceeding the size limit, the oldest elements are dropped in
/// favor of the newest element:
///
/// ```rust,ignore
/// let buffer: RingBuffer<u8> = RingBuffer::new(2);
/// buffer.push_back(0);
/// buffer.push_back(1);
/// buffer.push_back(2);
/// assert_eq!(vec![1, 2], buffer.data);
/// ```
///
/// Take the last `n` elements from an iterator:
///
/// ```rust,ignore
/// let iter = vec![0, 1, 2, 3].iter();
/// assert_eq!(vec![2, 3], RingBuffer::from_iter(iter, 2).data);
/// ```
pub struct RingBuffer<T> {
pub data: VecDeque<T>,
size: usize,
}
impl<T> RingBuffer<T> {
pub fn new(size: usize) -> RingBuffer<T> {
RingBuffer {
data: VecDeque::new(),
size,
}
}
pub fn from_iter(iter: impl Iterator<Item = T>, size: usize) -> RingBuffer<T> {
let mut ringbuf = RingBuffer::new(size);
for value in iter {
ringbuf.push_back(value);
}
ringbuf
}
pub fn push_back(&mut self, value: T) {
if self.size <= self.data.len() {
self.data.pop_front();
}
self.data.push_back(value)
}
}

View file

@ -16,6 +16,8 @@ extern crate clap;
extern crate uucore;
mod platform;
mod ringbuffer;
use ringbuffer::RingBuffer;
use clap::{App, Arg};
use std::collections::VecDeque;
@ -482,71 +484,46 @@ fn bounded_tail(mut file: &File, settings: &Settings) {
}
}
/// Collect the last elements of an iterator into a `VecDeque`.
///
/// This function returns a [`VecDeque`] containing either the last
/// `count` elements of `iter`, an [`Iterator`] over [`Result`]
/// instances, or all but the first `count` elements of `iter`. If
/// `beginning` is `true`, then all but the first `count` elements are
/// returned.
///
/// # Panics
///
/// If any element of `iter` is an [`Err`], then this function panics.
fn unbounded_tail_collect<T, E>(
iter: impl Iterator<Item = Result<T, E>>,
count: u64,
beginning: bool,
) -> VecDeque<T>
where
E: fmt::Debug,
{
if beginning {
iter.skip(count as usize).map(|r| r.unwrap()).collect()
} else {
RingBuffer::from_iter(iter.map(|r| r.unwrap()), count as usize).data
}
}
fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
// Read through each line/char and store them in a ringbuffer that always
// contains count lines/chars. When reaching the end of file, output the
// data in the ringbuf.
match settings.mode {
FilterMode::Lines(mut count, _delimiter) => {
let mut ringbuf: VecDeque<String> = VecDeque::new();
let mut skip = if settings.beginning {
let temp = count;
count = ::std::u64::MAX;
temp - 1
} else {
0
};
loop {
let mut datum = String::new();
match reader.read_line(&mut datum) {
Ok(0) => break,
Ok(_) => {
if skip > 0 {
skip -= 1;
} else {
if count <= ringbuf.len() as u64 {
ringbuf.pop_front();
}
ringbuf.push_back(datum);
}
}
Err(err) => panic!("{}", err),
}
}
let mut stdout = stdout();
for datum in &ringbuf {
print_string(&mut stdout, datum);
FilterMode::Lines(count, _) => {
for line in unbounded_tail_collect(reader.lines(), count, settings.beginning) {
println!("{}", line);
}
}
FilterMode::Bytes(mut count) => {
let mut ringbuf: VecDeque<u8> = VecDeque::new();
let mut skip = if settings.beginning {
let temp = count;
count = ::std::u64::MAX;
temp - 1
} else {
0
};
loop {
let mut datum = [0; 1];
match reader.read(&mut datum) {
Ok(0) => break,
Ok(_) => {
if skip > 0 {
skip -= 1;
} else {
if count <= ringbuf.len() as u64 {
ringbuf.pop_front();
}
ringbuf.push_back(datum[0]);
}
}
Err(err) => panic!("{}", err),
}
}
let mut stdout = stdout();
for datum in &ringbuf {
print_byte(&mut stdout, *datum);
FilterMode::Bytes(count) => {
for byte in unbounded_tail_collect(reader.bytes(), count, settings.beginning) {
let mut stdout = stdout();
print_byte(&mut stdout, byte);
}
}
}
@ -562,8 +539,3 @@ fn print_byte<T: Write>(stdout: &mut T, ch: u8) {
crash!(1, "{}", err);
}
}
#[inline]
fn print_string<T: Write>(_: &mut T, s: &str) {
print!("{}", s);
}