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