use crate::{
error::Error,
literal::{self, Literal},
reader::Reader,
ty::Type,
};
const PS_ADDR_STACK_START: u32 = 0x6000_0000;
const PS_ADDR_NEGATIVE_STACK_START: u32 = 0x4000_0000;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarRef {
Global(u32),
Stack(i64),
}
impl VarRef {
pub fn from_param(param: u32) -> Self {
if param < PS_ADDR_NEGATIVE_STACK_START {
Self::Global(param)
} else {
Self::Stack(i64::from(param).wrapping_sub(i64::from(PS_ADDR_STACK_START)))
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum Operand<'a> {
Var(VarRef),
Literal {
type_no: u32,
value: Literal<'a>,
},
Indexed {
base: VarRef,
sub_index: u32,
},
DynamicIndexed {
base: VarRef,
index_var: VarRef,
},
}
pub(crate) fn parse_operand<'a>(
reader: &mut Reader<'a>,
types: &[Type<'a>],
) -> Result<Operand<'a>, Error> {
let var_type = reader.u8("operand VarType")?;
let param = reader.u32_le("operand Param")?;
let operand = match var_type {
0 => Operand::Var(VarRef::from_param(param)),
1 => {
let count = u32::try_from(types.len()).unwrap_or(u32::MAX);
let ty = types
.get(param as usize)
.ok_or(Error::TypeIndexOutOfRange {
index: param,
count,
})?;
let value = literal::parse_literal(reader, ty)?;
Operand::Literal {
type_no: param,
value,
}
}
2 => {
let sub_index = reader.u32_le("operand sub_index")?;
Operand::Indexed {
base: VarRef::from_param(param),
sub_index,
}
}
3 => {
let inner = reader.u32_le("operand dynamic-index Param")?;
Operand::DynamicIndexed {
base: VarRef::from_param(param),
index_var: VarRef::from_param(inner),
}
}
other => return Err(Error::UnknownBaseType { byte: other }),
};
Ok(operand)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ty::{BaseType, Type, TypeBody};
fn put_le32(out: &mut Vec<u8>, v: u32) {
out.extend_from_slice(&v.to_le_bytes());
}
fn ty(base_type: BaseType, body: TypeBody<'static>) -> Type<'static> {
Type {
base_type,
body,
export_name: None,
attributes: Vec::new(),
}
}
#[test]
fn parses_global_var_ref() {
let mut buf = vec![0u8]; put_le32(&mut buf, 7);
let mut r = Reader::new(&buf);
let op = parse_operand(&mut r, &[]).unwrap();
assert_eq!(op, Operand::Var(VarRef::Global(7)));
}
#[test]
fn parses_stack_var_ref() {
let mut buf = vec![0u8];
put_le32(&mut buf, PS_ADDR_STACK_START + 4);
let mut r = Reader::new(&buf);
let op = parse_operand(&mut r, &[]).unwrap();
assert_eq!(op, Operand::Var(VarRef::Stack(4)));
}
#[test]
fn parses_typed_literal_u32() {
let mut buf = vec![1u8];
put_le32(&mut buf, 0); put_le32(&mut buf, 0xCAFE_BABE);
let types = [ty(BaseType::U32, TypeBody::Bare)];
let mut r = Reader::new(&buf);
let op = parse_operand(&mut r, &types).unwrap();
assert!(matches!(
op,
Operand::Literal {
type_no: 0,
value: Literal::U32(0xCAFE_BABE),
},
));
}
#[test]
fn parses_indexed_record_field() {
let mut buf = vec![2u8];
put_le32(&mut buf, 3); put_le32(&mut buf, 1); let mut r = Reader::new(&buf);
let op = parse_operand(&mut r, &[]).unwrap();
assert_eq!(
op,
Operand::Indexed {
base: VarRef::Global(3),
sub_index: 1,
},
);
}
#[test]
fn parses_dynamic_indexed() {
let mut buf = vec![3u8];
put_le32(&mut buf, 5); put_le32(&mut buf, PS_ADDR_STACK_START + 8); let mut r = Reader::new(&buf);
let op = parse_operand(&mut r, &[]).unwrap();
assert_eq!(
op,
Operand::DynamicIndexed {
base: VarRef::Global(5),
index_var: VarRef::Stack(8),
},
);
}
#[test]
fn rejects_unknown_var_type() {
let mut buf = vec![9u8];
put_le32(&mut buf, 0);
let mut r = Reader::new(&buf);
assert!(parse_operand(&mut r, &[]).is_err());
}
}