use crate::{
error::Error,
literal::{self, Literal},
reader::Reader,
ty::Type,
};
pub type AttributeFieldValue<'a> = Literal<'a>;
#[derive(Clone, Debug)]
pub struct Attribute<'a> {
pub name: &'a [u8],
pub fields: Vec<AttributeField<'a>>,
}
#[derive(Clone, Debug)]
pub struct AttributeField<'a> {
pub type_no: u32,
pub value: Literal<'a>,
}
const MAX_NAME_LEN: u32 = 0x4000_0000;
pub(crate) fn parse_block<'a>(
reader: &mut Reader<'a>,
types: &[Type<'a>],
) -> Result<Vec<Attribute<'a>>, Error> {
let count = reader.u32_le("attribute block count")?;
if count == 0 {
return Ok(Vec::new());
}
if count > MAX_NAME_LEN {
return Err(Error::Overflow {
what: "attribute block count",
});
}
let mut out = Vec::with_capacity(count as usize);
for _ in 0..count {
out.push(parse_attribute(reader, types)?);
}
Ok(out)
}
fn parse_attribute<'a>(
reader: &mut Reader<'a>,
types: &[Type<'a>],
) -> Result<Attribute<'a>, Error> {
let name_len = reader.u32_le("attribute name length")?;
if name_len > MAX_NAME_LEN {
return Err(Error::Overflow {
what: "attribute name length",
});
}
let name = reader.take(name_len as usize, "attribute name bytes")?;
let field_count = reader.u32_le("attribute field count")?;
if field_count > MAX_NAME_LEN {
return Err(Error::Overflow {
what: "attribute field count",
});
}
let mut fields = Vec::with_capacity(field_count as usize);
for _ in 0..field_count {
fields.push(parse_field(reader, types)?);
}
Ok(Attribute { name, fields })
}
fn parse_field<'a>(
reader: &mut Reader<'a>,
types: &[Type<'a>],
) -> Result<AttributeField<'a>, Error> {
let type_no = reader.u32_le("attribute field TypeNo")?;
let count = u32::try_from(types.len()).unwrap_or(u32::MAX);
let ty = types
.get(type_no as usize)
.ok_or(Error::TypeIndexOutOfRange {
index: type_no,
count,
})?;
let value = literal::parse_literal(reader, ty)?;
Ok(AttributeField { type_no, value })
}
#[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 empty_block() {
let buf = 0u32.to_le_bytes();
let mut r = Reader::new(&buf);
let attrs = parse_block(&mut r, &[]).unwrap();
assert!(attrs.is_empty());
}
#[test]
fn parses_single_attribute_with_u32_field() {
let mut buf = Vec::new();
put_le32(&mut buf, 1); put_le32(&mut buf, 4); buf.extend_from_slice(b"Test");
put_le32(&mut buf, 1); put_le32(&mut buf, 0); put_le32(&mut buf, 0xDEAD_BEEF); let types = [ty(BaseType::U32, TypeBody::Bare)];
let mut r = Reader::new(&buf);
let attrs = parse_block(&mut r, &types).unwrap();
assert_eq!(attrs.len(), 1);
assert_eq!(attrs[0].name, b"Test");
assert_eq!(attrs[0].fields.len(), 1);
assert_eq!(
attrs[0].fields[0].value,
AttributeFieldValue::U32(0xDEAD_BEEF),
);
}
#[test]
fn parses_string_field() {
let mut buf = Vec::new();
put_le32(&mut buf, 1);
put_le32(&mut buf, 4);
buf.extend_from_slice(b"Desc");
put_le32(&mut buf, 1);
put_le32(&mut buf, 0); put_le32(&mut buf, 5); buf.extend_from_slice(b"hello");
let types = [ty(BaseType::String, TypeBody::Bare)];
let mut r = Reader::new(&buf);
let attrs = parse_block(&mut r, &types).unwrap();
assert_eq!(
attrs[0].fields[0].value,
AttributeFieldValue::String(b"hello")
);
}
#[test]
fn parses_set_field_with_correct_byte_size() {
let mut buf = Vec::new();
put_le32(&mut buf, 1);
put_le32(&mut buf, 1);
buf.push(b'S');
put_le32(&mut buf, 1);
put_le32(&mut buf, 0);
buf.extend_from_slice(&[0xAA, 0xBB, 0x01]);
let types = [ty(BaseType::Set, TypeBody::Set { bit_size: 17 })];
let mut r = Reader::new(&buf);
let attrs = parse_block(&mut r, &types).unwrap();
assert_eq!(
attrs[0].fields[0].value,
AttributeFieldValue::Set(&[0xAA, 0xBB, 0x01][..]),
);
}
#[test]
fn rejects_unsupported_basetype() {
let mut buf = Vec::new();
put_le32(&mut buf, 1);
put_le32(&mut buf, 1);
buf.push(b'X');
put_le32(&mut buf, 1);
put_le32(&mut buf, 0); let types = [ty(
BaseType::Record,
TypeBody::Record {
field_types: vec![],
},
)];
let mut r = Reader::new(&buf);
let err = parse_block(&mut r, &types).unwrap_err();
assert!(matches!(err, Error::UnknownBaseType { .. }));
}
#[test]
fn rejects_out_of_range_type_ref() {
let mut buf = Vec::new();
put_le32(&mut buf, 1);
put_le32(&mut buf, 1);
buf.push(b'X');
put_le32(&mut buf, 1);
put_le32(&mut buf, 99); let types: [Type<'static>; 0] = [];
let mut r = Reader::new(&buf);
let err = parse_block(&mut r, &types).unwrap_err();
assert!(matches!(err, Error::TypeIndexOutOfRange { .. }));
}
}