use core::mem;
use crate::{
BinaryReader, BinaryReaderError, ExternalKind, FromReader, GlobalType, MemoryType, Result,
SectionLimited, SectionLimitedIntoIterWithOffsets, TableType, TagType,
};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum TypeRef {
Func(u32),
Table(TableType),
Memory(MemoryType),
Global(GlobalType),
Tag(TagType),
FuncExact(u32),
}
#[derive(Debug, Clone)]
pub enum Imports<'a> {
Single(usize, Import<'a>),
Compact1 {
module: &'a str,
items: SectionLimited<'a, ImportItemCompact<'a>>,
},
Compact2 {
module: &'a str,
ty: TypeRef,
names: SectionLimited<'a, &'a str>,
},
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Import<'a> {
pub module: &'a str,
pub name: &'a str,
pub ty: TypeRef,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ImportItemCompact<'a> {
pub name: &'a str,
pub ty: TypeRef,
}
pub type ImportSectionReader<'a> = SectionLimited<'a, Imports<'a>>;
impl<'a> FromReader<'a> for Imports<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let start = reader.original_position();
let module = reader.read_string()?;
let single_item_name = reader.read_string()?;
let discriminator = reader.peek_bytes(1)?[0];
match (single_item_name, discriminator) {
("", 0x7F) => {
if !reader.compact_imports() {
bail!(
reader.original_position(),
"invalid leading byte 0x7F with compact imports \
proposal disabled"
);
}
reader.read_bytes(1)?;
let items = reader.skip(|reader| {
let count = reader.read_var_u32()?;
for _ in 0..count {
reader.skip_string()?;
reader.read::<TypeRef>()?;
}
Ok(())
})?;
Ok(Imports::Compact1 {
module,
items: SectionLimited::new(items)?,
})
}
("", 0x7E) => {
if !reader.compact_imports() {
bail!(
reader.original_position(),
"invalid leading byte 0x7E with compact imports \
proposal disabled"
);
}
reader.read_bytes(1)?;
let ty: TypeRef = reader.read()?;
let names = reader.skip(|reader| {
let count = reader.read_var_u32()?;
for _ in 0..count {
reader.skip_string()?;
}
Ok(())
})?;
Ok(Imports::Compact2 {
module,
ty,
names: SectionLimited::new(names)?,
})
}
_ => Ok(Imports::Single(
start,
Import {
module: module,
name: single_item_name,
ty: reader.read()?,
},
)),
}
}
}
impl<'a> FromReader<'a> for Import<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(Import {
module: reader.read()?,
name: reader.read()?,
ty: reader.read()?,
})
}
}
impl<'a> FromReader<'a> for ImportItemCompact<'a> {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(ImportItemCompact {
name: reader.read()?,
ty: reader.read()?,
})
}
}
impl<'a> FromReader<'a> for TypeRef {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.read()? {
ExternalKind::Func => TypeRef::Func(reader.read_var_u32()?),
ExternalKind::FuncExact => TypeRef::FuncExact(reader.read_var_u32()?),
ExternalKind::Table => TypeRef::Table(reader.read()?),
ExternalKind::Memory => TypeRef::Memory(reader.read()?),
ExternalKind::Global => TypeRef::Global(reader.read()?),
ExternalKind::Tag => TypeRef::Tag(reader.read()?),
})
}
}
impl<'a> SectionLimited<'a, Imports<'a>> {
pub fn into_imports(self) -> impl Iterator<Item = Result<Import<'a>>> {
self.into_imports_with_offsets()
.map(|res| res.map(|(_, import)| import))
}
pub fn into_imports_with_offsets(self) -> impl Iterator<Item = Result<(usize, Import<'a>)>> {
self.into_iter().flat_map(|res| match res {
Ok(imports) => imports.into_iter(),
Err(e) => ImportsIter {
state: ImportsIterState::Error(e),
},
})
}
}
impl<'a> IntoIterator for Imports<'a> {
type Item = Result<(usize, Import<'a>)>;
type IntoIter = ImportsIter<'a>;
fn into_iter(self) -> Self::IntoIter {
ImportsIter {
state: match self {
Imports::Single(start, import) => ImportsIterState::Single(start, import),
Imports::Compact1 { module, items } => ImportsIterState::Compact1 {
module: module,
iter: items.into_iter_with_offsets(),
},
Imports::Compact2 {
module,
ty,
names: items,
} => ImportsIterState::Compact2 {
module: module,
ty: ty,
iter: items.into_iter_with_offsets(),
},
},
}
}
}
pub struct ImportsIter<'a> {
state: ImportsIterState<'a>,
}
enum ImportsIterState<'a> {
Done,
Error(BinaryReaderError),
Single(usize, Import<'a>),
Compact1 {
module: &'a str,
iter: SectionLimitedIntoIterWithOffsets<'a, ImportItemCompact<'a>>,
},
Compact2 {
module: &'a str,
ty: TypeRef,
iter: SectionLimitedIntoIterWithOffsets<'a, &'a str>,
},
}
impl<'a> Iterator for ImportsIter<'a> {
type Item = Result<(usize, Import<'a>)>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.state {
ImportsIterState::Done => None,
ImportsIterState::Error(_) => {
let ImportsIterState::Error(e) =
mem::replace(&mut self.state, ImportsIterState::Done)
else {
unreachable!()
};
Some(Err(e))
}
ImportsIterState::Single(offset, i) => {
let ret = Some(Ok((*offset, *i)));
self.state = ImportsIterState::Done;
ret
}
ImportsIterState::Compact1 { module, iter } => {
let item = iter.next()?;
Some(item.map(|(offset, item)| {
(
offset,
Import {
module,
name: item.name,
ty: item.ty,
},
)
}))
}
ImportsIterState::Compact2 { module, ty, iter } => {
let item = iter.next()?;
Some(item.map(|(offset, name)| {
(
offset,
Import {
module,
name,
ty: *ty,
},
)
}))
}
}
}
}