1extern crate alloc;
5
6use alloc::string::String;
7use alloc::vec::Vec;
8
9use crate::error::{Error, Result};
10
11pub type UlBytes = [u8; 16];
13
14pub type StrongRef = UlBytes;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Rational {
24 pub numerator: i32,
26 pub denominator: i32,
28}
29
30pub const RATIONAL_LEN: usize = 8;
32
33impl Rational {
34 pub fn parse(bytes: &[u8]) -> Result<Self> {
36 if bytes.len() != RATIONAL_LEN {
37 return Err(Error::InvalidPropertyLength {
38 tag: 0,
39 name: "Rational",
40 found: bytes.len(),
41 expected: RATIONAL_LEN,
42 });
43 }
44 Ok(Rational {
45 numerator: i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
46 denominator: i32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
47 })
48 }
49
50 pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
52 if buf.len() < RATIONAL_LEN {
53 return Err(Error::BufferTooShort {
54 need: RATIONAL_LEN,
55 have: buf.len(),
56 what: "Rational",
57 });
58 }
59 buf[0..4].copy_from_slice(&self.numerator.to_be_bytes());
60 buf[4..8].copy_from_slice(&self.denominator.to_be_bytes());
61 Ok(RATIONAL_LEN)
62 }
63}
64
65pub(crate) fn ul_bytes_from_prefix(bytes: &[u8]) -> UlBytes {
74 let mut out = [0u8; 16];
75 let mut i = 0;
76 while i < 16 {
77 out[i] = bytes[i];
78 i += 1;
79 }
80 out
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90pub struct PackageId(#[cfg_attr(feature = "serde", serde(with = "serde_bytes32"))] pub [u8; 32]);
91
92impl PackageId {
93 pub const NULL: PackageId = PackageId([0u8; 32]);
95
96 #[must_use]
98 pub fn is_null(&self) -> bool {
99 self.0 == [0u8; 32]
100 }
101}
102
103#[cfg(feature = "serde")]
104mod serde_bytes32 {
105 use serde::{Deserializer, Serializer};
106
107 pub fn serialize<S: Serializer>(v: &[u8; 32], s: S) -> core::result::Result<S::Ok, S::Error> {
108 s.serialize_bytes(v)
109 }
110
111 pub fn deserialize<'de, D: Deserializer<'de>>(
112 d: D,
113 ) -> core::result::Result<[u8; 32], D::Error> {
114 let bytes = serde_bytes_vec::deserialize(d)?;
115 <[u8; 32]>::try_from(bytes.as_slice())
116 .map_err(|_| serde::de::Error::custom("PackageId must be 32 bytes"))
117 }
118
119 mod serde_bytes_vec {
120 use alloc::vec::Vec;
121 use serde::Deserialize;
122 pub fn deserialize<'de, D: serde::Deserializer<'de>>(
123 d: D,
124 ) -> core::result::Result<Vec<u8>, D::Error> {
125 Vec::<u8>::deserialize(d)
126 }
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct Auid(pub UlBytes);
137
138impl Auid {
139 #[must_use]
141 pub fn is_ul(&self) -> bool {
142 self.0[0] & 0x80 == 0
143 }
144
145 #[must_use]
148 pub fn as_ul_bytes(&self) -> UlBytes {
149 self.0
150 }
151
152 #[must_use]
156 pub fn as_uuid_bytes(&self) -> UlBytes {
157 let mut out = [0u8; 16];
158 out[..8].copy_from_slice(&self.0[8..]);
159 out[8..].copy_from_slice(&self.0[..8]);
160 out
161 }
162
163 #[must_use]
165 pub fn from_ul(ul: UlBytes) -> Self {
166 Auid(ul)
167 }
168
169 #[must_use]
172 pub fn from_uuid(uuid: UlBytes) -> Self {
173 let mut out = [0u8; 16];
174 out[..8].copy_from_slice(&uuid[8..]);
175 out[8..].copy_from_slice(&uuid[..8]);
176 Auid(out)
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184pub struct MxfTimestamp {
185 pub year: i16,
188 pub month: u8,
190 pub day: u8,
192 pub hour: u8,
194 pub minute: u8,
196 pub second: u8,
198 pub msec_div4: u8,
200}
201
202pub const TIMESTAMP_LEN: usize = 8;
204
205impl MxfTimestamp {
206 pub const UNKNOWN: MxfTimestamp = MxfTimestamp {
209 year: 0,
210 month: 0,
211 day: 0,
212 hour: 0,
213 minute: 0,
214 second: 0,
215 msec_div4: 0,
216 };
217
218 pub fn parse(bytes: &[u8]) -> Result<Self> {
220 if bytes.len() != TIMESTAMP_LEN {
221 return Err(Error::InvalidPropertyLength {
222 tag: 0,
223 name: "Timestamp",
224 found: bytes.len(),
225 expected: TIMESTAMP_LEN,
226 });
227 }
228 Ok(MxfTimestamp {
229 year: i16::from_be_bytes([bytes[0], bytes[1]]),
230 month: bytes[2],
231 day: bytes[3],
232 hour: bytes[4],
233 minute: bytes[5],
234 second: bytes[6],
235 msec_div4: bytes[7],
236 })
237 }
238
239 pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
241 if buf.len() < TIMESTAMP_LEN {
242 return Err(Error::BufferTooShort {
243 need: TIMESTAMP_LEN,
244 have: buf.len(),
245 what: "Timestamp",
246 });
247 }
248 let yb = self.year.to_be_bytes();
249 buf[0] = yb[0];
250 buf[1] = yb[1];
251 buf[2] = self.month;
252 buf[3] = self.day;
253 buf[4] = self.hour;
254 buf[5] = self.minute;
255 buf[6] = self.second;
256 buf[7] = self.msec_div4;
257 Ok(TIMESTAMP_LEN)
258 }
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
264#[non_exhaustive]
265pub enum ReleaseType {
266 Unknown,
268 Released,
270 Development,
272 ReleasedWithPatches,
274 PreReleaseBeta,
276 Private,
278 Reserved(u16),
280}
281
282impl ReleaseType {
283 #[must_use]
285 pub fn name(&self) -> &'static str {
286 match self {
287 Self::Unknown => "unknown version",
288 Self::Released => "released version",
289 Self::Development => "development version",
290 Self::ReleasedWithPatches => "released version with patches",
291 Self::PreReleaseBeta => "pre-release beta version",
292 Self::Private => "private version",
293 Self::Reserved(_) => "reserved",
294 }
295 }
296
297 #[must_use]
299 pub fn from_u16(v: u16) -> Self {
300 match v {
301 0 => Self::Unknown,
302 1 => Self::Released,
303 2 => Self::Development,
304 3 => Self::ReleasedWithPatches,
305 4 => Self::PreReleaseBeta,
306 5 => Self::Private,
307 other => Self::Reserved(other),
308 }
309 }
310
311 #[must_use]
313 pub fn to_u16(self) -> u16 {
314 match self {
315 Self::Unknown => 0,
316 Self::Released => 1,
317 Self::Development => 2,
318 Self::ReleasedWithPatches => 3,
319 Self::PreReleaseBeta => 4,
320 Self::Private => 5,
321 Self::Reserved(v) => v,
322 }
323 }
324}
325
326broadcast_common::impl_spec_display!(ReleaseType, Reserved);
327
328pub const PRODUCT_VERSION_LEN: usize = 10;
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
334pub struct ProductVersion {
335 pub major: u16,
337 pub minor: u16,
339 pub tertiary: u16,
341 pub patch: u16,
343 pub release: ReleaseType,
345}
346
347impl ProductVersion {
348 pub fn parse(bytes: &[u8]) -> Result<Self> {
350 if bytes.len() != PRODUCT_VERSION_LEN {
351 return Err(Error::InvalidPropertyLength {
352 tag: 0,
353 name: "ProductVersion",
354 found: bytes.len(),
355 expected: PRODUCT_VERSION_LEN,
356 });
357 }
358 let u16_at = |i: usize| u16::from_be_bytes([bytes[i], bytes[i + 1]]);
359 Ok(ProductVersion {
360 major: u16_at(0),
361 minor: u16_at(2),
362 tertiary: u16_at(4),
363 patch: u16_at(6),
364 release: ReleaseType::from_u16(u16_at(8)),
365 })
366 }
367
368 pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
370 if buf.len() < PRODUCT_VERSION_LEN {
371 return Err(Error::BufferTooShort {
372 need: PRODUCT_VERSION_LEN,
373 have: buf.len(),
374 what: "ProductVersion",
375 });
376 }
377 let put =
378 |buf: &mut [u8], i: usize, v: u16| buf[i..i + 2].copy_from_slice(&v.to_be_bytes());
379 put(buf, 0, self.major);
380 put(buf, 2, self.minor);
381 put(buf, 4, self.tertiary);
382 put(buf, 6, self.patch);
383 put(buf, 8, self.release.to_u16());
384 Ok(PRODUCT_VERSION_LEN)
385 }
386}
387
388pub fn decode_utf16_be(bytes: &[u8]) -> Result<String> {
390 if !bytes.len().is_multiple_of(2) {
391 return Err(Error::InvalidUtf16 {
392 tag: 0,
393 name: "UTF-16 string",
394 });
395 }
396 let units: Vec<u16> = bytes
397 .chunks_exact(2)
398 .map(|c| u16::from_be_bytes([c[0], c[1]]))
399 .collect();
400 char::decode_utf16(units)
401 .collect::<core::result::Result<String, _>>()
402 .map_err(|_| Error::InvalidUtf16 {
403 tag: 0,
404 name: "UTF-16 string",
405 })
406}
407
408#[must_use]
410pub fn encode_utf16_be(s: &str) -> Vec<u8> {
411 let mut out = Vec::with_capacity(s.len() * 2);
412 for unit in s.encode_utf16() {
413 out.extend_from_slice(&unit.to_be_bytes());
414 }
415 out
416}
417
418pub fn parse_uid_batch(bytes: &[u8]) -> Result<Vec<UlBytes>> {
424 if bytes.is_empty() {
425 return Ok(Vec::new());
430 }
431 if bytes.len() < 8 {
432 return Err(Error::InvalidBatchHeader {
433 count: 0,
434 item_len: 0,
435 buffer_len: bytes.len(),
436 });
437 }
438 let count = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
439 let item_len = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
440 let body = &bytes[8..];
441 if (count > 0 && item_len != 16) || body.len() != count as usize * 16 {
449 return Err(Error::InvalidBatchHeader {
450 count,
451 item_len,
452 buffer_len: body.len(),
453 });
454 }
455 let mut out = Vec::with_capacity(count as usize);
456 for chunk in body.chunks_exact(16) {
457 out.push(ul_bytes_from_prefix(chunk));
458 }
459 Ok(out)
460}
461
462#[must_use]
471pub fn serialize_uid_batch(items: &[UlBytes]) -> Vec<u8> {
472 let mut out = Vec::with_capacity(8 + items.len() * 16);
473 out.extend_from_slice(&(items.len() as u32).to_be_bytes());
474 out.extend_from_slice(&16u32.to_be_bytes());
475 for item in items {
476 out.extend_from_slice(item);
477 }
478 out
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484 use alloc::string::ToString;
485
486 #[test]
487 fn auid_ul_round_trip() {
488 let ul: UlBytes = [
489 0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x0E, 0x04, 0x04, 0x05, 0x03, 0, 0, 0, 0,
490 ];
491 let auid = Auid::from_ul(ul);
492 assert!(auid.is_ul());
493 assert_eq!(auid.as_ul_bytes(), ul);
494 }
495
496 #[test]
497 fn auid_uuid_round_trip() {
498 let uuid: UlBytes = [
499 0x07, 0x72, 0x26, 0x2E, 0x76, 0x55, 0x43, 0x6F, 0x8F, 0xF3, 0x8A, 0xC5, 0x1B, 0x77,
500 0x1E, 0x02,
501 ];
502 let auid = Auid::from_uuid(uuid);
503 assert!(!auid.is_ul());
504 assert_eq!(
507 auid.0,
508 [
509 0x8F, 0xF3, 0x8A, 0xC5, 0x1B, 0x77, 0x1E, 0x02, 0x07, 0x72, 0x26, 0x2E, 0x76, 0x55,
510 0x43, 0x6F
511 ]
512 );
513 assert_eq!(auid.as_uuid_bytes(), uuid);
514 }
515
516 #[test]
517 fn timestamp_round_trip() {
518 let ts = MxfTimestamp {
519 year: 2019,
520 month: 11,
521 day: 28,
522 hour: 12,
523 minute: 34,
524 second: 56,
525 msec_div4: 10,
526 };
527 let mut buf = [0u8; TIMESTAMP_LEN];
528 ts.serialize_into(&mut buf).unwrap();
529 assert_eq!(MxfTimestamp::parse(&buf).unwrap(), ts);
530 }
531
532 #[test]
533 fn product_version_round_trip() {
534 let pv = ProductVersion {
535 major: 1,
536 minor: 2,
537 tertiary: 3,
538 patch: 4,
539 release: ReleaseType::Released,
540 };
541 let mut buf = [0u8; PRODUCT_VERSION_LEN];
542 pv.serialize_into(&mut buf).unwrap();
543 assert_eq!(ProductVersion::parse(&buf).unwrap(), pv);
544 }
545
546 #[test]
547 fn reserved_release_type_round_trips_value() {
548 let pv = ProductVersion {
549 major: 0,
550 minor: 0,
551 tertiary: 0,
552 patch: 0,
553 release: ReleaseType::from_u16(42),
554 };
555 assert_eq!(pv.release, ReleaseType::Reserved(42));
556 assert_eq!(pv.release.to_u16(), 42);
557 assert_eq!(pv.release.to_string(), "reserved(0x2A)");
558 }
559
560 #[test]
561 fn utf16_round_trip() {
562 let s = "MXF \u{1F3AC}"; let bytes = encode_utf16_be(s);
564 assert_eq!(decode_utf16_be(&bytes).unwrap(), s);
565 }
566
567 #[test]
568 fn rational_round_trip() {
569 let r = Rational {
570 numerator: 25,
571 denominator: 1,
572 };
573 let mut buf = [0u8; RATIONAL_LEN];
574 r.serialize_into(&mut buf).unwrap();
575 assert_eq!(Rational::parse(&buf).unwrap(), r);
576 }
577
578 #[test]
579 fn rational_negative_values_round_trip() {
580 let r = Rational {
581 numerator: -30000,
582 denominator: 1001,
583 };
584 let mut buf = [0u8; RATIONAL_LEN];
585 r.serialize_into(&mut buf).unwrap();
586 assert_eq!(Rational::parse(&buf).unwrap(), r);
587 }
588
589 #[test]
590 fn uid_batch_round_trip() {
591 let items = alloc::vec![[1u8; 16], [2u8; 16], [3u8; 16]];
592 let bytes = serialize_uid_batch(&items);
593 assert_eq!(parse_uid_batch(&bytes).unwrap(), items);
594 }
595
596 #[test]
597 fn empty_uid_batch_round_trip() {
598 let items: Vec<UlBytes> = Vec::new();
599 let bytes = serialize_uid_batch(&items);
600 assert_eq!(bytes.len(), 8);
601 assert_eq!(parse_uid_batch(&bytes).unwrap(), items);
602 }
603}