music21/common/filetools.rs
1//! music21's `common.fileTools`, as far as it is not about Python objects.
2
3use std::io::{self, Read};
4
5/// The most bytes music21 will uncompress from one member of a compressed
6/// file: 256 MiB.
7pub const MAX_UNCOMPRESSED_BYTES: u64 = 256 * 1024 * 1024;
8
9/// How much is read at a time.
10const CHUNK: usize = 1024 * 1024;
11
12/// Everything a reader holds, read a mebibyte at a time, or `None` as soon
13/// as it has given more than `limit` bytes. music21's `readZipMember` reads
14/// a member so, rather than trusting the size its header claims.
15///
16/// ```
17/// use music21::common::filetools::read_limited;
18///
19/// assert_eq!(read_limited(&b"<score/>"[..], 8).unwrap(), Some(b"<score/>".to_vec()));
20/// assert_eq!(read_limited(&b"<score/>"[..], 7).unwrap(), None);
21/// ```
22pub fn read_limited<R: Read>(mut reader: R, limit: u64) -> io::Result<Option<Vec<u8>>> {
23 let mut read = Vec::new();
24 let mut chunk = vec![0; CHUNK];
25 loop {
26 let count = reader.read(&mut chunk)?;
27 if count == 0 {
28 return Ok(Some(read));
29 }
30 read.extend_from_slice(&chunk[..count]);
31 if read.len() as u64 > limit {
32 return Ok(None);
33 }
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 /// A reader longer than one chunk is read whole, and stops being read
42 /// once past the limit.
43 #[test]
44 fn long_readers_are_read_whole_or_refused() {
45 let long = vec![7u8; CHUNK * 2 + 5];
46 assert_eq!(
47 read_limited(&long[..], MAX_UNCOMPRESSED_BYTES).unwrap(),
48 Some(long.clone())
49 );
50 assert_eq!(read_limited(&long[..], CHUNK as u64).unwrap(), None);
51 assert_eq!(read_limited(&b""[..], 0).unwrap(), Some(Vec::new()));
52 }
53}