1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Default)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Iprp {
9 pub ipco: Ipco,
10 pub ipma: Vec<Ipma>,
11}
12
13impl Atom for Iprp {
14 const KIND: FourCC = FourCC::new(b"iprp");
15
16 nested! {
17 required: [ Ipco ],
18 optional: [ ],
19 multiple: [ Ipma ],
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Default)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Ipco {
26 pub properties: Vec<crate::Any>,
28}
29
30impl Atom for Ipco {
31 const KIND: FourCC = FourCC::new(b"ipco");
32
33 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
34 let mut props = vec![];
35 while let Some(prop) = crate::Any::decode_maybe(buf)? {
36 props.push(prop);
37 }
38 Ok(Self { properties: props })
39 }
40
41 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
42 for property in &self.properties {
43 property.encode(buf)?
44 }
45 Ok(())
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Default)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51pub struct PropertyAssociation {
52 pub essential: bool,
53 pub property_index: u16,
54}
55
56impl PropertyAssociation {
57 fn encode<B: BufMut>(&self, buf: &mut B, prop_index_15_bit: bool) -> Result<()> {
58 if prop_index_15_bit {
59 if self.property_index > 0x7fff {
60 return Err(Error::TooLarge(Ipma::KIND));
61 }
62 let flag_and_prop_index = if self.essential {
63 0x8000 | self.property_index
64 } else {
65 self.property_index
66 };
67 flag_and_prop_index.encode(buf)
68 } else {
69 let flag_and_prop_index = if self.essential {
70 0x80 | (self.property_index as u8)
71 } else {
72 self.property_index as u8
73 };
74 flag_and_prop_index.encode(buf)
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Default)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct PropertyAssociations {
82 pub item_id: u32,
83 pub associations: Vec<PropertyAssociation>,
84}
85
86impl PropertyAssociations {
87 fn encode<B: BufMut>(
88 &self,
89 buf: &mut B,
90 version: IpmaVersion,
91 prop_index_15_bit: bool,
92 ) -> Result<()> {
93 if version == IpmaVersion::V0 {
94 (self.item_id as u16).encode(buf)?;
95 } else {
96 self.item_id.encode(buf)?;
97 }
98 let association_count: u8 = self
99 .associations
100 .len()
101 .try_into()
102 .map_err(|_| Error::TooLarge(Ipma::KIND))?;
103 association_count.encode(buf)?;
104 for association in &self.associations {
105 association.encode(buf, prop_index_15_bit)?;
106 }
107 Ok(())
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Default)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113pub struct Ipma {
114 pub item_properties: Vec<PropertyAssociations>,
115}
116
117ext! {
118 name: Ipma,
119 versions: [0, 1],
120 flags: {
121 prop_index_15_bits = 0,
122 }
123}
124
125impl AtomExt for Ipma {
126 type Ext = IpmaExt;
127
128 const KIND_EXT: FourCC = FourCC::new(b"ipma");
129
130 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
131 let mut version = IpmaVersion::V0;
132 let mut prop_index_15_bit = false;
133 for item_property in &self.item_properties {
134 if item_property.item_id > (u16::MAX as u32) {
135 version = IpmaVersion::V1;
136 }
137 for association in &item_property.associations {
138 if association.property_index > 0x7f {
139 prop_index_15_bit = true;
140 }
141 }
142 }
143 let entry_count: u32 = self.item_properties.len() as u32;
144 entry_count.encode(buf)?;
145 for item_property in &self.item_properties {
146 item_property.encode(buf, version, prop_index_15_bit)?;
147 }
148 Ok(IpmaExt {
149 version,
150 prop_index_15_bits: prop_index_15_bit,
151 })
152 }
153
154 fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
155 let entry_count = u32::decode(buf)?;
156 let mut item_properties = vec![];
157 for _i in 0..entry_count {
158 let item_id: u32 = if ext.version == IpmaVersion::V0 {
159 u16::decode(buf)? as u32
160 } else {
161 u32::decode(buf)?
162 };
163 let mut associations = vec![];
164 let association_count = u8::decode(buf)?;
165 for _j in 0..association_count {
168 if ext.prop_index_15_bits {
169 let flag_and_prop_index = u16::decode(buf)?;
170 let essential = (flag_and_prop_index & 0x8000) == 0x8000;
171 let property_index = flag_and_prop_index & 0x7fff;
172 associations.push(PropertyAssociation {
173 essential,
174 property_index,
175 });
176 } else {
177 let flag_and_prop_index = u8::decode(buf)?;
178 let essential = (flag_and_prop_index & 0x80) == 0x80;
179 let property_index = (flag_and_prop_index & 0x7f) as u16;
180 associations.push(PropertyAssociation {
181 essential,
182 property_index,
183 });
184 }
185 }
186 item_properties.push(PropertyAssociations {
187 item_id,
188 associations,
189 });
190 }
191 Ok(Self { item_properties })
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 const IPMA_WITH_15_BIT_INDEX: &[u8] = &[
200 0x00, 0x00, 0x00, 0x15, b'i', b'p', b'm', b'a', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
201 0x01, 0x00, 0x01, 0x01, 0x80, 0x80,
202 ];
203
204 fn ipma(property_index: u16, association_count: usize) -> Ipma {
205 Ipma {
206 item_properties: vec![PropertyAssociations {
207 item_id: 1,
208 associations: vec![
209 PropertyAssociation {
210 essential: true,
211 property_index,
212 };
213 association_count
214 ],
215 }],
216 }
217 }
218
219 #[test]
220 fn decode_15_bit_property_index() {
221 let decoded = Ipma::decode(&mut &IPMA_WITH_15_BIT_INDEX[..]).unwrap();
222
223 assert_eq!(decoded, ipma(0x80, 1));
224 }
225
226 #[test]
227 fn encode_15_bit_property_index() {
228 let mut encoded = Vec::new();
229 ipma(0x80, 1).encode(&mut encoded).unwrap();
230
231 assert_eq!(encoded, IPMA_WITH_15_BIT_INDEX);
232 }
233
234 #[test]
235 fn reject_property_index_above_15_bits() {
236 let result = ipma(0x8000, 1).encode(&mut Vec::new());
237
238 assert!(matches!(result, Err(Error::TooLarge(Ipma::KIND))));
239 }
240
241 #[test]
242 fn reject_more_than_255_associations() {
243 let result = ipma(1, 256).encode(&mut Vec::new());
244
245 assert!(matches!(result, Err(Error::TooLarge(Ipma::KIND))));
246 }
247}