use super::{ConstExpr, MemoryIdx};
use crate::Error;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::slice;
/// A Wasm [`Module`] data segment.
///
/// [`Module`]: [`super::Module`]
#[derive(Debug)]
pub struct DataSegment {
inner: DataSegmentInner,
}
/// The inner structure of a [`DataSegment`].
#[derive(Debug)]
pub enum DataSegmentInner {
/// An active data segment that is initialized upon Wasm module instantiation.
Active(ActiveDataSegment),
/// A passive data segment that can be used by some Wasm bulk instructions.
Passive {
/// The bytes of the passive data segment.
bytes: PassiveDataSegmentBytes,
},
}
/// An active data segment that is initialized upon Wasm module instantiation.
#[derive(Debug)]
pub struct ActiveDataSegment {
/// The linear memory that is to be initialized with this active segment.
memory_index: MemoryIdx,
/// The offset at which the data segment is initialized.
offset: ConstExpr,
/// Number of bytes of the active data segment.
len: u32,
}
impl ActiveDataSegment {
/// Returns the Wasm module memory index that is to be initialized.
pub fn memory_index(&self) -> MemoryIdx {
self.memory_index
}
/// Returns the offset expression of the [`ActiveDataSegment`].
pub fn offset(&self) -> &ConstExpr {
&self.offset
}
/// Returns the number of bytes of the [`ActiveDataSegment`] as `usize`.
pub fn len(&self) -> usize {
self.len as usize
}
}
/// The bytes of the passive data segment.
#[derive(Debug, Clone)]
pub struct PassiveDataSegmentBytes {
bytes: Arc<[u8]>,
}
impl AsRef<[u8]> for PassiveDataSegmentBytes {
fn as_ref(&self) -> &[u8] {
&self.bytes[..]
}
}
#[test]
fn size_of_data_segment() {
assert!(core::mem::size_of::<DataSegment>() <= 32);
assert!(core::mem::size_of::<DataSegmentInner>() <= 32);
}
impl DataSegment {
/// Returns the bytes of the [`DataSegment`] if passive, otherwise returns `None`.
pub fn passive_data_segment_bytes(&self) -> Option<PassiveDataSegmentBytes> {
match &self.inner {
DataSegmentInner::Active { .. } => None,
DataSegmentInner::Passive { bytes } => Some(bytes.clone()),
}
}
}
/// Stores all data segments and their associated data.
#[derive(Debug)]
pub struct DataSegments {
/// All data segments.
segments: Box<[DataSegment]>,
/// All bytes from all active data segments.
///
/// # Note
///
/// We deliberately do not use `Box<[u8]>` here because it is not possible
/// to properly pre-reserve space for the bytes and thus finishing construction
/// of the [`DataSegments`] would highly likely reallocate and mass-copy
/// which we prevent by simply using a `Vec<u8>` instead.
bytes: Vec<u8>,
}
impl DataSegments {
/// Creates a new [`DataSegmentsBuilder`].
pub fn build() -> DataSegmentsBuilder {
DataSegmentsBuilder {
segments: Vec::new(),
bytes: Vec::new(),
}
}
}
/// Builds up a [`DataSegments`] instance.
#[derive(Debug)]
pub struct DataSegmentsBuilder {
/// All active or passive data segments built-up so far.
segments: Vec<DataSegment>,
/// The bytes of all active data segments.
bytes: Vec<u8>,
}
impl DataSegmentsBuilder {
/// Reserves space for at least `additional` new [`DataSegments`].
pub fn reserve(&mut self, count: usize) {
assert!(
self.segments.capacity() == 0,
"must not reserve multiple times"
);
self.segments.reserve(count);
}
/// Pushes another [`DataSegment`] to the [`DataSegmentsBuilder`].
///
/// # Panics
///
/// If an active data segment has too many bytes.
pub fn push_data_segment(&mut self, segment: wasmparser::Data) -> Result<(), Error> {
match segment.kind {
wasmparser::DataKind::Passive => {
self.segments.push(DataSegment {
inner: DataSegmentInner::Passive {
bytes: PassiveDataSegmentBytes {
bytes: segment.data.into(),
},
},
});
}
wasmparser::DataKind::Active {
memory_index,
offset_expr,
} => {
let memory_index = MemoryIdx::from(memory_index);
let offset = ConstExpr::new(offset_expr);
let len = u32::try_from(segment.data.len()).unwrap_or_else(|_x| {
panic!("data segment has too many bytes: {}", segment.data.len())
});
self.bytes.extend_from_slice(segment.data);
self.segments.push(DataSegment {
inner: DataSegmentInner::Active(ActiveDataSegment {
memory_index,
offset,
len,
}),
});
}
}
Ok(())
}
pub fn finish(self) -> DataSegments {
DataSegments {
segments: self.segments.into(),
bytes: self.bytes,
}
}
}
impl<'a> IntoIterator for &'a DataSegments {
type Item = InitDataSegment<'a>;
type IntoIter = InitDataSegmentIter<'a>;
fn into_iter(self) -> Self::IntoIter {
InitDataSegmentIter {
segments: self.segments.iter(),
bytes: &self.bytes[..],
}
}
}
/// Iterator over the [`DataSegment`]s and their associated bytes.
#[derive(Debug)]
pub struct InitDataSegmentIter<'a> {
segments: slice::Iter<'a, DataSegment>,
bytes: &'a [u8],
}
impl<'a> Iterator for InitDataSegmentIter<'a> {
type Item = InitDataSegment<'a>;
fn next(&mut self) -> Option<Self::Item> {
let segment = self.segments.next()?;
match &segment.inner {
DataSegmentInner::Active(segment) => {
let (bytes, rest) = self.bytes.split_at(segment.len());
self.bytes = rest;
Some(InitDataSegment::Active {
memory_index: segment.memory_index(),
offset: segment.offset(),
bytes,
})
}
DataSegmentInner::Passive { bytes } => Some(InitDataSegment::Passive {
bytes: bytes.clone(),
}),
}
}
}
/// Iterated-over [`DataSegment`] when instantiating a [`Module`].
///
/// [`Module`]: crate::Module
pub enum InitDataSegment<'a> {
Active {
/// The linear memory that is to be initialized with this active segment.
memory_index: MemoryIdx,
/// The offset at which the data segment is initialized.
offset: &'a ConstExpr,
/// The bytes of the active data segment.
bytes: &'a [u8],
},
Passive {
bytes: PassiveDataSegmentBytes,
},
}
use ;
use crateError;
use ;
use slice;
/// A Wasm [`Module`] data segment.
///
/// [`Module`]: [`super::Module`]
/// The inner structure of a [`DataSegment`].
/// An active data segment that is initialized upon Wasm module instantiation.
/// The bytes of the passive data segment.
/// Stores all data segments and their associated data.
/// Builds up a [`DataSegments`] instance.
/// Iterator over the [`DataSegment`]s and their associated bytes.
/// Iterated-over [`DataSegment`] when instantiating a [`Module`].
///
/// [`Module`]: crate::Module