use super::*;
#[cfg(feature = "zstd")]
pub struct ChangeFile<'a> {
s: Option<zstd_seekable::Seekable<'a, OffFile>>,
hashed: Hashed<Local>,
hash: Hash,
unhashed: Option<toml::Value>,
}
struct OffFile {
f: std::fs::File,
start: u64,
}
unsafe impl Send for OffFile {}
impl std::io::Read for OffFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
self.f.read(buf)
}
}
impl std::io::Seek for OffFile {
fn seek(&mut self, from: std::io::SeekFrom) -> Result<u64, std::io::Error> {
use std::io::SeekFrom;
let from = match from {
SeekFrom::Start(s) => SeekFrom::Start(s + self.start),
c => c,
};
self.f.seek(from)
}
}
#[cfg(feature = "zstd")]
impl<'a> ChangeFile<'a> {
pub fn open(hash: Hash, path: &str) -> Result<Self, ChangeError> {
use std::io::Read;
let mut r = std::fs::File::open(path)?;
let mut buf = Vec::new();
buf.resize(Change::OFFSETS_SIZE as usize, 0);
r.read_exact(&mut buf)?;
let offsets: Offsets = bincode::deserialize(&buf)?;
if offsets.version != VERSION {
return Err(ChangeError::VersionMismatch);
}
buf.clear();
buf.resize((offsets.unhashed_off - Change::OFFSETS_SIZE) as usize, 0);
r.read_exact(&mut buf)?;
let mut buf2 = vec![0u8; offsets.hashed_len as usize];
let hashed: Hashed<Local> = {
let mut s = zstd_seekable::Seekable::init_buf(&buf)?;
s.decompress(&mut buf2, 0)?;
bincode::deserialize(&buf2)?
};
buf.resize((offsets.contents_off - offsets.unhashed_off) as usize, 0);
let unhashed = if buf.is_empty() {
None
} else {
r.read_exact(&mut buf)?;
let mut s = zstd_seekable::Seekable::init_buf(&buf)?;
buf2.resize(offsets.unhashed_len as usize, 0);
s.decompress(&mut buf2, 0)?;
Some(toml::de::from_slice(&buf2)?)
};
let m = r.metadata()?;
let s = if offsets.contents_off >= m.len() {
None
} else {
Some(zstd_seekable::Seekable::init(Box::new(OffFile {
f: r,
start: offsets.contents_off,
}))?)
};
Ok(ChangeFile {
s,
hashed,
hash,
unhashed,
})
}
pub fn has_contents(&self) -> bool {
self.s.is_some()
}
pub fn read_contents(&mut self, offset: u64, buf: &mut [u8]) -> Result<usize, ChangeError> {
debug!("read_contents {:?} {:?}", offset, buf.len());
if let Some(ref mut s) = self.s {
Ok(s.decompress(buf, offset)?)
} else {
Err(ChangeError::MissingContents { hash: self.hash })
}
}
pub fn hashed(&self) -> &Hashed<Local> {
&self.hashed
}
pub fn unhashed(&self) -> &Option<toml::Value> {
&self.unhashed
}
}