1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
Bi
Copyright (C) 2019, 2021-2022, 2024 Anonymous
There are several releases over multiple years,
they are listed as ranges, such as: "2021-2022".
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/
use {
std::{
fs::File,
io::{self, BufReader, Read, Stdin},
path::Path,
},
crate::IoResult,
};
#[cfg(test)]
mod tests;
/// # Limit
///
/// This struct can be used for limiting data read from some source.
#[derive(Debug)]
pub struct Limit<R> where R: Read {
inner: R,
read: u64,
limit: u64,
}
impl<R> Limit<R> where R: Read {
/// # Makes new object
pub fn new(inner: R, limit: u64) -> Self {
Self {
inner,
read: u64::MIN,
limit,
}
}
}
impl Limit<BufReader<File>> {
/// # Opens a file for reading with a limit
///
/// The file will be wrapped inside a [`BufReader`][struct:std/io/BufReader].
///
/// [struct:std/io/BufReader]: https://doc.rust-lang.org/std/io/struct.BufReader.html
pub fn open_file<P>(path: P, limit: u64) -> IoResult<Self> where P: AsRef<Path> {
let path = path.as_ref();
match path.metadata()?.len() {
size => if size > limit {
return Err(err!("File too large: {size} (limit: {limit})"));
},
};
Ok(Self::new(BufReader::new(File::open(path)?), limit))
}
}
impl Limit<Stdin> {
/// # Makes new object from standard input
pub fn stdin(limit: u64) -> IoResult<Self> {
Ok(Self::new(io::stdin(), limit))
}
}
impl<R> Read for Limit<R> where R: Read {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
macro_rules! check { () => {
if self.read > self.limit {
return Err(err!("Limit: {limit}, already read: {read}", limit=self.limit, read=self.read));
}
}}
check!();
let result = self.inner.read(buf)?;
match u64::try_from(result).map_err(|_| err!("Cannot convert {result} into u64"))?.checked_add(self.read) {
None => return Err(err!("Failed: {read} + {result}", read=self.read)),
Some(read) => self.read = read,
};
check!();
Ok(result)
}
}