1use crate::*;
2
3ext! {
7 name: Iref,
8 versions: [0, 1],
9 flags: {}
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Reference {
15 pub reference_type: FourCC,
16 pub from_item_id: u32,
17 pub to_item_ids: Vec<u32>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct Iref {
23 pub references: Vec<Reference>,
24}
25
26impl AtomExt for Iref {
27 type Ext = IrefExt;
28
29 const KIND_EXT: FourCC = FourCC::new(b"iref");
30
31 fn decode_body_ext<B: Buf>(buf: &mut B, ext: IrefExt) -> Result<Self> {
32 let mut references = vec![];
33 while buf.has_remaining() {
34 if buf.remaining() < 4 {
35 return Err(Error::InvalidSize);
36 }
37 let box_len = u32::decode(buf)? as usize;
38 let body_len = box_len.checked_sub(4).ok_or(Error::InvalidSize)?;
39 if body_len > buf.remaining() {
40 return Err(Error::InvalidSize);
41 }
42
43 let mut body = buf.slice(body_len);
44 if ext.version == IrefVersion::V0 {
45 let reference_type = FourCC::decode(&mut body)?;
46 let from_item_id: u32 = u16::decode(&mut body)?.into();
47 let reference_count: u16 = u16::decode(&mut body)?;
48 let mut to_item_ids: Vec<u32> = vec![];
49 for _ in 0..reference_count {
50 let to_item_id: u32 = u16::decode(&mut body)?.into();
51 to_item_ids.push(to_item_id);
52 }
53 let reference = Reference {
54 reference_type,
55 from_item_id,
56 to_item_ids,
57 };
58 references.push(reference);
59 } else {
60 let reference_type = FourCC::decode(&mut body)?;
61 let from_item_id: u32 = u32::decode(&mut body)?;
62 let reference_count: u16 = u16::decode(&mut body)?;
63 let mut to_item_ids: Vec<u32> = vec![];
64 for _ in 0..reference_count {
65 let to_item_id: u32 = u32::decode(&mut body)?;
66 to_item_ids.push(to_item_id);
67 }
68 let reference = Reference {
69 reference_type,
70 from_item_id,
71 to_item_ids,
72 };
73 references.push(reference);
74 }
75
76 if body.has_remaining() {
77 return Err(Error::InvalidSize);
78 }
79 buf.advance(body_len);
80 }
81 Ok(Iref { references })
82 }
83
84 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<IrefExt> {
85 let mut version = IrefVersion::V0;
86 'reference: for reference in &self.references {
87 if reference.from_item_id > (u16::MAX as u32) {
88 version = IrefVersion::V1;
89 break 'reference;
90 }
91 for id in &reference.to_item_ids {
92 if *id > (u16::MAX as u32) {
93 version = IrefVersion::V1;
94 break 'reference;
95 }
96 }
97 }
98 if version == IrefVersion::V0 {
99 for reference in &self.references {
100 let size = (4 + 4 + 2 + 2 + 2 * reference.to_item_ids.len()) as u32;
101 size.encode(buf)?;
102 reference.reference_type.encode(buf)?;
103 (reference.from_item_id as u16).encode(buf)?;
104 (reference.to_item_ids.len() as u16).encode(buf)?;
105 for id in &reference.to_item_ids {
106 (*id as u16).encode(buf)?;
107 }
108 }
109 } else {
110 for reference in &self.references {
111 let size = (4 + 4 + 4 + 2 + 4 * reference.to_item_ids.len()) as u32;
112 size.encode(buf)?;
113 reference.reference_type.encode(buf)?;
114 reference.from_item_id.encode(buf)?;
115 (reference.to_item_ids.len() as u16).encode(buf)?;
116 for id in &reference.to_item_ids {
117 id.encode(buf)?;
118 }
119 }
120 }
121 Ok(IrefExt { version })
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn oversized_nested_reference_returns_error() {
131 let body: &[u8] = &[
132 0x00, 0x00, 0x00, 0x20, b'd', b'i', b'm', b'g', 0x00, 0x01, 0x00, 0x00, ];
137
138 assert!(matches!(
139 Iref::decode_body_ext(
140 &mut std::io::Cursor::new(body),
141 IrefExt {
142 version: IrefVersion::V0,
143 },
144 ),
145 Err(Error::InvalidSize)
146 ));
147 }
148
149 #[test]
150 fn nested_reference_smaller_than_header_returns_error() {
151 let body: &[u8] = &[0x00, 0x00, 0x00, 0x03];
152
153 assert!(matches!(
154 Iref::decode_body_ext(
155 &mut std::io::Cursor::new(body),
156 IrefExt {
157 version: IrefVersion::V0,
158 },
159 ),
160 Err(Error::InvalidSize)
161 ));
162 }
163}