1use crate::*;
2
3ext! {
7 name: Iloc,
8 versions: [0, 1, 2],
9 flags: {}
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct ItemLocationExtent {
15 pub item_reference_index: u64,
16 pub offset: u64,
17 pub length: u64,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct ItemLocation {
23 pub item_id: u32,
24 pub construction_method: u8, pub data_reference_index: u16,
26 pub base_offset: u64,
27 pub extents: Vec<ItemLocationExtent>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Iloc {
33 pub item_locations: Vec<ItemLocation>,
34}
35
36impl AtomExt for Iloc {
37 type Ext = IlocExt;
38
39 const KIND_EXT: FourCC = FourCC::new(b"iloc");
40
41 fn decode_body_ext<B: Buf>(buf: &mut B, ext: IlocExt) -> Result<Self> {
42 let sizes0 = u8::decode(buf)?;
43 let offset_size = sizes0 >> 4;
44 let length_size = sizes0 & 0x0F;
45 let sizes1 = u8::decode(buf)?;
46 let base_offset_size = sizes1 >> 4;
47 let index_size: u8 = if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
48 sizes1 & 0x0F
49 } else {
50 0
51 };
52
53 let item_count = if ext.version == IlocVersion::V0 || ext.version == IlocVersion::V1 {
54 u16::decode(buf)? as usize
55 } else {
56 u32::decode(buf)? as usize
57 };
58 let mut item_locations = vec![];
59 for _i in 0..item_count {
60 let item_id = if ext.version == IlocVersion::V0 || ext.version == IlocVersion::V1 {
61 u16::decode(buf)? as u32
62 } else {
63 u32::decode(buf)?
64 };
65 let construction_method: u8 =
66 if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
67 let construction_method_packed = u16::decode(buf)?;
68 (construction_method_packed & 0x0f) as u8
69 } else {
70 0
71 };
72 let data_reference_index = u16::decode(buf)?;
73 let base_offset = match base_offset_size {
74 0 => 0u64,
75 4 => u32::decode(buf)? as u64,
76 8 => u64::decode(buf)?,
77 _ => return Err(Error::Reserved),
78 };
79 let extent_count = u16::decode(buf)?;
80 let mut extents = vec![];
81 for _j in 0..extent_count {
82 let item_reference_index: u64 =
83 if ext.version == IlocVersion::V1 || ext.version == IlocVersion::V2 {
84 match index_size {
85 0 => 0,
86 4 => u32::decode(buf)? as u64,
87 8 => u64::decode(buf)?,
88 _ => return Err(Error::Reserved),
89 }
90 } else {
91 0
92 };
93 let extent_offset = match offset_size {
94 0 => 0u64,
95 4 => u32::decode(buf)? as u64,
96 8 => u64::decode(buf)?,
97 _ => return Err(Error::Reserved),
98 };
99 let extent_length = match length_size {
100 0 => 0u64,
101 4 => u32::decode(buf)? as u64,
102 8 => u64::decode(buf)?,
103 _ => return Err(Error::Reserved),
104 };
105 extents.push(ItemLocationExtent {
106 item_reference_index,
107 offset: extent_offset,
108 length: extent_length,
109 });
110 }
111 item_locations.push(ItemLocation {
112 item_id,
113 construction_method,
114 data_reference_index,
115 base_offset,
116 extents,
117 })
118 }
119 Ok(Iloc { item_locations })
120 }
121
122 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<IlocExt> {
123 let mut base_offset_size = 0u8;
124 for item_location in &self.item_locations {
126 if item_location.base_offset > 0 {
127 if item_location.base_offset > u32::MAX as u64 {
128 base_offset_size = 8u8;
129 } else if base_offset_size != 8 {
130 base_offset_size = 4u8;
131 }
132 }
133 }
134 let version = IlocVersion::V0;
135 let offset_size = 4u8;
136 let length_size = 4u8;
137
138 let index_size = 0u8;
139 let size0 = (offset_size << 4) | length_size;
140 let size1 = (base_offset_size << 4) | index_size;
141 size0.encode(buf)?;
142 size1.encode(buf)?;
143 if version == IlocVersion::V0 || version == IlocVersion::V1 {
144 (self.item_locations.len() as u16).encode(buf)?;
145 } else {
146 (self.item_locations.len() as u32).encode(buf)?;
147 }
148 for item_location in &self.item_locations {
149 if version == IlocVersion::V0 || version == IlocVersion::V1 {
150 (item_location.item_id as u16).encode(buf)?;
151 } else {
152 item_location.item_id.encode(buf)?;
153 }
154 if version == IlocVersion::V1 || version == IlocVersion::V2 {
155 (item_location.construction_method as u16).encode(buf)?
156 }
157 item_location.data_reference_index.encode(buf)?;
158 match base_offset_size {
159 0 => {}
160 4 => (item_location.base_offset as u32).encode(buf)?,
161 8 => item_location.base_offset.encode(buf)?,
162 _ => unreachable!("iloc base_offset_size must be in [0,4,8]"),
163 }
164 (item_location.extents.len() as u16).encode(buf)?;
165 for extent in &item_location.extents {
166 match index_size {
167 0 => {}
168 4 => (extent.item_reference_index as u32).encode(buf)?,
169 8 => extent.item_reference_index.encode(buf)?,
170 _ => unreachable!("iloc index_size must be in [0,4,8]"),
171 }
172 match offset_size {
173 0 => {}
174 4 => (extent.offset as u32).encode(buf)?,
175 8 => extent.offset.encode(buf)?,
176 _ => unreachable!("iloc offset_size must be in [0,4,8]"),
177 }
178 match length_size {
179 0 => {}
180 4 => (extent.length as u32).encode(buf)?,
181 8 => extent.length.encode(buf)?,
182 _ => unreachable!("iloc length_size must be in [0,4,8]"),
183 }
184 }
185 }
186 Ok(IlocExt {
187 version: IlocVersion::V0,
188 })
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 const ENCODED_ILOC_LIBAVIF: &[u8] = &[
197 0x00, 0x00, 0x00, 0x1e, 0x69, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
198 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x1a,
199 ];
200
201 #[test]
202 fn test_iloc_libavif_decode() {
203 let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_ILOC_LIBAVIF);
204
205 let iloc: Iloc = Iloc {
206 item_locations: vec![ItemLocation {
207 item_id: 1,
208 construction_method: 0,
209 data_reference_index: 0,
210 base_offset: 0,
211 extents: vec![ItemLocationExtent {
212 item_reference_index: 0,
213 offset: 312,
214 length: 26,
215 }],
216 }],
217 };
218 let decoded = Iloc::decode(buf).unwrap();
219 assert_eq!(decoded, iloc);
220 }
221
222 #[test]
227 fn test_iloc_reserved_base_offset_size() {
228 let body: &[u8] = &[0x00, 0xF0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
232 let ext = IlocExt {
233 version: IlocVersion::V0,
234 };
235 assert!(matches!(
236 Iloc::decode_body_ext(&mut std::io::Cursor::new(body), ext),
237 Err(Error::Reserved)
238 ));
239 }
240
241 #[test]
242 fn test_iloc_reserved_index_size() {
243 let body: &[u8] = &[
246 0x00, 0x0F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
254 let ext = IlocExt {
255 version: IlocVersion::V1,
256 };
257 assert!(matches!(
258 Iloc::decode_body_ext(&mut std::io::Cursor::new(body), ext),
259 Err(Error::Reserved)
260 ));
261 }
262
263 #[test]
264 fn test_iloc_reserved_offset_size() {
265 let body: &[u8] = &[
267 0xF0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
273 let ext = IlocExt {
274 version: IlocVersion::V0,
275 };
276 assert!(matches!(
277 Iloc::decode_body_ext(&mut std::io::Cursor::new(body), ext),
278 Err(Error::Reserved)
279 ));
280 }
281
282 #[test]
283 fn test_iloc_reserved_length_size() {
284 let body: &[u8] = &[
287 0x0F, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
293 let ext = IlocExt {
294 version: IlocVersion::V0,
295 };
296 assert!(matches!(
297 Iloc::decode_body_ext(&mut std::io::Cursor::new(body), ext),
298 Err(Error::Reserved)
299 ));
300 }
301
302 #[test]
303 fn test_iloc_avif_encode() {
304 let iloc: Iloc = Iloc {
305 item_locations: vec![ItemLocation {
306 item_id: 1,
307 construction_method: 0,
308 data_reference_index: 0,
309 base_offset: 0,
310 extents: vec![ItemLocationExtent {
311 item_reference_index: 0,
312 offset: 312,
313 length: 26,
314 }],
315 }],
316 };
317 let mut buf = Vec::new();
318 iloc.encode(&mut buf).unwrap();
319
320 assert_eq!(buf.as_slice(), ENCODED_ILOC_LIBAVIF);
321 }
322}