355 lines
12 KiB
Rust
355 lines
12 KiB
Rust
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<u8, InterpError> {
|
|
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<u32, InterpError> {
|
|
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<i64, InterpError> {
|
|
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<f64, InterpError> {
|
|
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<String, InterpError> {
|
|
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<Option<String>, 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<Option<Value>, 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<Vec<Value>, 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<Vec<String>, 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)?;
|
|
}
|
|
}
|
|
}
|