mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
head: add abstractions for "all but last n lines"
Add some abstractions to simplify the `rbuf_but_last_n_lines()` function, which implements the "take all but the last `n` lines" functionality of the `head` program. This commit adds - `RingBuffer`, a fixed-size ring buffer, - `ZLines`, an iterator over zero-terminated "lines", - `TakeAllBut`, an iterator over all but the last `n` elements of an iterator. These three together make the implementation of `rbuf_but_last_n_lines()` concise.
This commit is contained in:
parent
70e65c419f
commit
bc9db289e8
12 changed files with 333 additions and 91 deletions
|
@ -16,7 +16,7 @@ path = "src/head.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
|
@ -27,8 +27,12 @@ mod options {
|
||||||
pub const ZERO_NAME: &str = "ZERO";
|
pub const ZERO_NAME: &str = "ZERO";
|
||||||
pub const FILES_NAME: &str = "FILE";
|
pub const FILES_NAME: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
mod lines;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod split;
|
mod split;
|
||||||
|
mod take;
|
||||||
|
use lines::zlines;
|
||||||
|
use take::take_all_but;
|
||||||
|
|
||||||
fn app<'a>() -> App<'a, 'a> {
|
fn app<'a>() -> App<'a, 'a> {
|
||||||
App::new(executable!())
|
App::new(executable!())
|
||||||
|
@ -293,36 +297,22 @@ fn rbuf_but_last_n_bytes(input: &mut impl std::io::BufRead, n: usize) -> std::io
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rbuf_but_last_n_lines(
|
fn rbuf_but_last_n_lines(
|
||||||
input: &mut impl std::io::BufRead,
|
input: impl std::io::BufRead,
|
||||||
n: usize,
|
n: usize,
|
||||||
zero: bool,
|
zero: bool,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
if n == 0 {
|
if zero {
|
||||||
//prints everything
|
let stdout = std::io::stdout();
|
||||||
return rbuf_n_bytes(input, std::usize::MAX);
|
let mut stdout = stdout.lock();
|
||||||
|
for bytes in take_all_but(zlines(input), n) {
|
||||||
|
stdout.write_all(&bytes?)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for line in take_all_but(input.lines(), n) {
|
||||||
|
println!("{}", line?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut ringbuf = vec![Vec::new(); n];
|
Ok(())
|
||||||
let stdout = std::io::stdout();
|
|
||||||
let mut stdout = stdout.lock();
|
|
||||||
let mut line = Vec::new();
|
|
||||||
let mut lines = 0usize;
|
|
||||||
split::walk_lines(input, zero, |e| match e {
|
|
||||||
split::Event::Data(dat) => {
|
|
||||||
line.extend_from_slice(dat);
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
split::Event::Line => {
|
|
||||||
if lines < n {
|
|
||||||
ringbuf[lines] = std::mem::replace(&mut line, Vec::new());
|
|
||||||
lines += 1;
|
|
||||||
} else {
|
|
||||||
stdout.write_all(&ringbuf[0])?;
|
|
||||||
ringbuf.rotate_left(1);
|
|
||||||
ringbuf[n - 1] = std::mem::replace(&mut line, Vec::new());
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Result<()> {
|
fn head_backwards_file(input: &mut std::fs::File, options: &HeadOptions) -> std::io::Result<()> {
|
||||||
|
|
73
src/uu/head/src/lines.rs
Normal file
73
src/uu/head/src/lines.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//! Iterate over zero-terminated lines.
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
/// The zero byte, representing the null character.
|
||||||
|
const ZERO: u8 = 0;
|
||||||
|
|
||||||
|
/// Returns an iterator over the lines of the given reader.
|
||||||
|
///
|
||||||
|
/// The iterator returned from this function will yield instances of
|
||||||
|
/// [`io::Result`]<[`Vec`]<[`u8`]>>, representing the bytes of the line
|
||||||
|
/// *including* the null character (with the possible exception of the
|
||||||
|
/// last line, which may not have one).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let cursor = Cursor::new(b"x\0y\0z\0");
|
||||||
|
/// let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
/// assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), Some(b"z\0".to_vec()));
|
||||||
|
/// assert_eq!(iter.next(), None);
|
||||||
|
/// ```
|
||||||
|
pub fn zlines<B>(buf: B) -> ZLines<B> {
|
||||||
|
ZLines { buf }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over the zero-terminated lines of an instance of `BufRead`.
|
||||||
|
pub struct ZLines<B> {
|
||||||
|
buf: B,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BufRead> Iterator for ZLines<B> {
|
||||||
|
type Item = std::io::Result<Vec<u8>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
match self.buf.read_until(ZERO, &mut buf) {
|
||||||
|
Ok(0) => None,
|
||||||
|
Ok(_) => Some(Ok(buf)),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::lines::zlines;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_null_terminated() {
|
||||||
|
let cursor = Cursor::new(b"x\0y\0z\0");
|
||||||
|
let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"z\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_null_terminated() {
|
||||||
|
let cursor = Cursor::new(b"x\0y\0z");
|
||||||
|
let mut iter = zlines(cursor).map(|l| l.unwrap());
|
||||||
|
assert_eq!(iter.next(), Some(b"x\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"y\0".to_vec()));
|
||||||
|
assert_eq!(iter.next(), Some(b"z".to_vec()));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
}
|
93
src/uu/head/src/take.rs
Normal file
93
src/uu/head/src/take.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
//! Take all but the last elements of an iterator.
|
||||||
|
use uucore::ringbuffer::RingBuffer;
|
||||||
|
|
||||||
|
/// Create an iterator over all but the last `n` elements of `iter`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let data = [1, 2, 3, 4, 5];
|
||||||
|
/// let n = 2;
|
||||||
|
/// let mut iter = take_all_but(data.iter(), n);
|
||||||
|
/// assert_eq!(Some(4), iter.next());
|
||||||
|
/// assert_eq!(Some(5), iter.next());
|
||||||
|
/// assert_eq!(None, iter.next());
|
||||||
|
/// ```
|
||||||
|
pub fn take_all_but<I: Iterator>(iter: I, n: usize) -> TakeAllBut<I> {
|
||||||
|
TakeAllBut::new(iter, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator that only iterates over the last elements of another iterator.
|
||||||
|
pub struct TakeAllBut<I: Iterator> {
|
||||||
|
iter: I,
|
||||||
|
buf: RingBuffer<<I as Iterator>::Item>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator> TakeAllBut<I> {
|
||||||
|
pub fn new(mut iter: I, n: usize) -> TakeAllBut<I> {
|
||||||
|
// Create a new ring buffer and fill it up.
|
||||||
|
//
|
||||||
|
// If there are fewer than `n` elements in `iter`, then we
|
||||||
|
// exhaust the iterator so that whenever `TakeAllBut::next()` is
|
||||||
|
// called, it will return `None`, as expected.
|
||||||
|
let mut buf = RingBuffer::new(n);
|
||||||
|
for _ in 0..n {
|
||||||
|
let value = match iter.next() {
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(x) => x,
|
||||||
|
};
|
||||||
|
buf.push_back(value);
|
||||||
|
}
|
||||||
|
TakeAllBut { iter, buf }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator> Iterator for TakeAllBut<I>
|
||||||
|
where
|
||||||
|
I: Iterator,
|
||||||
|
{
|
||||||
|
type Item = <I as Iterator>::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<<I as Iterator>::Item> {
|
||||||
|
match self.iter.next() {
|
||||||
|
Some(value) => self.buf.push_back(value),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::take::take_all_but;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fewer_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1, 2].iter(), 2);
|
||||||
|
assert_eq!(Some(&0), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_same_number_of_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1].iter(), 2);
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_more_elements() {
|
||||||
|
let mut iter = take_all_but([0].iter(), 2);
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_elements() {
|
||||||
|
let mut iter = take_all_but([0, 1, 2].iter(), 0);
|
||||||
|
assert_eq!(Some(&0), iter.next());
|
||||||
|
assert_eq!(Some(&1), iter.next());
|
||||||
|
assert_eq!(Some(&2), iter.next());
|
||||||
|
assert_eq!(None, iter.next());
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ path = "src/tail.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["ringbuffer"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//! 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,9 +17,7 @@ extern crate uucore;
|
||||||
|
|
||||||
mod chunks;
|
mod chunks;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod ringbuffer;
|
|
||||||
use chunks::ReverseChunks;
|
use chunks::ReverseChunks;
|
||||||
use ringbuffer::RingBuffer;
|
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -30,6 +28,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use uucore::ringbuffer::RingBuffer;
|
||||||
|
|
||||||
pub mod options {
|
pub mod options {
|
||||||
pub mod verbosity {
|
pub mod verbosity {
|
||||||
|
|
|
@ -47,6 +47,7 @@ mode = ["libc"]
|
||||||
parse_time = []
|
parse_time = []
|
||||||
perms = ["libc"]
|
perms = ["libc"]
|
||||||
process = ["libc"]
|
process = ["libc"]
|
||||||
|
ringbuffer = []
|
||||||
signals = []
|
signals = []
|
||||||
utf8 = []
|
utf8 = []
|
||||||
utmpx = ["time", "libc"]
|
utmpx = ["time", "libc"]
|
||||||
|
|
|
@ -8,6 +8,8 @@ pub mod fs;
|
||||||
pub mod fsext;
|
pub mod fsext;
|
||||||
#[cfg(feature = "parse_time")]
|
#[cfg(feature = "parse_time")]
|
||||||
pub mod parse_time;
|
pub mod parse_time;
|
||||||
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
pub mod ringbuffer;
|
||||||
#[cfg(feature = "zero-copy")]
|
#[cfg(feature = "zero-copy")]
|
||||||
pub mod zero_copy;
|
pub mod zero_copy;
|
||||||
|
|
||||||
|
|
134
src/uucore/src/lib/features/ringbuffer.rs
Normal file
134
src/uucore/src/lib/features/ringbuffer.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
//! 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 mut 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 = [0, 1, 2].iter();
|
||||||
|
/// let actual = RingBuffer::from_iter(iter, 2).data;
|
||||||
|
/// let expected = VecDeque::from_iter([1, 2].iter());
|
||||||
|
/// assert_eq!(expected, actual);
|
||||||
|
/// ```
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a value to the end of the ring buffer.
|
||||||
|
///
|
||||||
|
/// If the ring buffer is not full, this method return [`None`]. If
|
||||||
|
/// the ring buffer is full, appending a new element will cause the
|
||||||
|
/// oldest element to be evicted. In that case this method returns
|
||||||
|
/// that element, or `None`.
|
||||||
|
///
|
||||||
|
/// In the special case where the size limit is zero, each call to
|
||||||
|
/// this method with input `value` returns `Some(value)`, because
|
||||||
|
/// the input is immediately evicted.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Appending an element when the buffer is full returns the oldest
|
||||||
|
/// element:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let mut buf = RingBuffer::new(3);
|
||||||
|
/// assert_eq!(None, buf.push_back(0));
|
||||||
|
/// assert_eq!(None, buf.push_back(1));
|
||||||
|
/// assert_eq!(None, buf.push_back(2));
|
||||||
|
/// assert_eq!(Some(0), buf.push_back(3));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If the size limit is zero, then this method always returns the
|
||||||
|
/// input value:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// let mut buf = RingBuffer::new(0);
|
||||||
|
/// assert_eq!(Some(0), buf.push_back(0));
|
||||||
|
/// assert_eq!(Some(1), buf.push_back(1));
|
||||||
|
/// assert_eq!(Some(2), buf.push_back(2));
|
||||||
|
/// ```
|
||||||
|
pub fn push_back(&mut self, value: T) -> Option<T> {
|
||||||
|
if self.size == 0 {
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
let result = if self.size <= self.data.len() {
|
||||||
|
self.data.pop_front()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.data.push_back(value);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::ringbuffer::RingBuffer;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_size_limit_zero() {
|
||||||
|
let mut buf = RingBuffer::new(0);
|
||||||
|
assert_eq!(Some(0), buf.push_back(0));
|
||||||
|
assert_eq!(Some(1), buf.push_back(1));
|
||||||
|
assert_eq!(Some(2), buf.push_back(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_evict_oldest() {
|
||||||
|
let mut buf = RingBuffer::new(2);
|
||||||
|
assert_eq!(None, buf.push_back(0));
|
||||||
|
assert_eq!(None, buf.push_back(1));
|
||||||
|
assert_eq!(Some(0), buf.push_back(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_iter() {
|
||||||
|
let iter = [0, 1, 2].iter();
|
||||||
|
let actual = RingBuffer::from_iter(iter, 2).data;
|
||||||
|
let expected = VecDeque::from_iter([1, 2].iter());
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,8 @@ pub use crate::features::fs;
|
||||||
pub use crate::features::fsext;
|
pub use crate::features::fsext;
|
||||||
#[cfg(feature = "parse_time")]
|
#[cfg(feature = "parse_time")]
|
||||||
pub use crate::features::parse_time;
|
pub use crate::features::parse_time;
|
||||||
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
pub use crate::features::ringbuffer;
|
||||||
#[cfg(feature = "zero-copy")]
|
#[cfg(feature = "zero-copy")]
|
||||||
pub use crate::features::zero_copy;
|
pub use crate::features::zero_copy;
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,15 @@ fn test_zero_terminated_syntax_2() {
|
||||||
.stdout_is("x\0y");
|
.stdout_is("x\0y");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_terminated_negative_lines() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-z", "-n", "-1"])
|
||||||
|
.pipe_in("x\0y\0z\0")
|
||||||
|
.run()
|
||||||
|
.stdout_is("x\0y\0");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative_byte_syntax() {
|
fn test_negative_byte_syntax() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue