use super::opcodes::*; use super::types::{InterpError, Value}; use compact_str::CompactString; pub struct Reader<'a> { pub data: &'a [u8], pub pos: usize, } impl<'a> Reader<'a> { #[inline(always)] pub fn new(data: &'a [u8]) -> Self { Self { data, pos: 0 } } #[inline(always)] pub fn remaining(&self) -> usize { self.data.len() - self.pos } #[inline(always)] fn require(&self, n: usize) -> Result<(), InterpError> { if self.pos + n > self.data.len() { Err(InterpError::UnexpectedEof) } else { Ok(()) } } #[inline(always)] pub fn read_byte(&mut self) -> Result { self.require(1)?; let b = unsafe { *self.data.get_unchecked(self.pos) }; self.pos += 1; Ok(b) } #[inline(always)] pub fn read_u32(&mut self) -> Result { self.require(4)?; let v = unsafe { let ptr = self.data.as_ptr().add(self.pos) as *const [u8; 4]; u32::from_le_bytes(std::ptr::read_unaligned(ptr)) }; self.pos += 4; Ok(v) } #[inline(always)] pub fn read_i64(&mut self) -> Result { self.require(8)?; let v = unsafe { let ptr = self.data.as_ptr().add(self.pos) as *const [u8; 8]; i64::from_le_bytes(std::ptr::read_unaligned(ptr)) }; self.pos += 8; Ok(v) } #[inline(always)] pub fn read_f64(&mut self) -> Result { self.require(8)?; let v = unsafe { let ptr = self.data.as_ptr().add(self.pos) as *const [u8; 8]; f64::from_le_bytes(std::ptr::read_unaligned(ptr)) }; self.pos += 8; Ok(v) } #[inline(always)] pub fn read_str_ref(&mut self) -> Result<&'a str, InterpError> { let len = self.read_u32()? as usize; self.require(len)?; let slice = &self.data[self.pos .. self.pos + len]; self.pos += len; unsafe { Ok(std::str::from_utf8_unchecked(slice)) } } #[inline(always)] pub fn read_string(&mut self) -> Result { self.read_str_ref().map(|s| s.to_owned()) } #[inline(always)] pub fn skip_string(&mut self) -> Result<(), InterpError> { let len = self.read_u32()? as usize; self.require(len)?; self.pos += len; Ok(()) } pub fn read_value_as_string(&mut self, type_op: u8) -> Result, InterpError> { let s = match type_op { OP_PROP_STR | OP_PROP_COLOR | OP_PROP_FSPATH | OP_PROP_RHEI | OP_PROP_IDENT => { Some(self.read_string()?) } OP_PROP_VAR => { let name = self.read_str_ref()?; let mut s = String::with_capacity(name.len() + 1); s.push('$'); s.push_str(name); Some(s) } OP_PROP_INT => Some(self.read_i64()?.to_string()), OP_PROP_FLOAT => Some(self.read_f64()?.to_string()), OP_PROP_BOOL => Some((self.read_byte()? != 0).to_string()), OP_PROP_NULL => None, OP_PROP_ARRAY => { let items = self.read_array_as_strings()?; Some(items.join(",")) } OP_PROP_UNIT => { let num = self.read_f64()?; let unit = self.read_str_ref()?; if num.fract() == 0.0 { Some(format!("{}{}", num as i64, unit)) } else { Some(format!("{}{}", num, unit)) } } OP_PROP_CALL => { let name = self.read_string()?; let arg_count = self.read_u32()? as usize; let mut args = Vec::with_capacity(arg_count); for _ in 0..arg_count { let op = self.read_byte()?; let val = self.read_value_as_string(op)?.unwrap_or_default(); args.push(val); } Some(format!("{}({})", name, args.join(","))) } _ => None, }; Ok(s) } pub fn read_value(&mut self, type_op: u8) -> Result, InterpError> { let val = match type_op { OP_PROP_STR | OP_PROP_COLOR | OP_PROP_FSPATH | OP_PROP_IDENT => { Some(Value::Str(CompactString::new(self.read_string()?))) } OP_PROP_RHEI => { let raw = self.read_string()?; let mut s = String::with_capacity(super::rhei::RHEI_PREFIX.len() + raw.len()); s.push_str(super::rhei::RHEI_PREFIX); s.push_str(&raw); Some(Value::Str(CompactString::new(s))) } OP_PROP_VAR => { let name = self.read_str_ref()?; let mut s = String::with_capacity(name.len() + 1); s.push('$'); s.push_str(name); Some(Value::Str(CompactString::new(s))) } OP_PROP_INT => Some(Value::Int(self.read_i64()?)), OP_PROP_FLOAT => Some(Value::Float(self.read_f64()?)), OP_PROP_BOOL => Some(Value::Bool(self.read_byte()? != 0)), OP_PROP_NULL => None, OP_PROP_ARRAY => { let items = self.read_array_as_values()?; Some(Value::Array(items)) } OP_PROP_UNIT => { let num = self.read_f64()?; let unit = self.read_str_ref()?; let s = if num.fract() == 0.0 { format!("{}{}", num as i64, unit) } else { format!("{}{}", num, unit) }; Some(Value::Str(CompactString::new(s))) } OP_PROP_CALL => { let name = self.read_string()?; let arg_count = self.read_u32()? as usize; let mut args = Vec::with_capacity(arg_count); for _ in 0..arg_count { let op = self.read_byte()?; let val = self.read_value(op)?.unwrap_or(Value::None); args.push(match val { Value::Str(s) => s.to_string(), Value::Int(i) => i.to_string(), Value::Float(f) => f.to_string(), Value::Bool(b) => b.to_string(), _ => String::new(), }); } Some(Value::Str(CompactString::new(format!("{}({})", name, args.join(","))))) } _ => None, }; Ok(val) } pub fn read_array_as_values(&mut self) -> Result, InterpError> { let count = self.read_u32()? as usize; let mut items = Vec::with_capacity(count); for _ in 0..count { let elem_op = self.read_byte()?; if let Some(v) = self.read_value(elem_op)? { items.push(v); } } Ok(items) } pub fn read_array_as_strings(&mut self) -> Result, InterpError> { let count = self.read_u32()? as usize; let mut items = Vec::with_capacity(count); for _ in 0..count { let elem_op = self.read_byte()?; if let Some(s) = self.read_value_as_string(elem_op)? { items.push(s); } } Ok(items) } pub fn skip_value(&mut self, type_op: u8) -> Result<(), InterpError> { match type_op { OP_PROP_STR | OP_PROP_COLOR | OP_PROP_FSPATH | OP_PROP_VAR | OP_PROP_RHEI | OP_PROP_IDENT => { self.skip_string()?; } OP_PROP_INT => { self.read_i64()?; } OP_PROP_FLOAT => { self.read_f64()?; } OP_PROP_BOOL => { self.read_byte()?; } OP_PROP_NULL => {} OP_PROP_ARRAY => { let count = self.read_u32()?; for _ in 0..count { let elem_op = self.read_byte()?; self.skip_value(elem_op)?; } } OP_PROP_UNIT => { self.read_f64()?; self.skip_string()?; } OP_PROP_CALL => { self.skip_string()?; let arg_count = self.read_u32()?; for _ in 0..arg_count { let op = self.read_byte()?; self.skip_value(op)?; } } _ => {} } Ok(()) } pub fn skip_opcode(&mut self, op: u8) -> Result<(), InterpError> { match op { OP_VERSION => { self.read_i64()?; } OP_STYLE | OP_RHEI_BLK => { self.skip_string()?; } OP_PROP_STR | OP_PROP_COLOR | OP_PROP_FSPATH | OP_PROP_VAR | OP_PROP_RHEI | OP_PROP_INT | OP_PROP_FLOAT | OP_PROP_BOOL | OP_PROP_NULL | OP_PROP_ARRAY | OP_PROP_CALL | OP_PROP_UNIT | OP_PROP_IDENT => { self.skip_string()?; self.skip_value(op)?; } OP_GLOBAL | OP_LET => { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; } OP_SINGLETON => { self.skip_string()?; let count = self.read_u32()?; for _ in 0..count { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; } } OP_COMPONENT => { self.skip_string()?; let params = self.read_u32()?; for _ in 0..params { self.skip_string()?; self.skip_string()?; } self.skip_block()?; } OP_IF => { let vop = self.read_byte()?; self.skip_value(vop)?; self.skip_block()?; if self.read_byte()? == 1 { self.skip_block()?; } } OP_EACH => { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; self.skip_block()?; } OP_ON => { self.skip_string()?; let args = self.read_u32()?; for _ in 0..args { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; } self.skip_block()?; } OP_ELEM_PUSH => { self.skip_string()?; } OP_CONTENT => { let vop = self.read_byte()?; self.skip_value(vop)?; } OP_STYLE_RULE => { self.skip_string()?; let count = self.read_u32()?; for _ in 0..count { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; } } OP_STYLE_ANIM => { self.skip_string()?; let frame_count = self.read_u32()?; for _ in 0..frame_count { self.skip_string()?; let prop_count = self.read_u32()?; for _ in 0..prop_count { self.skip_string()?; let vop = self.read_byte()?; self.skip_value(vop)?; } } } _ => {} } Ok(()) } pub fn skip_block(&mut self) -> Result<(), InterpError> { loop { let op = self.read_byte()?; if op == OP_END_BLOCK { return Ok(()); } self.skip_opcode(op)?; } } }