use std::{
collections::HashMap,
io::{Read, Seek, SeekFrom},
ops::DerefMut,
str,
sync::{Arc, Mutex},
};
use super::resource::Resource;
use crate::global::{error::*, flags::Flags, header::Header, reg_entry::RegistryEntry};
#[cfg(feature = "crypto")]
use crate::crypto;
#[cfg(feature = "compression")]
use crate::global::compressor::*;
#[derive(Debug)]
pub struct Archive<T> {
handle: Mutex<T>,
header: Header,
entries: HashMap<Arc<str>, RegistryEntry>,
#[cfg(feature = "crypto")]
decryptor: Option<crypto::Encryptor>,
#[cfg(feature = "crypto")]
key: Option<crypto::VerifyingKey>,
}
impl<T> std::fmt::Display for Archive<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self
.entries
.values()
.map(|re| re.offset)
.reduce(|a, b| a + b)
.unwrap_or(0);
write!(
f,
"[Archive Header] Version: {}, Members: {}, Compressed Size: {bytes}B, Header-Flags: <{:#x} : {:#016b}>",
self.header.version,
self.entries.len(),
self.header.flags.bits,
self.header.flags.bits,
)
}
}
impl<T> Archive<T> {
pub fn into_inner(self) -> Result<T, std::sync::PoisonError<T>> {
self.handle.into_inner()
}
#[inline(never)]
fn process(&self, entry: &RegistryEntry, mut raw: Vec<u8>) -> InternalResult<(Vec<u8>, bool)> {
let mut decrypted = None;
let mut verified = false;
#[cfg(feature = "crypto")]
if let Some(pk) = self.key {
if let Some(signature) = entry.signature {
let raw_size = raw.len();
let entry_bytes = entry.to_bytes(true)?;
raw.extend_from_slice(&entry_bytes);
verified = pk.verify_strict(&raw, &signature).is_ok();
raw.truncate(raw_size);
}
}
if entry.flags.contains(Flags::ENCRYPTED_FLAG) {
#[cfg(feature = "crypto")]
match self.decryptor.as_ref() {
Some(dc) => {
decrypted = Some(dc.decrypt(&raw)?);
},
None => return Err(InternalError::NoKeypairError),
}
#[cfg(not(feature = "crypto"))]
return Err(InternalError::MissingFeatureError("crypto"));
}
if entry.flags.contains(Flags::COMPRESSED_FLAG) {
#[cfg(feature = "compression")]
{
let (source, mut target) = match decrypted {
Some(vec) => {
raw.clear();
(vec, raw)
},
None => {
let capacity = raw.capacity();
(raw, Vec::with_capacity(capacity))
},
};
if entry.flags.contains(Flags::LZ4_COMPRESSED) {
Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::LZ4, &mut target)?
} else if entry.flags.contains(Flags::BROTLI_COMPRESSED) {
Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::Brotli(0), &mut target)?
} else if entry.flags.contains(Flags::SNAPPY_COMPRESSED) {
Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::Snappy, &mut target)?
} else {
return InternalResult::Err(InternalError::OtherError(
format!(
"Unable to determine the compression algorithm used for entry: {}",
entry
)
.into(),
));
};
Ok((target, verified))
}
#[cfg(not(feature = "compression"))]
Err(InternalError::MissingFeatureError("compression"))
} else {
match decrypted {
Some(decrypted) => Ok((decrypted, verified)),
None => Ok((raw, verified)),
}
}
}
}
impl<T> Archive<T>
where
T: Seek + Read,
{
pub fn new(mut handle: T) -> InternalResult<Archive<T>> {
handle.seek(SeekFrom::Start(0))?;
let header = Header::from_handle(&mut handle)?;
header.validate()?;
let mut entries = HashMap::new();
for _ in 0..header.capacity {
let entry = RegistryEntry::from_handle(&mut handle)?;
entries.insert(entry.id.clone(), entry);
}
let archive = Archive {
header,
handle: Mutex::new(handle),
entries,
#[cfg(feature = "crypto")]
key: None,
#[cfg(feature = "crypto")]
decryptor: None,
};
Ok(archive)
}
#[cfg(feature = "crypto")]
pub fn with_key(mut handle: T, vk: &ed25519_dalek::VerifyingKey) -> InternalResult<Archive<T>> {
handle.seek(SeekFrom::Start(0))?;
let header = Header::from_handle(&mut handle)?;
header.validate()?;
let mut entries = HashMap::new();
for _ in 0..header.capacity {
let entry = RegistryEntry::from_handle(&mut handle)?;
entries.insert(entry.id.clone(), entry);
}
let archive = Archive {
header,
handle: Mutex::new(handle),
entries,
key: Some(vk.clone()),
decryptor: Some(crypto::Encryptor::new(vk)),
};
Ok(archive)
}
pub fn fetch_entry(&self, id: impl AsRef<str>) -> Option<RegistryEntry> {
self.entries.get(id.as_ref()).cloned()
}
#[inline(always)]
pub fn entries(&self) -> &HashMap<Arc<str>, RegistryEntry> {
&self.entries
}
#[inline(always)]
pub fn flags(&self) -> &Flags {
&self.header.flags
}
}
impl<T> Archive<T>
where
T: Read + Seek,
{
pub(crate) fn read_raw(handle: &mut T, entry: &RegistryEntry) -> InternalResult<Vec<u8>> {
let mut buffer = Vec::with_capacity(entry.offset as usize + 64);
handle.seek(SeekFrom::Start(entry.location))?;
let mut take = handle.take(entry.offset);
take.read_to_end(&mut buffer)?;
Ok(buffer)
}
pub fn fetch_mut(&mut self, id: impl AsRef<str>) -> InternalResult<Resource> {
if let Some(entry) = self.fetch_entry(&id) {
let raw = Archive::read_raw(self.handle.get_mut().unwrap(), &entry)?;
let (buffer, verified) = self.process(&entry, raw)?;
Ok(Resource {
content_version: entry.content_version,
flags: entry.flags,
data: buffer.into_boxed_slice(),
verified,
})
} else {
Err(InternalError::MissingResourceError(id.as_ref().to_string()))
}
}
pub fn fetch(&self, id: impl AsRef<str>) -> InternalResult<Resource> {
if let Some(entry) = self.fetch_entry(&id) {
let raw = {
let mut guard = self.handle.lock().unwrap();
Archive::read_raw(guard.deref_mut(), &entry)?
};
let (buffer, is_secure) = self.process(&entry, raw)?;
Ok(Resource {
content_version: entry.content_version,
flags: entry.flags,
data: buffer.into_boxed_slice(),
verified: is_secure,
})
} else {
Err(InternalError::MissingResourceError(id.as_ref().to_string()))
}
}
}