use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const URI_MESSAGE_LEN: usize = 8;
pub const PROTOCOL_VERSION_V3: u8 = 0x03;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum EmiData {
CopyingNotRestricted {
rct: bool,
},
CopyOnce,
OneGenerationCopy {
trick_mode: bool,
},
NoMoreCopies {
dot: bool,
rl: u8,
},
}
impl EmiData {
#[must_use]
pub fn emi(&self) -> u8 {
match self {
Self::CopyingNotRestricted { .. } => 0b00,
Self::CopyOnce => 0b01,
Self::OneGenerationCopy { .. } => 0b10,
Self::NoMoreCopies { .. } => 0b11,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct UriMessage {
pub protocol_version: u8,
pub aps: u8,
pub ict: bool,
pub emi_data: EmiData,
}
const EMI_COPY_NOT_RESTRICTED: u8 = 0b00;
const EMI_COPY_ONCE: u8 = 0b01;
const EMI_ONE_GEN_COPY: u8 = 0b10;
const EMI_NO_MORE_COPIES: u8 = 0b11;
impl<'a> Parse<'a> for UriMessage {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < URI_MESSAGE_LEN {
return Err(Error::BufferTooShort {
need: URI_MESSAGE_LEN,
have: bytes.len(),
what: "uri_message",
});
}
let protocol_version = bytes[0];
let mut acc: u64 = 0;
for &b in &bytes[1..URI_MESSAGE_LEN] {
acc = (acc << 8) | b as u64;
}
let take = |hi_from_top: u32, width: u32| -> u64 {
let shift = 56 - hi_from_top - width;
(acc >> shift) & ((1u64 << width) - 1)
};
let aps = take(0, 2) as u8;
let emi = take(2, 2) as u8;
let ict = take(4, 1) != 0;
let rct = take(5, 1) != 0;
let dot = take(7, 1) != 0;
let rl = take(8, 8) as u8;
let trick_mode = take(16, 1) != 0;
let emi_data = match emi {
EMI_COPY_NOT_RESTRICTED => EmiData::CopyingNotRestricted { rct },
EMI_COPY_ONCE => EmiData::CopyOnce,
EMI_ONE_GEN_COPY => EmiData::OneGenerationCopy { trick_mode },
EMI_NO_MORE_COPIES => EmiData::NoMoreCopies { dot, rl },
_ => unreachable!("emi is 2 bits"),
};
Ok(Self {
protocol_version,
aps,
ict,
emi_data,
})
}
}
impl Serialize for UriMessage {
type Error = Error;
fn serialized_len(&self) -> usize {
URI_MESSAGE_LEN
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
if buf.len() < URI_MESSAGE_LEN {
return Err(Error::OutputBufferTooSmall {
need: URI_MESSAGE_LEN,
have: buf.len(),
});
}
let mut acc: u64 = 0;
let mut put = |value: u64, hi_from_top: u32, width: u32| {
let shift = 56 - hi_from_top - width;
acc |= (value & ((1u64 << width) - 1)) << shift;
};
put(self.aps as u64, 0, 2);
put(self.emi_data.emi() as u64, 2, 2);
put(u64::from(self.ict), 4, 1);
match self.emi_data {
EmiData::CopyingNotRestricted { rct } => put(u64::from(rct), 5, 1),
EmiData::CopyOnce => {}
EmiData::OneGenerationCopy { trick_mode } => put(u64::from(trick_mode), 16, 1),
EmiData::NoMoreCopies { dot, rl } => {
put(u64::from(dot), 7, 1);
put(rl as u64, 8, 8);
}
}
buf[0] = self.protocol_version;
for (i, slot) in buf[1..URI_MESSAGE_LEN].iter_mut().enumerate() {
let shift = 56 - 8 * (i as u32 + 1);
*slot = (acc >> shift) as u8;
}
Ok(URI_MESSAGE_LEN)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_values_round_trip() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b00,
ict: false,
emi_data: EmiData::NoMoreCopies { dot: false, rl: 0 },
};
let bytes = u.to_bytes();
assert_eq!(bytes.len(), 8);
assert_eq!(bytes[0], 0x03);
assert_eq!(bytes[1], 0b0011_0000); assert_eq!(UriMessage::parse(&bytes).unwrap(), u);
}
#[test]
fn one_generation_trick_mode_enabled_bites() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b10,
ict: true,
emi_data: EmiData::OneGenerationCopy { trick_mode: true },
};
let bytes = u.to_bytes();
assert_eq!(bytes[0], 0x03);
assert_eq!(bytes[1], 0b1010_1000);
assert_eq!(bytes[3] & 0x80, 0x80);
let parsed = UriMessage::parse(&bytes).unwrap();
assert_eq!(parsed, u);
let off = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b10,
ict: true,
emi_data: EmiData::OneGenerationCopy { trick_mode: false },
};
assert_ne!(bytes, off.to_bytes());
assert_eq!(off.to_bytes()[3] & 0x80, 0x00);
}
#[test]
fn copying_not_restricted_rct_bit() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b00,
ict: false,
emi_data: EmiData::CopyingNotRestricted { rct: true },
};
let bytes = u.to_bytes();
assert_eq!(bytes[1], 0b0000_0100);
assert_eq!(UriMessage::parse(&bytes).unwrap(), u);
}
#[test]
fn no_more_copies_dot_rl_round_trips() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b01,
ict: true,
emi_data: EmiData::NoMoreCopies {
dot: true,
rl: 0xA5,
},
};
let bytes = u.to_bytes();
let parsed = UriMessage::parse(&bytes).unwrap();
assert_eq!(parsed, u);
let mut other = u;
other.emi_data = EmiData::NoMoreCopies {
dot: true,
rl: 0xA4,
};
assert_ne!(bytes, other.to_bytes());
}
#[test]
fn copy_once_has_no_case_bits() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0b11,
ict: false,
emi_data: EmiData::CopyOnce,
};
let bytes = u.to_bytes();
assert_eq!(bytes[1], 0b1101_0000);
assert_eq!(&bytes[2..8], &[0u8; 6]);
assert_eq!(UriMessage::parse(&bytes).unwrap(), u);
}
#[test]
fn reserved_bits_in_other_emi_cases_dont_leak() {
let u = UriMessage {
protocol_version: PROTOCOL_VERSION_V3,
aps: 0,
ict: false,
emi_data: EmiData::CopyOnce,
};
let bytes = u.to_bytes();
assert_eq!(UriMessage::parse(&bytes).unwrap(), u);
}
#[test]
fn too_short_errors() {
assert!(matches!(
UriMessage::parse(&[0x03, 0x00]),
Err(Error::BufferTooShort { .. })
));
}
}