blob: 0242afa1bacaeb62367623139652004f45ec6bca [file] [log] [blame]
Victor Hsieh1fe51c42020-11-05 11:08:10 -08001/*
2 * Copyright (C) 2020 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//! A module for reading data by chunks.
18
19use std::fs::File;
20use std::io::Result;
21use std::os::unix::fs::FileExt;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080022
Victor Hsiehda3fbc42021-02-23 16:12:49 -080023use crate::common::CHUNK_SIZE;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080024
25/// A trait for reading data by chunks. The data is assumed readonly and has fixed length. Chunks
26/// can be read by specifying the chunk index. Only the last chunk may have incomplete chunk size.
27pub trait ReadOnlyDataByChunk {
Victor Hsieh1fe51c42020-11-05 11:08:10 -080028 /// Read the `chunk_index`-th chunk to `buf`. Each slice/chunk has size `CHUNK_SIZE` except for
29 /// the last one, which can be an incomplete chunk. `buf` is currently required to be large
30 /// enough to hold a full chunk of data. Reading beyond the file size (including empty file)
31 /// will crash.
32 fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize>;
33}
34
Victor Hsiehda3fbc42021-02-23 16:12:49 -080035fn chunk_index_to_range(size: u64, chunk_index: u64) -> Result<(u64, u64)> {
36 let start = chunk_index * CHUNK_SIZE;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080037 assert!(start < size);
Victor Hsiehda3fbc42021-02-23 16:12:49 -080038 let end = std::cmp::min(size, start + CHUNK_SIZE);
Victor Hsieh1fe51c42020-11-05 11:08:10 -080039 Ok((start, end))
40}
41
42/// A read-only file that can be read by chunks.
43pub struct ChunkedFileReader {
44 file: File,
45 size: u64,
46}
47
48impl ChunkedFileReader {
49 /// Creates a `ChunkedFileReader` to read from for the specified `path`.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080050 pub fn new(file: File) -> Result<ChunkedFileReader> {
Victor Hsieh1fe51c42020-11-05 11:08:10 -080051 let size = file.metadata()?.len();
52 Ok(ChunkedFileReader { file, size })
53 }
Victor Hsiehfa4477a2021-02-08 10:51:50 -080054
55 pub fn len(&self) -> u64 {
56 self.size
57 }
Victor Hsieh1fe51c42020-11-05 11:08:10 -080058}
59
60impl ReadOnlyDataByChunk for ChunkedFileReader {
61 fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> Result<usize> {
Victor Hsiehda3fbc42021-02-23 16:12:49 -080062 debug_assert!(buf.len() as u64 >= CHUNK_SIZE);
63 let (start, end) = chunk_index_to_range(self.size, chunk_index)?;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080064 let size = (end - start) as usize;
65 self.file.read_at(&mut buf[..size], start)
66 }
67}
68
Victor Hsieh1fe51c42020-11-05 11:08:10 -080069#[cfg(test)]
70mod tests {
71 use super::*;
Victor Hsiehfa4477a2021-02-08 10:51:50 -080072 use std::env::temp_dir;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080073
Victor Hsiehfa4477a2021-02-08 10:51:50 -080074 #[test]
75 fn test_read_4k_file() -> Result<()> {
76 let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k")?)?;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080077 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -080078 let size = file_reader.read_chunk(0, &mut buf)?;
79 assert_eq!(size, buf.len());
Victor Hsieh1fe51c42020-11-05 11:08:10 -080080 Ok(())
81 }
82
Victor Hsieh1fe51c42020-11-05 11:08:10 -080083 #[test]
Victor Hsiehfa4477a2021-02-08 10:51:50 -080084 fn test_read_4k1_file() -> Result<()> {
85 let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k1")?)?;
Victor Hsieh1fe51c42020-11-05 11:08:10 -080086 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -080087 let size = file_reader.read_chunk(0, &mut buf)?;
88 assert_eq!(size, buf.len());
89 let size = file_reader.read_chunk(1, &mut buf)?;
90 assert_eq!(size, 1);
91 Ok(())
92 }
93
94 #[test]
95 fn test_read_4m_file() -> Result<()> {
96 let file_reader = ChunkedFileReader::new(File::open("testdata/input.4m")?)?;
97 for index in 0..file_reader.len() / 4096 {
98 let mut buf = [0u8; 4096];
99 let size = file_reader.read_chunk(index, &mut buf)?;
100 assert_eq!(size, buf.len());
101 }
102 Ok(())
Victor Hsieh1fe51c42020-11-05 11:08:10 -0800103 }
104
105 #[test]
106 #[should_panic]
Victor Hsieh1fe51c42020-11-05 11:08:10 -0800107 fn test_read_beyond_file_size() {
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800108 let file_reader = ChunkedFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
Victor Hsieh1fe51c42020-11-05 11:08:10 -0800109 let mut buf = [0u8; 4096];
Victor Hsiehfa4477a2021-02-08 10:51:50 -0800110 let _ = file_reader.read_chunk(1u64, &mut buf); // should panic
111 }
112
113 #[test]
114 #[should_panic]
115 fn test_read_empty_file() {
116 let mut temp_file = temp_dir();
117 temp_file.push("authfs_test_empty_file");
118 let file_reader = ChunkedFileReader::new(File::create(temp_file).unwrap()).unwrap();
119 let mut buf = [0u8; 4096];
120 let _ = file_reader.read_chunk(0, &mut buf); // should panic
Victor Hsieh1fe51c42020-11-05 11:08:10 -0800121 }
122}