use std::{
cmp,
io::{BufRead, Read},
};
pub struct RefTake<'a, R> {
inner: &'a mut R,
limit: u64,
}
impl<'a, R> RefTake<'a, R> {
pub fn wrap(inner: &'a mut R, limit: u64) -> Self {
Self { inner, limit }
}
pub fn set_limit(&mut self, limit: u64) {
self.limit = limit;
}
pub fn current_limit(&self) -> u64 {
self.limit
}
}
impl<T: Read> Read for RefTake<'_, T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
if self.limit == 0 {
return Ok(0);
}
let max = cmp::min(buf.len() as u64, self.limit) as usize;
let n = self.inner.read(&mut buf[..max])?;
assert!(n as u64 <= self.limit, "number of read bytes exceeds limit");
self.limit -= n as u64;
Ok(n)
}
}
impl<T: BufRead> BufRead for RefTake<'_, T> {
fn fill_buf(&mut self) -> Result<&[u8], std::io::Error> {
if self.limit == 0 {
return Ok(&[]);
}
let buf = self.inner.fill_buf()?;
let cap = cmp::min(buf.len() as u64, self.limit) as usize;
Ok(&buf[..cap])
}
fn consume(&mut self, amt: usize) {
let amt = cmp::min(amt as u64, self.limit) as usize;
self.limit -= amt as u64;
self.inner.consume(amt);
}
}
pub trait RefTakeExt {
fn take_ref(&mut self, limit: u64) -> RefTake<'_, Self>
where
Self: Sized;
}
impl<T: Read> RefTakeExt for T {
fn take_ref(&mut self, limit: u64) -> RefTake<'_, Self> {
RefTake::wrap(self, limit)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{BufReader, Cursor, Read};
#[test]
fn test_read_respects_limit() {
let data = b"Hello, world!";
let mut reader = Cursor::new(data);
let mut take = reader.take_ref(5);
let mut buf = [0u8; 10];
let n = take.read(&mut buf).unwrap();
assert_eq!(n, 5);
assert_eq!(&buf[..n], b"Hello");
assert_eq!(take.current_limit(), 0);
}
#[test]
fn test_read_in_multiple_calls() {
let data = b"abcdef";
let mut reader = Cursor::new(data);
let mut take = reader.take_ref(6);
let mut buf1 = [0u8; 2];
let n1 = take.read(&mut buf1).unwrap();
assert_eq!(n1, 2);
assert_eq!(&buf1[..n1], b"ab");
assert_eq!(take.current_limit(), 4);
let mut buf2 = [0u8; 3];
let n2 = take.read(&mut buf2).unwrap();
assert_eq!(n2, 3);
assert_eq!(&buf2[..n2], b"cde");
assert_eq!(take.current_limit(), 1);
let mut buf3 = [0u8; 4];
let n3 = take.read(&mut buf3).unwrap();
assert_eq!(n3, 1);
assert_eq!(&buf3[..n3], b"f");
assert_eq!(take.current_limit(), 0);
let mut buf4 = [0u8; 1];
let n4 = take.read(&mut buf4).unwrap();
assert_eq!(n4, 0); assert_eq!(take.current_limit(), 0);
}
#[test]
fn test_read_zero_limit() {
let data = b"Hello";
let mut reader = Cursor::new(data);
let mut take = reader.take_ref(0);
let mut buf = [0u8; 5];
let n = take.read(&mut buf).unwrap();
assert_eq!(n, 0);
}
#[test]
fn test_set_limit() {
let data = b"123456789";
let mut reader = Cursor::new(data);
let mut take = reader.take_ref(3);
let mut buf = [0u8; 10];
let n1 = take.read(&mut buf).unwrap();
assert_eq!(n1, 3);
assert_eq!(&buf[..n1], b"123");
take.set_limit(2);
let n2 = take.read(&mut buf).unwrap();
assert_eq!(n2, 2);
assert_eq!(&buf[..n2], b"45");
}
#[test]
fn test_bufread_fill_buf_respects_limit() {
let data = b"abcdef";
let mut reader = BufReader::new(Cursor::new(data));
let mut take = reader.take_ref(4);
let buf = take.fill_buf().unwrap();
assert_eq!(buf, b"abcd");
take.consume(2);
let buf2 = take.fill_buf().unwrap();
assert_eq!(buf2, b"cd");
take.consume(2);
let buf3 = take.fill_buf().unwrap();
assert_eq!(buf3, b"");
}
#[test]
fn test_bufread_consume_does_not_exceed_limit() {
let data = b"abcde";
let mut reader = BufReader::new(Cursor::new(data));
let mut take = reader.take_ref(3);
let _ = take.fill_buf().unwrap();
take.consume(10);
assert_eq!(take.limit, 0);
let buf = take.fill_buf().unwrap();
assert_eq!(buf, b"");
}
}