use crate::{error::Error, reader::Reader};
const FLAG_EXPORTED: u8 = 0x01;
#[derive(Clone, Debug)]
pub struct Var<'a> {
pub type_no: u32,
pub flags_raw: u8,
pub export_name: Option<&'a [u8]>,
}
const MAX_NAME_LEN: u32 = 0x4000_0000;
pub(crate) fn parse_var<'a>(reader: &mut Reader<'a>, type_count: u32) -> Result<Var<'a>, Error> {
let type_no = reader.u32_le("var TypeNo")?;
let flags = reader.u8("var Flags")?;
if type_no >= type_count {
return Err(Error::TypeIndexOutOfRange {
index: type_no,
count: type_count,
});
}
let export_name = if (flags & FLAG_EXPORTED) != 0 {
let len = reader.u32_le("var export-name length")?;
if len > MAX_NAME_LEN {
return Err(Error::Overflow {
what: "var export-name length",
});
}
Some(reader.take(len as usize, "var export-name bytes")?)
} else {
None
};
Ok(Var {
type_no,
flags_raw: flags,
export_name,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn put_le32(out: &mut Vec<u8>, v: u32) {
out.extend_from_slice(&v.to_le_bytes());
}
#[test]
fn parses_unexported_var() {
let mut buf = Vec::new();
put_le32(&mut buf, 7);
buf.push(0);
let mut r = Reader::new(&buf);
let v = parse_var(&mut r, 8).unwrap();
assert_eq!(v.type_no, 7);
assert!(v.export_name.is_none());
}
#[test]
fn parses_exported_var() {
let mut buf = Vec::new();
put_le32(&mut buf, 0);
buf.push(1);
put_le32(&mut buf, 4);
buf.extend_from_slice(b"name");
let mut r = Reader::new(&buf);
let v = parse_var(&mut r, 1).unwrap();
assert_eq!(v.type_no, 0);
assert_eq!(v.export_name, Some(&b"name"[..]));
}
#[test]
fn rejects_type_ref_out_of_range() {
let mut buf = Vec::new();
put_le32(&mut buf, 99);
buf.push(0);
let mut r = Reader::new(&buf);
let err = parse_var(&mut r, 5).unwrap_err();
assert!(matches!(
err,
Error::TypeIndexOutOfRange {
index: 99,
count: 5
}
));
}
}