Lines
0 %
Functions
Branches
100 %
use crate::entity::{EntityRef, Split, Tag, Transaction};
use crate::error::{Error, Result};
use crate::host;
use scripting_format::{
ContextType, ENTITY_HEADER_SIZE, EntityHeader, EntityType, GLOBAL_HEADER_SIZE, GlobalHeader,
};
pub struct InputReader {
buffer: &'static [u8],
header: GlobalHeader,
base_offset: u32,
}
impl InputReader {
pub fn new() -> Result<Self> {
let base_offset = host::input_offset();
let base = base_offset as *const u8;
// SAFETY: WASM linear memory is valid for the lifetime of the module.
// The host guarantees the input buffer is properly initialized.
let (buffer, header) = unsafe {
let header_slice = core::slice::from_raw_parts(base, GLOBAL_HEADER_SIZE);
let header = GlobalHeader::from_bytes(header_slice).ok_or(Error::InvalidMagic)?;
let total_size =
(header.strings_pool_offset - base_offset + header.strings_pool_size) as usize;
let buffer = core::slice::from_raw_parts(base, total_size);
(buffer, header)
Ok(Self {
buffer,
header,
base_offset,
})
pub fn context_type(&self) -> Result<ContextType> {
ContextType::try_from(self.header.context_type).map_err(|()| Error::InvalidEntityType)
pub fn primary_entity_type(&self) -> Result<EntityType> {
EntityType::try_from(self.header.primary_entity_type).map_err(|()| Error::InvalidEntityType)
#[must_use]
pub fn entity_count(&self) -> u32 {
self.header.input_entity_count
pub fn primary_entity_idx(&self) -> u32 {
self.header.primary_entity_idx
fn strings_pool(&self) -> &[u8] {
let offset = (self.header.strings_pool_offset - self.base_offset) as usize;
let size = self.header.strings_pool_size as usize;
&self.buffer[offset..offset + size]
fn entity_offset(&self, idx: u32) -> Result<usize> {
if idx >= self.header.input_entity_count {
return Err(Error::EntityNotFound);
let mut offset = (self.header.entities_offset - self.base_offset) as usize;
for _ in 0..idx {
let header_bytes = &self.buffer[offset..offset + ENTITY_HEADER_SIZE];
let header = EntityHeader::from_bytes(header_bytes).ok_or(Error::InvalidHeader)?;
offset += ENTITY_HEADER_SIZE + header.data_size as usize;
Ok(offset)
pub fn entity(&self, idx: u32) -> Result<EntityRef<'_>> {
let offset = self.entity_offset(idx)?;
let data_offset = offset + ENTITY_HEADER_SIZE;
let data_size = header.data_size as usize;
let data = &self.buffer[data_offset..data_offset + data_size];
Ok(EntityRef {
data,
strings_pool: self.strings_pool(),
pub fn primary(&self) -> Result<EntityRef<'_>> {
self.entity(self.header.primary_entity_idx)
pub fn primary_transaction(&self) -> Result<Transaction> {
self.primary()?.as_transaction()
pub fn transaction(&self, idx: u32) -> Result<Transaction> {
self.entity(idx)?.as_transaction()
pub fn split(&self, idx: u32) -> Result<Split> {
self.entity(idx)?.as_split()
pub fn tag(&self, idx: u32) -> Result<Tag<'_>> {
self.entity(idx)?.as_tag()
pub fn splits_for(&self, parent_idx: u32) -> impl Iterator<Item = Result<Split>> + '_ {
let count = self.header.input_entity_count;
(0..count).filter_map(move |idx| {
let entity = match self.entity(idx) {
Ok(e) => e,
Err(e) => return Some(Err(e)),
if entity.header.parent_idx == parent_idx as i32 {
match entity.entity_type() {
Ok(EntityType::Split) => Some(entity.as_split()),
Ok(_) => None,
Err(e) => Some(Err(e)),
} else {
None
pub fn tags_for(&self, parent_idx: u32) -> impl Iterator<Item = Result<Tag<'_>>> + '_ {
Ok(EntityType::Tag) => Some(entity.as_tag()),