Skip to main content

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}