use std::fs::File;
use std::path::Path;
use std::sync::Arc;
use memmap2::Mmap;
use crate::error::{Error, Result};
pub trait TiffSource: Send + Sync {
fn len(&self) -> u64;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>>;
fn as_slice(&self) -> Option<&[u8]> {
None
}
}
pub type SharedSource = Arc<dyn TiffSource>;
pub struct MmapSource {
mmap: Mmap,
}
impl MmapSource {
pub fn open(path: &Path) -> Result<Self> {
let file = File::open(path).map_err(|e| Error::Io(e, path.display().to_string()))?;
let mmap =
unsafe { Mmap::map(&file) }.map_err(|e| Error::Io(e, path.display().to_string()))?;
Ok(Self { mmap })
}
}
impl TiffSource for MmapSource {
fn len(&self) -> u64 {
self.mmap.len() as u64
}
fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
let start = usize::try_from(offset).map_err(|_| Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
})?;
let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
})?;
if end > self.mmap.len() {
return Err(Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
});
}
Ok(self.mmap[start..end].to_vec())
}
fn as_slice(&self) -> Option<&[u8]> {
Some(&self.mmap)
}
}
pub struct BytesSource {
bytes: Vec<u8>,
}
impl BytesSource {
pub fn new(bytes: Vec<u8>) -> Self {
Self { bytes }
}
}
impl TiffSource for BytesSource {
fn len(&self) -> u64 {
self.bytes.len() as u64
}
fn read_exact_at(&self, offset: u64, len: usize) -> Result<Vec<u8>> {
let start = usize::try_from(offset).map_err(|_| Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
})?;
let end = start.checked_add(len).ok_or(Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
})?;
if end > self.bytes.len() {
return Err(Error::OffsetOutOfBounds {
offset,
length: len as u64,
data_len: self.len(),
});
}
Ok(self.bytes[start..end].to_vec())
}
fn as_slice(&self) -> Option<&[u8]> {
Some(&self.bytes)
}
}