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 _ => panic!("iloc base_offset_size must be in [0,4,8]"),
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 _ => panic!("iloc index_size must be in [0,4,8]"),
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 _ => panic!("iloc offset_size must be in [0,4,8]"),
98 };
99 let extent_length = match length_size {
100 0 => 0u64,
101 4 => u32::decode(buf)? as u64,
102 8 => u64::decode(buf)?,
103 _ => panic!("iloc length_size must be in [0,4,8]"),
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]
223 fn test_iloc_avif_encode() {
224 let iloc: Iloc = Iloc {
225 item_locations: vec![ItemLocation {
226 item_id: 1,
227 construction_method: 0,
228 data_reference_index: 0,
229 base_offset: 0,
230 extents: vec![ItemLocationExtent {
231 item_reference_index: 0,
232 offset: 312,
233 length: 26,
234 }],
235 }],
236 };
237 let mut buf = Vec::new();
238 iloc.encode(&mut buf).unwrap();
239
240 assert_eq!(buf.as_slice(), ENCODED_ILOC_LIBAVIF);
241 }
242}