1#![cfg_attr(not(debug_assertions), deny(warnings, missing_docs, clippy::dbg_macro))]
4#![cfg_attr(feature = "read_initializer", feature(read_initializer))]
5
6use std::cmp;
7use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};
8
9pub trait Ext: Read + Seek + Sized {
11 fn takes(self, limit: u64) -> Result<Takes<Self>>;
16}
17
18impl<R: Read + Seek> Ext for R {
19 fn takes(mut self, limit: u64) -> Result<Takes<Self>> {
20 let start = self.seek(SeekFrom::Current(0))?;
21
22 Ok(Takes {
23 inner: self,
24 start,
25 limit,
26 current: 0,
27 })
28 }
29}
30
31pub struct Takes<R> {
35 inner: R,
36 start: u64,
37 limit: u64,
38 current: u64, }
40
41impl<R: Read> Read for Takes<R> {
43 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
44 let rem = self.limit - self.current;
45 if rem == 0 {
47 return Ok(0);
48 }
49
50 let max = cmp::min(buf.len() as u64, rem) as usize;
51 let n = self.inner.read(&mut buf[..max])?;
52 self.current += n as u64;
53 Ok(n)
54 }
55
56 #[cfg(feature = "read_initializer")]
57 unsafe fn initializer(&self) -> Initializer {
58 self.inner.initializer()
59 }
60}
61
62impl<R: Seek> Seek for Takes<R> {
66 fn seek(&mut self, seek: SeekFrom) -> Result<u64> {
67 Ok(match seek {
68 SeekFrom::Start(offset) => {
69 if offset < self.start || offset > self.current {
70 return Err(Error::new(
71 ErrorKind::UnexpectedEof,
72 "cannot seek beyond Takes range",
73 ));
74 }
75 self.inner.seek(SeekFrom::Start(offset))?
76 }
77 SeekFrom::Current(delta) => {
78 let dest = (self.current as i64) + delta;
79 if dest < 0 || (dest as u64) > self.limit {
80 return Err(Error::new(
81 ErrorKind::UnexpectedEof,
82 "cannot seek beyond Takes range",
83 ));
84 }
85 self.inner.seek(SeekFrom::Current(delta))?
86 }
87 SeekFrom::End(_) => unimplemented!("SeekFrom::End implementation would be ambiguous"),
88 })
89 }
90}