use std::fs::File;
use std::io;
use std::sync::Arc;
fn pread(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
#[cfg(unix)]
use std::os::unix::fs::FileExt;
#[cfg(windows)]
use std::os::windows::fs::FileExt;
let mut total = 0usize;
while total < buf.len() {
#[cfg(unix)]
let res = file.read_at(&mut buf[total..], offset + total as u64);
#[cfg(windows)]
let res = file.seek_read(&mut buf[total..], offset + total as u64);
#[cfg(not(any(unix, windows)))]
let res: io::Result<usize> = Err(io::Error::new(
io::ErrorKind::Unsupported,
"positioned reads unsupported on this platform",
));
match res {
Ok(0) => break,
Ok(n) => total += n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(total)
}
pub enum Backing {
File(File),
Sub {
file: Arc<File>,
base: u64,
len: u64,
},
Mem(Arc<[u8]>),
}
impl Backing {
#[must_use]
pub fn sub(file: Arc<File>, base: u64, len: u64) -> Self {
Backing::Sub { file, base, len }
}
#[must_use]
pub fn from_bytes(bytes: impl Into<Arc<[u8]>>) -> Self {
Backing::Mem(bytes.into())
}
#[must_use]
pub fn len(&self) -> u64 {
match self {
Backing::File(f) => f.metadata().map_or(0, |m| m.len()),
Backing::Sub { len, .. } => *len,
Backing::Mem(b) => b.len() as u64,
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
match self {
Backing::File(f) => pread(f, buf, offset),
Backing::Sub { file, base, len } => {
let avail = len.saturating_sub(offset);
if avail == 0 {
return Ok(0);
}
let want = (buf.len() as u64).min(avail) as usize;
pread(file, &mut buf[..want], base + offset)
}
Backing::Mem(bytes) => {
let off = offset.min(bytes.len() as u64) as usize;
let src = &bytes[off..];
let n = src.len().min(buf.len());
buf[..n].copy_from_slice(&src[..n]);
Ok(n)
}
}
}
pub fn read_exact_at(&self, offset: u64, len: usize) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; len];
let n = self.read_at(&mut buf, offset)?;
buf.truncate(n);
Ok(buf)
}
}
impl std::fmt::Debug for Backing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Backing::File(_) => f.debug_struct("File").field("len", &self.len()).finish(),
Backing::Sub { base, len, .. } => f
.debug_struct("Sub")
.field("base", base)
.field("len", len)
.finish(),
Backing::Mem(b) => f.debug_struct("Mem").field("len", &b.len()).finish(),
}
}
}