use crate::*;
use std::marker::*;
use std::mem::*;
use wasm_encoder::*;
use wasmparser::*;
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WassetItem<'a, A: AssetSchema> {
data: &'a [u8],
marker: PhantomData<fn(A)>
}
impl<'a, A: AssetSchema> WassetItem<'a, A> {
pub fn deserialize(&self) -> Result<A, WassetError> {
rmp_serde::from_slice(self.data).map_err(WassetError::from_deserialize)
}
}
impl<'a, A: AssetSchema> Deref for WassetItem<'a, A> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'a, A: AssetSchema> From<&'a [u8]> for WassetItem<'a, A> {
fn from(value: &'a [u8]) -> Self {
Self {
data: value,
marker: PhantomData
}
}
}
pub struct WassetParser<'a, A: AssetSchema> {
manifest: WassetManifest,
module: &'a [u8],
marker: PhantomData<fn(A)>
}
impl<'a, A: AssetSchema> WassetParser<'a, A> {
const ASSET_MANIFEST_SECTION_PREFIX: &'static str = "__wasset_manifest:";
const ASSET_DATA_SECTION_PREFIX: &'static str = "__wasset_data:";
pub fn parse(module: &'a [u8]) -> Result<Self, WassetError> {
let mut contents = module;
let mut parser = Parser::new(0);
let mut offsets = FxHashMap::default();
loop {
let payload = match parser.parse(contents, true).map_err(WassetError::from_deserialize)? {
Chunk::Parsed { consumed, payload } => {
contents = &contents[consumed..];
payload
}
Chunk::NeedMoreData(_) => unreachable!(),
};
match payload {
Payload::CodeSectionStart { size, .. } => {
parser.skip_section();
contents = &contents[size as usize..];
}
Payload::CustomSection(c) => Self::parse_module_custom_section(c, &mut offsets)?,
Payload::End(_) => break,
_ => {}
}
}
let manifest = Self::collect_manifests(offsets)?;
Ok(Self {
manifest,
module,
marker: PhantomData
})
}
pub fn ids(&self) -> impl '_ + Iterator<Item = WassetId> {
self.manifest.asset_ranges.keys().copied()
}
pub fn iter(&self) -> WassetIter<A> {
self.into_iter()
}
pub fn load(&self, id: WassetId) -> Result<Option<A>, WassetError> {
if let Some(range) = self.manifest.asset_ranges.get(&id) {
Ok(Some(self.load_by_range(range.clone())?.deserialize()?))
}
else {
Ok(None)
}
}
pub fn load_raw(&self, id: WassetId) -> Result<Option<WassetItem<A>>, WassetError> {
if let Some(range) = self.manifest.asset_ranges.get(&id) {
Ok(Some(self.load_by_range(range.clone())?))
}
else {
Ok(None)
}
}
pub fn manifest(&self) -> &WassetManifest {
&self.manifest
}
pub fn strip_module(&self) -> Result<Vec<u8>, WassetError> {
let mut output = Vec::new();
let mut stack = Vec::new();
for payload in Parser::new(0).parse_all(self.module) {
let payload = payload.map_err(WassetError::from_deserialize)?;
match payload {
Payload::Version { .. } => output.extend_from_slice(&Module::HEADER),
Payload::ModuleSection { .. } => {
stack.push(take(&mut output));
continue;
}
Payload::End { .. } => {
let mut parent = match stack.pop() {
Some(c) => c,
None => break,
};
parent.push(ComponentSectionId::CoreModule as u8);
output.encode(&mut parent);
output = parent;
}
_ => {}
}
match &payload {
Payload::CustomSection(c) => {
if c.name().starts_with(Self::ASSET_MANIFEST_SECTION_PREFIX)
|| c.name().starts_with(Self::ASSET_DATA_SECTION_PREFIX) {
continue;
}
}
_ => {}
}
if let Some((id, range)) = payload.as_section() {
RawSection {
id,
data: &self.module[range],
}.append_to(&mut output);
}
}
Ok(output)
}
fn load_by_range(&self, range: Range<u32>) -> Result<WassetItem<A>, WassetError> {
if let Some(slice) = self.module.get(range.start as usize..range.end as usize) {
Ok(WassetItem::from(slice))
}
else {
Err(WassetError::from_deserialize("index out of range"))
}
}
fn collect_manifests(offsets: FxHashMap<Uuid, WassetOffsets>) -> Result<WassetManifest, WassetError> {
let mut manifest = WassetManifest::default();
for manifest_offset in offsets.into_values() {
let manifest_instance = rmp_serde::from_slice::<WassetManifest>(manifest_offset.manifest).map_err(WassetError::from_deserialize)?;
for (id, range) in manifest_instance.asset_ranges {
manifest.asset_ranges.insert(id, range.start + manifest_offset.data_offset..range.end + manifest_offset.data_offset);
}
}
Ok(manifest)
}
fn parse_module_custom_section(reader: CustomSectionReader<'a>, offsets: &mut FxHashMap<Uuid, WassetOffsets<'a>>) -> Result<(), WassetError> {
if reader.name().starts_with(Self::ASSET_MANIFEST_SECTION_PREFIX) {
let id = Uuid::try_parse(&reader.name()[Self::ASSET_MANIFEST_SECTION_PREFIX.len()..])
.map_err(WassetError::from_deserialize)?;
offsets.entry(id).or_default().manifest = reader.data();
}
else if reader.name().starts_with(Self::ASSET_DATA_SECTION_PREFIX) {
let id = Uuid::try_parse(&reader.name()[Self::ASSET_DATA_SECTION_PREFIX.len()..])
.map_err(WassetError::from_deserialize)?;
offsets.entry(id).or_default().data_offset = reader.data_offset() as u32;
}
Ok(())
}
}
impl<'a, A: AssetSchema> IntoIterator for &'a WassetParser<'a, A> {
type Item = (WassetId, Result<A, WassetError>);
type IntoIter = WassetIter<'a, A>;
fn into_iter(self) -> Self::IntoIter {
WassetIter {
iter: self.manifest.asset_ranges.iter(),
parser: self
}
}
}
pub struct WassetIter<'a, A: AssetSchema> {
iter: std::collections::hash_map::Iter<'a, WassetId, Range<u32>>,
parser: &'a WassetParser<'a, A>
}
impl<'a, A: AssetSchema> Iterator for WassetIter<'a, A> {
type Item = (WassetId, Result<A, WassetError>);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(id, range)| (*id, self.parser.load_by_range(range.clone()).and_then(|x| x.deserialize())))
}
}
#[derive(Copy, Clone, Debug, Default)]
struct WassetOffsets<'a> {
data_offset: u32,
manifest: &'a [u8],
}