use anyhow::{bail, Context, Error};
use std::{collections::HashMap, str};
pub struct FileDescriptor {
content_type: &'static str,
etag: &'static str,
content: &'static [u8],
content_gzip: Option<&'static [u8]>,
}
impl FileDescriptor {
pub fn content_type(&self) -> &'static str {
self.content_type
}
pub fn etag(&self) -> &'static str {
self.etag
}
pub fn content(&self) -> &'static [u8] {
self.content
}
pub fn content_gzip(&self) -> Option<&'static [u8]> {
self.content_gzip
}
}
pub struct Loader {
files: HashMap<&'static str, FileDescriptor>,
}
impl Loader {
fn read_u8(rest: &mut &'static [u8]) -> Result<&'static [u8], Error> {
#[allow(clippy::len_zero)]
if rest.len() < 1 {
bail!("Premature length termination");
}
let length = u8::from_ne_bytes(unsafe { [*rest.get_unchecked(0)] }) as usize;
if rest.len() - 1 < length {
bail!("Premature data termination");
}
let data = &rest[1..(1 + length)];
*rest = &rest[1 + length..];
Ok(data)
}
fn read_u16(rest: &mut &'static [u8]) -> Result<&'static [u8], Error> {
if rest.len() < 2 {
bail!("Premature length termination");
}
let length = u16::from_ne_bytes(unsafe { [*rest.get_unchecked(0), *rest.get_unchecked(1)] })
as usize;
if rest.len() - 2 < length {
bail!("Premature data termination");
}
let data = &rest[2..(2 + length)];
*rest = &rest[2 + length..];
Ok(data)
}
fn read_u32(rest: &mut &'static [u8]) -> Result<&'static [u8], Error> {
if rest.len() < 4 {
bail!("Premature length termination");
}
let length = u32::from_ne_bytes(unsafe {
[
*rest.get_unchecked(0),
*rest.get_unchecked(1),
*rest.get_unchecked(2),
*rest.get_unchecked(3),
]
}) as usize;
if rest.len() - 4 < length {
bail!("Premature data termination");
}
let data = &rest[4..(4 + length)];
*rest = &rest[4 + length..];
Ok(data)
}
pub fn new(included_bytes: &'static [u8]) -> Result<Self, Error> {
let mut rest = included_bytes;
let mut files = HashMap::<&'static str, FileDescriptor>::new();
while !rest.is_empty() {
let path =
unsafe { str::from_utf8_unchecked(Self::read_u16(&mut rest).context("path")?) };
let content_type = unsafe {
str::from_utf8_unchecked(Self::read_u8(&mut rest).context("content_type")?)
};
let etag =
unsafe { str::from_utf8_unchecked(Self::read_u8(&mut rest).context("etag")?) };
let content = Self::read_u32(&mut rest).context("content")?;
let content_gzip = Self::read_u32(&mut rest).context("content_gzip")?;
let content_gzip = if !content_gzip.is_empty() {
Some(content_gzip)
} else {
None
};
let file_descriptor = FileDescriptor {
content_type,
etag,
content,
content_gzip,
};
if files.insert(path, file_descriptor).is_some() {
bail!("File corrupted, duplicated path: {}", path);
}
}
log::info!("Loaded total {} files", files.len());
Ok(Self { files })
}
pub fn get(
&self,
path: &str,
) -> Option<&FileDescriptor> {
self.files.get(path)
}
}