1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 use std::cmp::min;
18 use std::fs::File;
19 use std::io;
20 use std::os::unix::fs::FileExt;
21 
22 use super::{ChunkBuffer, ReadByChunk};
23 use crate::common::CHUNK_SIZE;
24 
25 /// A read-only file that can be read by chunks.
26 pub struct LocalFileReader {
27     file: File,
28     size: u64,
29 }
30 
31 impl LocalFileReader {
32     /// Creates a `LocalFileReader` to read from for the specified `path`.
33     pub fn new(file: File) -> io::Result<LocalFileReader> {
34         let size = file.metadata()?.len();
35         Ok(LocalFileReader { file, size })
36     }
37 
38     pub fn len(&self) -> u64 {
39         self.size
40     }
41 }
42 
43 impl ReadByChunk for LocalFileReader {
44     fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
45         let start = chunk_index * CHUNK_SIZE;
46         if start >= self.size {
47             return Ok(0);
48         }
49         let end = min(self.size, start + CHUNK_SIZE);
50         let read_size = (end - start) as usize;
51         debug_assert!(read_size <= buf.len());
52         self.file.read_exact_at(&mut buf[..read_size], start)?;
53         Ok(read_size)
54     }
55 }
56 
57 #[cfg(test)]
58 mod tests {
59     use super::*;
60     use std::env::temp_dir;
61 
62     #[test]
63     fn test_read_4k_file() -> io::Result<()> {
64         let file_reader = LocalFileReader::new(File::open("testdata/input.4k")?)?;
65         let mut buf = [0u8; 4096];
66         let size = file_reader.read_chunk(0, &mut buf)?;
67         assert_eq!(size, buf.len());
68         Ok(())
69     }
70 
71     #[test]
72     fn test_read_4k1_file() -> io::Result<()> {
73         let file_reader = LocalFileReader::new(File::open("testdata/input.4k1")?)?;
74         let mut buf = [0u8; 4096];
75         let size = file_reader.read_chunk(0, &mut buf)?;
76         assert_eq!(size, buf.len());
77         let size = file_reader.read_chunk(1, &mut buf)?;
78         assert_eq!(size, 1);
79         Ok(())
80     }
81 
82     #[test]
83     fn test_read_4m_file() -> io::Result<()> {
84         let file_reader = LocalFileReader::new(File::open("testdata/input.4m")?)?;
85         for index in 0..file_reader.len() / 4096 {
86             let mut buf = [0u8; 4096];
87             let size = file_reader.read_chunk(index, &mut buf)?;
88             assert_eq!(size, buf.len());
89         }
90         Ok(())
91     }
92 
93     #[test]
94     fn test_read_beyond_file_size() -> io::Result<()> {
95         let file_reader = LocalFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
96         let mut buf = [0u8; 4096];
97         let size = file_reader.read_chunk(1u64, &mut buf)?;
98         assert_eq!(size, 0);
99         Ok(())
100     }
101 
102     #[test]
103     fn test_read_empty_file() -> io::Result<()> {
104         let mut temp_file = temp_dir();
105         temp_file.push("authfs_test_empty_file");
106         let file_reader = LocalFileReader::new(File::create(temp_file).unwrap()).unwrap();
107         let mut buf = [0u8; 4096];
108         let size = file_reader.read_chunk(0, &mut buf)?;
109         assert_eq!(size, 0);
110         Ok(())
111     }
112 }
113