mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 12:37:49 +00:00
Merge pull request #2198 from jfinkels/tail-refactor
tail: simplify unbounded_tail() function
This commit is contained in:
commit
8f24ec9414
2 changed files with 96 additions and 63 deletions
61
src/uu/tail/src/ringbuffer.rs
Normal file
61
src/uu/tail/src/ringbuffer.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ extern crate clap;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
mod platform;
|
mod platform;
|
||||||
|
mod ringbuffer;
|
||||||
|
use ringbuffer::RingBuffer;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use std::collections::VecDeque;
|
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) {
|
fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
|
||||||
// Read through each line/char and store them in a ringbuffer that always
|
// 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
|
// contains count lines/chars. When reaching the end of file, output the
|
||||||
// data in the ringbuf.
|
// data in the ringbuf.
|
||||||
match settings.mode {
|
match settings.mode {
|
||||||
FilterMode::Lines(mut count, _delimiter) => {
|
FilterMode::Lines(count, _) => {
|
||||||
let mut ringbuf: VecDeque<String> = VecDeque::new();
|
for line in unbounded_tail_collect(reader.lines(), count, settings.beginning) {
|
||||||
let mut skip = if settings.beginning {
|
println!("{}", line);
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FilterMode::Bytes(count) => {
|
||||||
|
for byte in unbounded_tail_collect(reader.bytes(), count, settings.beginning) {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
for datum in &ringbuf {
|
print_byte(&mut stdout, byte);
|
||||||
print_string(&mut stdout, datum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,8 +539,3 @@ fn print_byte<T: Write>(stdout: &mut T, ch: u8) {
|
||||||
crash!(1, "{}", err);
|
crash!(1, "{}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn print_string<T: Write>(_: &mut T, s: &str) {
|
|
||||||
print!("{}", s);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue