1use std::fs::File;
4use std::io;
5#[cfg(not(any(unix, windows)))]
6use std::io::{Read, Seek, SeekFrom};
7use std::ops::Range;
8use std::path::Path;
9use std::sync::Arc;
10#[cfg(not(any(unix, windows)))]
11use std::sync::Mutex;
12
13use memmap2::Mmap;
14
15use crate::error::{Error, Result};
16
17pub trait TiffSource: Send + Sync {
19 fn len(&self) -> u64;
21
22 fn is_empty(&self) -> bool {
24 self.len() == 0
25 }
26
27 fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>>;
29
30 fn as_slice(&self) -> Option<&[u8]> {
32 None
33 }
34}
35
36pub type SharedSource = Arc<dyn TiffSource>;
38
39pub struct FileSource {
41 #[cfg(any(unix, windows))]
42 file: File,
43 #[cfg(not(any(unix, windows)))]
44 file: Mutex<File>,
45 len: u64,
46 path: String,
47}
48
49impl FileSource {
50 pub fn open(path: &Path) -> Result<Self> {
51 let file = File::open(path).map_err(|e| Error::Io(e, path.display().to_string()))?;
52 let len = file
53 .metadata()
54 .map_err(|e| Error::Io(e, path.display().to_string()))?
55 .len();
56 let path = path.display().to_string();
57 #[cfg(any(unix, windows))]
58 {
59 Ok(Self { file, len, path })
60 }
61 #[cfg(not(any(unix, windows)))]
62 {
63 Ok(Self {
64 file: Mutex::new(file),
65 len,
66 path,
67 })
68 }
69 }
70}
71
72impl TiffSource for FileSource {
73 fn len(&self) -> u64 {
74 self.len
75 }
76
77 fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
78 checked_read_range(offset, len, self.len)?;
79 let mut bytes = vec![0; len];
80 read_file_exact_at(self, offset, &mut bytes)
81 .map_err(|e| Error::Io(e, self.path.clone()))?;
82 Ok(bytes)
83 }
84}
85
86pub struct MmapSource {
88 mmap: Mmap,
89}
90
91impl MmapSource {
92 pub unsafe fn open(path: &Path) -> Result<Self> {
100 let file = File::open(path).map_err(|e| Error::Io(e, path.display().to_string()))?;
101 let mmap =
102 unsafe { Mmap::map(&file) }.map_err(|e| Error::Io(e, path.display().to_string()))?;
103 Ok(Self { mmap })
104 }
105}
106
107impl TiffSource for MmapSource {
108 fn len(&self) -> u64 {
109 self.mmap.len() as u64
110 }
111
112 fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
113 let range = checked_slice_range(offset, len, self.len())?;
114 Ok(self.mmap[range].to_vec())
115 }
116
117 fn as_slice(&self) -> Option<&[u8]> {
118 Some(&self.mmap)
119 }
120}
121
122pub struct BytesSource {
124 bytes: Vec<u8>,
125}
126
127impl BytesSource {
128 pub fn new(bytes: Vec<u8>) -> Self {
129 Self { bytes }
130 }
131}
132
133impl TiffSource for BytesSource {
134 fn len(&self) -> u64 {
135 self.bytes.len() as u64
136 }
137
138 fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
139 let range = checked_slice_range(offset, len, self.len())?;
140 Ok(self.bytes[range].to_vec())
141 }
142
143 fn as_slice(&self) -> Option<&[u8]> {
144 Some(&self.bytes)
145 }
146}
147
148fn checked_read_range(offset: u64, len: usize, data_len: u64) -> Result<u64> {
149 let length = u64::try_from(len).map_err(|_| Error::OffsetOutOfBounds {
150 offset,
151 length: u64::MAX,
152 data_len,
153 })?;
154 let end = offset.checked_add(length).ok_or(Error::OffsetOutOfBounds {
155 offset,
156 length,
157 data_len,
158 })?;
159 if end > data_len {
160 return Err(Error::OffsetOutOfBounds {
161 offset,
162 length,
163 data_len,
164 });
165 }
166 Ok(end)
167}
168
169fn checked_slice_range(offset: u64, len: usize, data_len: u64) -> Result<Range<usize>> {
170 checked_read_range(offset, len, data_len)?;
171 let start = usize::try_from(offset).map_err(|_| Error::OffsetOutOfBounds {
172 offset,
173 length: len as u64,
174 data_len,
175 })?;
176 let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
177 offset,
178 length: len as u64,
179 data_len,
180 })?;
181 Ok(start..end)
182}
183
184#[cfg(unix)]
185fn read_file_exact_at(source: &FileSource, offset: u64, bytes: &mut [u8]) -> io::Result<()> {
186 use std::os::unix::fs::FileExt;
187
188 read_with_offset_loop(bytes, offset, |buf, current_offset| {
189 source.file.read_at(buf, current_offset)
190 })
191}
192
193#[cfg(windows)]
194fn read_file_exact_at(source: &FileSource, offset: u64, bytes: &mut [u8]) -> io::Result<()> {
195 use std::os::windows::fs::FileExt;
196
197 read_with_offset_loop(bytes, offset, |buf, current_offset| {
198 source.file.seek_read(buf, current_offset)
199 })
200}
201
202#[cfg(not(any(unix, windows)))]
203fn read_file_exact_at(source: &FileSource, offset: u64, bytes: &mut [u8]) -> io::Result<()> {
204 let mut file = source
205 .file
206 .lock()
207 .map_err(|_| io::Error::new(io::ErrorKind::Other, "file source lock poisoned"))?;
208 file.seek(SeekFrom::Start(offset))?;
209 file.read_exact(bytes)
210}
211
212#[cfg(any(unix, windows))]
213fn read_with_offset_loop(
214 bytes: &mut [u8],
215 offset: u64,
216 mut read_at: impl FnMut(&mut [u8], u64) -> io::Result<usize>,
217) -> io::Result<()> {
218 let mut total_read = 0usize;
219 while total_read < bytes.len() {
220 let current_offset = offset + total_read as u64;
221 match read_at(&mut bytes[total_read..], current_offset) {
222 Ok(0) => {
223 return Err(io::Error::new(
224 io::ErrorKind::UnexpectedEof,
225 "failed to fill whole buffer",
226 ));
227 }
228 Ok(read) => total_read += read,
229 Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
230 Err(error) => return Err(error),
231 }
232 }
233 Ok(())
234}