extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use crate::error::{Error, Result};
pub type UlBytes = [u8; 16];
pub type StrongRef = UlBytes;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rational {
pub numerator: i32,
pub denominator: i32,
}
pub const RATIONAL_LEN: usize = 8;
impl Rational {
pub fn parse(bytes: &[u8]) -> Result<Self> {
if bytes.len() != RATIONAL_LEN {
return Err(Error::InvalidPropertyLength {
tag: 0,
name: "Rational",
found: bytes.len(),
expected: RATIONAL_LEN,
});
}
Ok(Rational {
numerator: i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
denominator: i32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
})
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < RATIONAL_LEN {
return Err(Error::BufferTooShort {
need: RATIONAL_LEN,
have: buf.len(),
what: "Rational",
});
}
buf[0..4].copy_from_slice(&self.numerator.to_be_bytes());
buf[4..8].copy_from_slice(&self.denominator.to_be_bytes());
Ok(RATIONAL_LEN)
}
}
pub(crate) fn ul_bytes_from_prefix(bytes: &[u8]) -> UlBytes {
let mut out = [0u8; 16];
let mut i = 0;
while i < 16 {
out[i] = bytes[i];
i += 1;
}
out
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PackageId(#[cfg_attr(feature = "serde", serde(with = "serde_bytes32"))] pub [u8; 32]);
impl PackageId {
pub const NULL: PackageId = PackageId([0u8; 32]);
#[must_use]
pub fn is_null(&self) -> bool {
self.0 == [0u8; 32]
}
}
#[cfg(feature = "serde")]
mod serde_bytes32 {
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(v: &[u8; 32], s: S) -> core::result::Result<S::Ok, S::Error> {
s.serialize_bytes(v)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
d: D,
) -> core::result::Result<[u8; 32], D::Error> {
let bytes = serde_bytes_vec::deserialize(d)?;
<[u8; 32]>::try_from(bytes.as_slice())
.map_err(|_| serde::de::Error::custom("PackageId must be 32 bytes"))
}
mod serde_bytes_vec {
use alloc::vec::Vec;
use serde::Deserialize;
pub fn deserialize<'de, D: serde::Deserializer<'de>>(
d: D,
) -> core::result::Result<Vec<u8>, D::Error> {
Vec::<u8>::deserialize(d)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Auid(pub UlBytes);
impl Auid {
#[must_use]
pub fn is_ul(&self) -> bool {
self.0[0] & 0x80 == 0
}
#[must_use]
pub fn as_ul_bytes(&self) -> UlBytes {
self.0
}
#[must_use]
pub fn as_uuid_bytes(&self) -> UlBytes {
let mut out = [0u8; 16];
out[..8].copy_from_slice(&self.0[8..]);
out[8..].copy_from_slice(&self.0[..8]);
out
}
#[must_use]
pub fn from_ul(ul: UlBytes) -> Self {
Auid(ul)
}
#[must_use]
pub fn from_uuid(uuid: UlBytes) -> Self {
let mut out = [0u8; 16];
out[..8].copy_from_slice(&uuid[8..]);
out[8..].copy_from_slice(&uuid[..8]);
Auid(out)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MxfTimestamp {
pub year: i16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub msec_div4: u8,
}
pub const TIMESTAMP_LEN: usize = 8;
impl MxfTimestamp {
pub const UNKNOWN: MxfTimestamp = MxfTimestamp {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
msec_div4: 0,
};
pub fn parse(bytes: &[u8]) -> Result<Self> {
if bytes.len() != TIMESTAMP_LEN {
return Err(Error::InvalidPropertyLength {
tag: 0,
name: "Timestamp",
found: bytes.len(),
expected: TIMESTAMP_LEN,
});
}
Ok(MxfTimestamp {
year: i16::from_be_bytes([bytes[0], bytes[1]]),
month: bytes[2],
day: bytes[3],
hour: bytes[4],
minute: bytes[5],
second: bytes[6],
msec_div4: bytes[7],
})
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < TIMESTAMP_LEN {
return Err(Error::BufferTooShort {
need: TIMESTAMP_LEN,
have: buf.len(),
what: "Timestamp",
});
}
let yb = self.year.to_be_bytes();
buf[0] = yb[0];
buf[1] = yb[1];
buf[2] = self.month;
buf[3] = self.day;
buf[4] = self.hour;
buf[5] = self.minute;
buf[6] = self.second;
buf[7] = self.msec_div4;
Ok(TIMESTAMP_LEN)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ReleaseType {
Unknown,
Released,
Development,
ReleasedWithPatches,
PreReleaseBeta,
Private,
Reserved(u16),
}
impl ReleaseType {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Unknown => "unknown version",
Self::Released => "released version",
Self::Development => "development version",
Self::ReleasedWithPatches => "released version with patches",
Self::PreReleaseBeta => "pre-release beta version",
Self::Private => "private version",
Self::Reserved(_) => "reserved",
}
}
#[must_use]
pub fn from_u16(v: u16) -> Self {
match v {
0 => Self::Unknown,
1 => Self::Released,
2 => Self::Development,
3 => Self::ReleasedWithPatches,
4 => Self::PreReleaseBeta,
5 => Self::Private,
other => Self::Reserved(other),
}
}
#[must_use]
pub fn to_u16(self) -> u16 {
match self {
Self::Unknown => 0,
Self::Released => 1,
Self::Development => 2,
Self::ReleasedWithPatches => 3,
Self::PreReleaseBeta => 4,
Self::Private => 5,
Self::Reserved(v) => v,
}
}
}
broadcast_common::impl_spec_display!(ReleaseType, Reserved);
pub const PRODUCT_VERSION_LEN: usize = 10;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ProductVersion {
pub major: u16,
pub minor: u16,
pub tertiary: u16,
pub patch: u16,
pub release: ReleaseType,
}
impl ProductVersion {
pub fn parse(bytes: &[u8]) -> Result<Self> {
if bytes.len() != PRODUCT_VERSION_LEN {
return Err(Error::InvalidPropertyLength {
tag: 0,
name: "ProductVersion",
found: bytes.len(),
expected: PRODUCT_VERSION_LEN,
});
}
let u16_at = |i: usize| u16::from_be_bytes([bytes[i], bytes[i + 1]]);
Ok(ProductVersion {
major: u16_at(0),
minor: u16_at(2),
tertiary: u16_at(4),
patch: u16_at(6),
release: ReleaseType::from_u16(u16_at(8)),
})
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < PRODUCT_VERSION_LEN {
return Err(Error::BufferTooShort {
need: PRODUCT_VERSION_LEN,
have: buf.len(),
what: "ProductVersion",
});
}
let put =
|buf: &mut [u8], i: usize, v: u16| buf[i..i + 2].copy_from_slice(&v.to_be_bytes());
put(buf, 0, self.major);
put(buf, 2, self.minor);
put(buf, 4, self.tertiary);
put(buf, 6, self.patch);
put(buf, 8, self.release.to_u16());
Ok(PRODUCT_VERSION_LEN)
}
}
pub fn decode_utf16_be(bytes: &[u8]) -> Result<String> {
if bytes.len() % 2 != 0 {
return Err(Error::InvalidUtf16 {
tag: 0,
name: "UTF-16 string",
});
}
let units: Vec<u16> = bytes
.chunks_exact(2)
.map(|c| u16::from_be_bytes([c[0], c[1]]))
.collect();
char::decode_utf16(units)
.collect::<core::result::Result<String, _>>()
.map_err(|_| Error::InvalidUtf16 {
tag: 0,
name: "UTF-16 string",
})
}
#[must_use]
pub fn encode_utf16_be(s: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(s.len() * 2);
for unit in s.encode_utf16() {
out.extend_from_slice(&unit.to_be_bytes());
}
out
}
pub fn parse_uid_batch(bytes: &[u8]) -> Result<Vec<UlBytes>> {
if bytes.is_empty() {
return Ok(Vec::new());
}
if bytes.len() < 8 {
return Err(Error::InvalidBatchHeader {
count: 0,
item_len: 0,
buffer_len: bytes.len(),
});
}
let count = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let item_len = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let body = &bytes[8..];
if (count > 0 && item_len != 16) || body.len() != count as usize * 16 {
return Err(Error::InvalidBatchHeader {
count,
item_len,
buffer_len: body.len(),
});
}
let mut out = Vec::with_capacity(count as usize);
for chunk in body.chunks_exact(16) {
out.push(ul_bytes_from_prefix(chunk));
}
Ok(out)
}
#[must_use]
pub fn serialize_uid_batch(items: &[UlBytes]) -> Vec<u8> {
let mut out = Vec::with_capacity(8 + items.len() * 16);
out.extend_from_slice(&(items.len() as u32).to_be_bytes());
out.extend_from_slice(&16u32.to_be_bytes());
for item in items {
out.extend_from_slice(item);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn auid_ul_round_trip() {
let ul: UlBytes = [
0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x0E, 0x04, 0x04, 0x05, 0x03, 0, 0, 0, 0,
];
let auid = Auid::from_ul(ul);
assert!(auid.is_ul());
assert_eq!(auid.as_ul_bytes(), ul);
}
#[test]
fn auid_uuid_round_trip() {
let uuid: UlBytes = [
0x07, 0x72, 0x26, 0x2E, 0x76, 0x55, 0x43, 0x6F, 0x8F, 0xF3, 0x8A, 0xC5, 0x1B, 0x77,
0x1E, 0x02,
];
let auid = Auid::from_uuid(uuid);
assert!(!auid.is_ul());
assert_eq!(
auid.0,
[
0x8F, 0xF3, 0x8A, 0xC5, 0x1B, 0x77, 0x1E, 0x02, 0x07, 0x72, 0x26, 0x2E, 0x76, 0x55,
0x43, 0x6F
]
);
assert_eq!(auid.as_uuid_bytes(), uuid);
}
#[test]
fn timestamp_round_trip() {
let ts = MxfTimestamp {
year: 2019,
month: 11,
day: 28,
hour: 12,
minute: 34,
second: 56,
msec_div4: 10,
};
let mut buf = [0u8; TIMESTAMP_LEN];
ts.serialize_into(&mut buf).unwrap();
assert_eq!(MxfTimestamp::parse(&buf).unwrap(), ts);
}
#[test]
fn product_version_round_trip() {
let pv = ProductVersion {
major: 1,
minor: 2,
tertiary: 3,
patch: 4,
release: ReleaseType::Released,
};
let mut buf = [0u8; PRODUCT_VERSION_LEN];
pv.serialize_into(&mut buf).unwrap();
assert_eq!(ProductVersion::parse(&buf).unwrap(), pv);
}
#[test]
fn reserved_release_type_round_trips_value() {
let pv = ProductVersion {
major: 0,
minor: 0,
tertiary: 0,
patch: 0,
release: ReleaseType::from_u16(42),
};
assert_eq!(pv.release, ReleaseType::Reserved(42));
assert_eq!(pv.release.to_u16(), 42);
assert_eq!(pv.release.to_string(), "reserved(0x2A)");
}
#[test]
fn utf16_round_trip() {
let s = "MXF \u{1F3AC}"; let bytes = encode_utf16_be(s);
assert_eq!(decode_utf16_be(&bytes).unwrap(), s);
}
#[test]
fn rational_round_trip() {
let r = Rational {
numerator: 25,
denominator: 1,
};
let mut buf = [0u8; RATIONAL_LEN];
r.serialize_into(&mut buf).unwrap();
assert_eq!(Rational::parse(&buf).unwrap(), r);
}
#[test]
fn rational_negative_values_round_trip() {
let r = Rational {
numerator: -30000,
denominator: 1001,
};
let mut buf = [0u8; RATIONAL_LEN];
r.serialize_into(&mut buf).unwrap();
assert_eq!(Rational::parse(&buf).unwrap(), r);
}
#[test]
fn uid_batch_round_trip() {
let items = alloc::vec![[1u8; 16], [2u8; 16], [3u8; 16]];
let bytes = serialize_uid_batch(&items);
assert_eq!(parse_uid_batch(&bytes).unwrap(), items);
}
#[test]
fn empty_uid_batch_round_trip() {
let items: Vec<UlBytes> = Vec::new();
let bytes = serialize_uid_batch(&items);
assert_eq!(bytes.len(), 8);
assert_eq!(parse_uid_batch(&bytes).unwrap(), items);
}
}