1use crate::*;
2
3pub(crate) const DATA_4CC: FourCC = FourCC::new(b"data");
4
5const TYPE_INDICATOR_RESERVED: u32 = 0u32;
8const TYPE_INDICATOR_UTF8: u32 = 1u32;
9const TYPE_INDICATOR_UTF16: u32 = 2u32;
10const TYPE_INDICATOR_JPEG: u32 = 13u32;
11const TYPE_INDICATOR_PNG: u32 = 14u32;
12const TYPE_INDICATOR_BE_SIGNED_INT: u32 = 21u32;
16const TYPE_INDICATOR_BE_UNSIGNED_INT: u32 = 22u32;
17const TYPE_INDICATOR_BE_FLOAT32: u32 = 23u32;
18const TYPE_INDICATOR_BE_FLOAT64: u32 = 24u32;
19const TYPE_INDICATOR_BMP: u32 = 27u32;
20const TYPE_INDICATOR_SIGNED_INT_FIXED: [u32; 4] = [65, 66, 67, 74];
28const TYPE_INDICATOR_UNSIGNED_INT_FIXED: [u32; 4] = [75, 76, 77, 78];
29const FIXED_INT_WIDTHS: [usize; 4] = [1, 2, 4, 8];
30const TYPE_INDICATOR_BE_SIGNED_INT64: u32 = TYPE_INDICATOR_SIGNED_INT_FIXED[3];
31const TYPE_INDICATOR_BE_UNSIGNED_INT64: u32 = TYPE_INDICATOR_UNSIGNED_INT_FIXED[3];
32
33#[derive(Debug, Clone, PartialEq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub enum IlstDataValue {
44 Reserved(Vec<u8>),
45 Utf8(String),
46 Utf16(String),
47 Jpeg(Vec<u8>),
48 Png(Vec<u8>),
49 Bmp(Vec<u8>),
50 BeSignedInt(i64),
51 BeUnsignedInt(u64),
52 BeFloat32(f32),
53 BeFloat64(f64),
54 Unknown(u32, Vec<u8>),
55}
56
57impl IlstDataValue {
58 fn from_raw(type_indicator: u32, value: &[u8]) -> Self {
59 match type_indicator {
60 TYPE_INDICATOR_RESERVED => IlstDataValue::Reserved(value.to_vec()),
61 TYPE_INDICATOR_UTF8 => match std::str::from_utf8(value) {
62 Ok(text) => IlstDataValue::Utf8(text.to_string()),
63 Err(_) => IlstDataValue::Unknown(type_indicator, value.to_vec()),
64 },
65 TYPE_INDICATOR_UTF16 => match decode_utf16_be(value) {
66 Some(text) => IlstDataValue::Utf16(text),
67 None => IlstDataValue::Unknown(type_indicator, value.to_vec()),
68 },
69 TYPE_INDICATOR_JPEG => IlstDataValue::Jpeg(value.to_vec()),
70 TYPE_INDICATOR_PNG => IlstDataValue::Png(value.to_vec()),
71 TYPE_INDICATOR_BMP => IlstDataValue::Bmp(value.to_vec()),
72 TYPE_INDICATOR_BE_SIGNED_INT if value.len() <= 4 => match decode_be_signed_int(value) {
76 Some(v) => IlstDataValue::BeSignedInt(v),
77 None => IlstDataValue::Unknown(type_indicator, value.to_vec()),
78 },
79 TYPE_INDICATOR_BE_UNSIGNED_INT if value.len() <= 4 => {
80 match decode_be_unsigned_int(value) {
81 Some(v) => IlstDataValue::BeUnsignedInt(v),
82 None => IlstDataValue::Unknown(type_indicator, value.to_vec()),
83 }
84 }
85 t if TYPE_INDICATOR_SIGNED_INT_FIXED.contains(&t) => {
86 let width = FIXED_INT_WIDTHS[TYPE_INDICATOR_SIGNED_INT_FIXED
87 .iter()
88 .position(|&x| x == t)
89 .unwrap()];
90 match (value.len() == width, decode_be_signed_int(value)) {
91 (true, Some(v)) => IlstDataValue::BeSignedInt(v),
92 _ => IlstDataValue::Unknown(type_indicator, value.to_vec()),
93 }
94 }
95 t if TYPE_INDICATOR_UNSIGNED_INT_FIXED.contains(&t) => {
96 let width = FIXED_INT_WIDTHS[TYPE_INDICATOR_UNSIGNED_INT_FIXED
97 .iter()
98 .position(|&x| x == t)
99 .unwrap()];
100 match (value.len() == width, decode_be_unsigned_int(value)) {
101 (true, Some(v)) => IlstDataValue::BeUnsignedInt(v),
102 _ => IlstDataValue::Unknown(type_indicator, value.to_vec()),
103 }
104 }
105 TYPE_INDICATOR_BE_FLOAT32 => match <[u8; 4]>::try_from(value) {
106 Ok(bytes) => IlstDataValue::BeFloat32(f32::from_be_bytes(bytes)),
107 Err(_) => IlstDataValue::Unknown(type_indicator, value.to_vec()),
108 },
109 TYPE_INDICATOR_BE_FLOAT64 => match <[u8; 8]>::try_from(value) {
110 Ok(bytes) => IlstDataValue::BeFloat64(f64::from_be_bytes(bytes)),
111 Err(_) => IlstDataValue::Unknown(type_indicator, value.to_vec()),
112 },
113 _ => IlstDataValue::Unknown(type_indicator, value.to_vec()),
114 }
115 }
116
117 fn to_raw(&self) -> (u32, Vec<u8>) {
118 match self {
119 IlstDataValue::Utf8(text) => (TYPE_INDICATOR_UTF8, text.clone().into_bytes()),
120 IlstDataValue::Utf16(text) => (TYPE_INDICATOR_UTF16, encode_utf16_be(text)),
121 IlstDataValue::Jpeg(bytes) => (TYPE_INDICATOR_JPEG, bytes.clone()),
122 IlstDataValue::Png(bytes) => (TYPE_INDICATOR_PNG, bytes.clone()),
123 IlstDataValue::Bmp(bytes) => (TYPE_INDICATOR_BMP, bytes.clone()),
124 IlstDataValue::BeSignedInt(v) => {
125 let bytes = encode_be_signed_int(*v);
126 let type_indicator = if bytes.len() > 4 {
129 TYPE_INDICATOR_BE_SIGNED_INT64
130 } else {
131 TYPE_INDICATOR_BE_SIGNED_INT
132 };
133 (type_indicator, bytes)
134 }
135 IlstDataValue::BeUnsignedInt(v) => {
136 let bytes = encode_be_unsigned_int(*v);
137 let type_indicator = if bytes.len() > 4 {
138 TYPE_INDICATOR_BE_UNSIGNED_INT64
139 } else {
140 TYPE_INDICATOR_BE_UNSIGNED_INT
141 };
142 (type_indicator, bytes)
143 }
144 IlstDataValue::BeFloat32(v) => (TYPE_INDICATOR_BE_FLOAT32, v.to_be_bytes().to_vec()),
145 IlstDataValue::BeFloat64(v) => (TYPE_INDICATOR_BE_FLOAT64, v.to_be_bytes().to_vec()),
146 IlstDataValue::Reserved(bytes) => (TYPE_INDICATOR_RESERVED, bytes.clone()),
147 IlstDataValue::Unknown(type_indicator, bytes) => (*type_indicator, bytes.clone()),
148 }
149 }
150}
151
152fn decode_be_signed_int(bytes: &[u8]) -> Option<i64> {
155 Some(match bytes.len() {
156 1 => i64::from(bytes[0] as i8),
157 2 => i64::from(i16::from_be_bytes(bytes.try_into().unwrap())),
158 3 => {
159 let mut widened = [0u8; 4];
160 widened[1..].copy_from_slice(bytes);
161 i64::from((i32::from_be_bytes(widened) << 8) >> 8)
162 }
163 4 => i64::from(i32::from_be_bytes(bytes.try_into().unwrap())),
164 8 => i64::from_be_bytes(bytes.try_into().unwrap()),
165 _ => return None,
166 })
167}
168
169fn decode_be_unsigned_int(bytes: &[u8]) -> Option<u64> {
170 Some(match bytes.len() {
171 1 => u64::from(bytes[0]),
172 2 => u64::from(u16::from_be_bytes(bytes.try_into().unwrap())),
173 3 => {
174 let mut widened = [0u8; 4];
175 widened[1..].copy_from_slice(bytes);
176 u64::from(u32::from_be_bytes(widened))
177 }
178 4 => u64::from(u32::from_be_bytes(bytes.try_into().unwrap())),
179 8 => u64::from_be_bytes(bytes.try_into().unwrap()),
180 _ => return None,
181 })
182}
183
184fn encode_be_signed_int(v: i64) -> Vec<u8> {
187 if let Ok(v) = i8::try_from(v) {
188 v.to_be_bytes().to_vec()
189 } else if let Ok(v) = i16::try_from(v) {
190 v.to_be_bytes().to_vec()
191 } else if let Ok(v) = i32::try_from(v) {
192 v.to_be_bytes().to_vec()
193 } else {
194 v.to_be_bytes().to_vec()
195 }
196}
197
198fn encode_be_unsigned_int(v: u64) -> Vec<u8> {
199 if let Ok(v) = u8::try_from(v) {
200 v.to_be_bytes().to_vec()
201 } else if let Ok(v) = u16::try_from(v) {
202 v.to_be_bytes().to_vec()
203 } else if let Ok(v) = u32::try_from(v) {
204 v.to_be_bytes().to_vec()
205 } else {
206 v.to_be_bytes().to_vec()
207 }
208}
209
210fn decode_utf16_be(bytes: &[u8]) -> Option<String> {
211 if !bytes.len().is_multiple_of(2) {
212 return None;
213 }
214 let units: Vec<u16> = bytes
215 .as_chunks::<2>()
216 .0
217 .iter()
218 .map(|c| u16::from_be_bytes([c[0], c[1]]))
219 .collect();
220 String::from_utf16(&units).ok()
221}
222
223fn encode_utf16_be(text: &str) -> Vec<u8> {
224 text.encode_utf16().flat_map(u16::to_be_bytes).collect()
225}
226
227#[derive(Debug, Clone, PartialEq)]
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241pub struct IlstData {
242 pub country_indicator: u16,
243 pub language_indicator: u16,
244 pub value: IlstDataValue,
245}
246
247impl IlstData {
248 pub(crate) fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
249 let type_indicator_or_len = u32::decode(buf)?;
250
251 let type_indicator = match type_indicator_or_len {
252 TYPE_INDICATOR_UTF8 => {
253 type_indicator_or_len
256 }
257 _ => {
258 let fourcc = FourCC::decode(buf)?;
261 if fourcc != DATA_4CC {
262 return Err(Error::UnexpectedBox(fourcc));
263 }
264
265 u32::decode(buf)?
266 }
267 };
268
269 let country_indicator = u16::decode(buf)?;
270 let language_indicator = u16::decode(buf)?;
271
272 let size = buf.remaining();
273 let value = IlstDataValue::from_raw(type_indicator, buf.slice(size));
274 buf.advance(size);
275
276 Ok(Self {
277 country_indicator,
278 language_indicator,
279 value,
280 })
281 }
282
283 pub(crate) fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
284 let (type_indicator, raw) = self.value.to_raw();
285
286 let nested_len = (4 + 4 + 4 + 2 + 2 + raw.len()) as u32;
291 nested_len.encode(buf)?;
292 DATA_4CC.encode(buf)?;
293 type_indicator.encode(buf)?;
294 self.country_indicator.encode(buf)?;
295 self.language_indicator.encode(buf)?;
296 raw.as_slice().encode(buf)?;
297 Ok(())
298 }
299}
300
301pub(crate) struct DataText {
303 pub country_indicator: u16,
304 pub language_indicator: u16,
305 pub text: String,
306}
307
308pub(crate) fn decode_text<B: Buf>(buf: &mut B) -> Result<DataText> {
309 let data = IlstData::decode(buf)?;
310 match data.value {
311 IlstDataValue::Utf8(text) => Ok(DataText {
312 country_indicator: data.country_indicator,
313 language_indicator: data.language_indicator,
314 text,
315 }),
316 _ => Err(Error::Unsupported(
317 "Only UTF-8 text is supported in ilst data atoms",
318 )),
319 }
320}
321
322pub(crate) fn encode_text<B: BufMut>(
323 country_indicator: u16,
324 language_indicator: u16,
325 text: &str,
326 buf: &mut B,
327) -> Result<()> {
328 IlstData {
329 country_indicator,
330 language_indicator,
331 value: IlstDataValue::Utf8(text.to_string()),
332 }
333 .encode(buf)
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 const LONG_STYLE: &[u8] = &[
343 0x00, 0x00, 0x00, 0x1A, b'd', b'a', b't', b'a', 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, b'(', b'c', b')', b' ', b'2', b'0', b'2', b'6', b' ', b'x',
348 ];
349
350 #[test]
351 fn test_decode_text_short_style() {
352 let buf = [
355 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, b'2', b'0', b'2', b'6',
358 ];
359 let decoded = decode_text(&mut &buf[..]).unwrap();
360 assert_eq!(decoded.country_indicator, 0);
361 assert_eq!(decoded.language_indicator, 0);
362 assert_eq!(decoded.text, "2026");
363 }
364
365 #[test]
366 fn test_decode_text_long_style() {
367 let decoded = decode_text(&mut &LONG_STYLE[..]).unwrap();
368 assert_eq!(decoded.country_indicator, 0);
369 assert_eq!(decoded.language_indicator, 0);
370 assert_eq!(decoded.text, "(c) 2026 x");
371 }
372
373 #[test]
374 fn test_encode_text_emits_long_style() {
375 let mut buf = Vec::new();
378 encode_text(0, 0, "(c) 2026 x", &mut buf).unwrap();
379 assert_eq!(buf, LONG_STYLE);
380
381 let decoded = decode_text(&mut buf.as_slice()).unwrap();
382 assert_eq!(decoded.text, "(c) 2026 x");
383 }
384
385 fn data_with(value: IlstDataValue) -> IlstData {
386 IlstData {
387 country_indicator: 0,
388 language_indicator: 0,
389 value,
390 }
391 }
392
393 #[test]
394 fn test_data_roundtrip_non_utf8_type() {
395 let data = data_with(IlstDataValue::BeSignedInt(42));
397
398 let mut buf = Vec::new();
399 data.encode(&mut buf).unwrap();
400
401 let decoded = IlstData::decode(&mut buf.as_slice()).expect("failed to decode data");
402 assert_eq!(decoded, data);
403 }
404
405 #[test]
406 fn test_data_roundtrip_unknown_preserves_exact_bytes() {
407 let data = data_with(IlstDataValue::Unknown(999, vec![1, 2, 3]));
410
411 let mut buf = Vec::new();
412 data.encode(&mut buf).unwrap();
413
414 let decoded = IlstData::decode(&mut buf.as_slice()).expect("failed to decode data");
415 assert_eq!(decoded, data);
416 }
417
418 #[test]
419 fn test_decode_value_utf8() {
420 let mut data_body = Vec::new();
421 data_body.extend_from_slice(&1u32.to_be_bytes());
422 data_body.extend_from_slice(&0u32.to_be_bytes());
423 data_body.extend_from_slice(b"hello");
424
425 let decoded = IlstData::decode(&mut data_body.as_slice()).unwrap();
426 assert_eq!(decoded.value, IlstDataValue::Utf8("hello".into()));
427 }
428
429 #[test]
430 fn test_decode_value_utf16() {
431 let text = "hi \u{263A}"; assert_eq!(
433 decode_raw(2, &encode_utf16_be(text)),
434 IlstDataValue::Utf16(text.into())
435 );
436 }
437
438 #[test]
439 fn test_decode_value_jpeg_png_bmp() {
440 assert_eq!(
441 decode_raw(13, &[0xFF, 0xD8]),
442 IlstDataValue::Jpeg(vec![0xFF, 0xD8])
443 );
444 assert_eq!(
445 decode_raw(14, &[0x89, 0x50]),
446 IlstDataValue::Png(vec![0x89, 0x50])
447 );
448 assert_eq!(
449 decode_raw(27, &[0x42, 0x4D]),
450 IlstDataValue::Bmp(vec![0x42, 0x4D])
451 );
452 }
453
454 fn decode_raw(type_indicator: u32, value: &[u8]) -> IlstDataValue {
457 let mut data_body = Vec::new();
458 let nested_len = (4 + 4 + 4 + 2 + 2 + value.len()) as u32;
459 data_body.extend_from_slice(&nested_len.to_be_bytes());
460 data_body.extend_from_slice(b"data");
461 data_body.extend_from_slice(&type_indicator.to_be_bytes());
462 data_body.extend_from_slice(&0u32.to_be_bytes()); data_body.extend_from_slice(value);
464 IlstData::decode(&mut data_body.as_slice()).unwrap().value
465 }
466
467 #[test]
468 fn test_decode_value_be_signed_int_variable_length() {
469 assert_eq!(decode_raw(21, &[0xFF]), IlstDataValue::BeSignedInt(-1));
470 assert_eq!(
471 decode_raw(21, &1000i16.to_be_bytes()),
472 IlstDataValue::BeSignedInt(1000)
473 );
474 assert_eq!(
475 decode_raw(21, &(-1i32).to_be_bytes()[1..]),
476 IlstDataValue::BeSignedInt(-1)
477 );
478 assert_eq!(
479 decode_raw(21, &(-100000i32).to_be_bytes()),
480 IlstDataValue::BeSignedInt(-100000)
481 );
482 }
483
484 #[test]
485 fn test_decode_value_be_signed_int_rejects_eight_bytes() {
486 assert_eq!(
490 decode_raw(21, &(-1i64).to_be_bytes()),
491 IlstDataValue::Unknown(21, (-1i64).to_be_bytes().to_vec())
492 );
493 }
494
495 #[test]
496 fn test_decode_value_be_unsigned_int_variable_length() {
497 assert_eq!(decode_raw(22, &[200]), IlstDataValue::BeUnsignedInt(200));
498 assert_eq!(
499 decode_raw(22, &40000u32.to_be_bytes()),
500 IlstDataValue::BeUnsignedInt(40000)
501 );
502 }
503
504 #[test]
505 fn test_decode_value_be_unsigned_int_rejects_eight_bytes() {
506 assert_eq!(
507 decode_raw(22, &1u64.to_be_bytes()),
508 IlstDataValue::Unknown(22, 1u64.to_be_bytes().to_vec())
509 );
510 }
511
512 #[test]
513 fn test_encode_be_int_uses_64_bit_fixed_indicator_beyond_four_bytes() {
514 assert_eq!(
518 data_with(IlstDataValue::BeSignedInt(42)).value.to_raw().0,
519 21
520 );
521 assert_eq!(
522 data_with(IlstDataValue::BeSignedInt(i64::from(i32::MAX) + 1))
523 .value
524 .to_raw()
525 .0,
526 74
527 );
528 assert_eq!(
529 data_with(IlstDataValue::BeUnsignedInt(42)).value.to_raw().0,
530 22
531 );
532 assert_eq!(
533 data_with(IlstDataValue::BeUnsignedInt(u64::from(u32::MAX) + 1))
534 .value
535 .to_raw()
536 .0,
537 78
538 );
539 }
540
541 #[test]
542 fn test_data_roundtrip_eight_byte_int() {
543 for value in [
544 IlstDataValue::BeSignedInt(i64::from(i32::MAX) + 1),
545 IlstDataValue::BeUnsignedInt(u64::from(u32::MAX) + 1),
546 ] {
547 let data = data_with(value.clone());
548
549 let mut buf = Vec::new();
550 data.encode(&mut buf).unwrap();
551
552 let decoded = IlstData::decode(&mut buf.as_slice()).expect("failed to decode data");
553 assert_eq!(decoded.value, value);
554 }
555 }
556
557 #[test]
558 fn test_decode_value_fixed_width_ints() {
559 assert_eq!(
562 decode_raw(67, &42i32.to_be_bytes()),
563 IlstDataValue::BeSignedInt(42)
564 );
565 assert_eq!(
566 decode_raw(77, &42u32.to_be_bytes()),
567 IlstDataValue::BeUnsignedInt(42)
568 );
569 }
570
571 #[test]
572 fn test_decode_value_fixed_width_ints_rejects_mismatched_length() {
573 assert_eq!(
577 decode_raw(67, &42i64.to_be_bytes()),
578 IlstDataValue::Unknown(67, 42i64.to_be_bytes().to_vec())
579 );
580 assert_eq!(
581 decode_raw(65, &42i32.to_be_bytes()),
582 IlstDataValue::Unknown(65, 42i32.to_be_bytes().to_vec())
583 );
584 assert_eq!(
585 decode_raw(77, &42u64.to_be_bytes()),
586 IlstDataValue::Unknown(77, 42u64.to_be_bytes().to_vec())
587 );
588 }
589
590 #[test]
591 fn test_decode_value_be_float32_and_float64() {
592 assert_eq!(
593 decode_raw(23, &1.5f32.to_be_bytes()),
594 IlstDataValue::BeFloat32(1.5)
595 );
596 assert_eq!(
597 decode_raw(24, &1.5f64.to_be_bytes()),
598 IlstDataValue::BeFloat64(1.5)
599 );
600 }
601
602 #[test]
603 fn test_decode_value_binary_for_reserved_type() {
604 assert_eq!(
605 decode_raw(0, &[1, 2, 3]),
606 IlstDataValue::Reserved(vec![1, 2, 3])
607 );
608 }
609
610 #[test]
611 fn test_decode_value_unknown_for_unrecognized_or_malformed() {
612 assert_eq!(
614 decode_raw(999, &[1, 2, 3]),
615 IlstDataValue::Unknown(999, vec![1, 2, 3])
616 );
617 assert_eq!(
619 decode_raw(23, &[1, 2, 3]),
620 IlstDataValue::Unknown(23, vec![1, 2, 3])
621 );
622 }
623}