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