use super::{
ior::{Ior, NameComponent},
BIOP_MAGIC, BIOP_VERSION_MAJOR, BIOP_VERSION_MINOR, BYTE_ORDER_BIG_ENDIAN,
COMPRESSED_MODULE_DESCRIPTOR_TAG,
};
use crate::error::{Error, Result};
use dvb_common::{Parse, Serialize};
const BIOP_HEADER_LEN: usize = 12;
const OBJECT_KEY_LEN_FIELD: usize = 1;
const OBJECT_KIND_LEN_FIELD: usize = 4;
const OBJECT_KIND_DATA_LEN: usize = 4;
const OBJECT_INFO_LEN_FIELD: usize = 2;
const SERVICE_CONTEXT_COUNT_FIELD: usize = 1;
const SERVICE_CONTEXT_FIXED: usize = 6;
const MESSAGE_BODY_LEN_FIELD: usize = 4;
const BINDINGS_COUNT_FIELD: usize = 2;
const BINDING_NAME_COUNT_FIELD: usize = 1;
const BINDING_TYPE_FIELD: usize = 1;
const BINDING_OBJ_INFO_LEN_FIELD: usize = 2;
const FILE_CONTENT_LEN_FIELD: usize = 4;
const FILE_CONTENT_SIZE_LEN: usize = 8;
const MODULE_INFO_FIXED: usize = 12;
const MODULE_TAPS_COUNT_FIELD: usize = 1;
const MODULE_USER_INFO_LEN_FIELD: usize = 1;
const SGI_DOWNLOAD_TAPS_COUNT_FIELD: usize = 1;
const SGI_USER_INFO_LEN_FIELD: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Binding<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: Vec<NameComponent<'a>>,
pub binding_type: u8,
pub ior: Ior<'a>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_info: &'a [u8],
}
impl<'a> Binding<'a> {
fn parse_from(bytes: &'a [u8], pos: usize, end: usize) -> Result<(Self, usize)> {
if pos + BINDING_NAME_COUNT_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + BINDING_NAME_COUNT_FIELD,
have: end,
what: "Binding nameComponents_count",
});
}
let name_count = bytes[pos] as usize;
let mut cur = pos + BINDING_NAME_COUNT_FIELD;
let mut name = Vec::with_capacity(name_count.min(4));
for _ in 0..name_count {
let (nc, next) = NameComponent::parse_8bit(bytes, cur, end)?;
name.push(nc);
cur = next;
}
if cur + BINDING_TYPE_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + BINDING_TYPE_FIELD,
have: end,
what: "Binding bindingType",
});
}
let binding_type = bytes[cur];
cur += BINDING_TYPE_FIELD;
let ior_slice = &bytes[cur..end];
let ior = Ior::parse(ior_slice)?;
let ior_len = ior.serialized_len();
cur += ior_len;
if cur + BINDING_OBJ_INFO_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + BINDING_OBJ_INFO_LEN_FIELD,
have: end,
what: "Binding objectInfo_length",
});
}
let obj_info_len = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]) as usize;
cur += BINDING_OBJ_INFO_LEN_FIELD;
if cur + obj_info_len > end {
return Err(Error::SectionLengthOverflow {
declared: obj_info_len,
available: end - cur,
});
}
let object_info = &bytes[cur..cur + obj_info_len];
cur += obj_info_len;
Ok((
Binding {
name,
binding_type,
ior,
object_info,
},
cur,
))
}
fn serialized_len(&self) -> usize {
let name_len: usize = self.name.iter().map(|n| n.serialized_len_8bit()).sum();
BINDING_NAME_COUNT_FIELD
+ name_len
+ BINDING_TYPE_FIELD
+ self.ior.serialized_len()
+ BINDING_OBJ_INFO_LEN_FIELD
+ self.object_info.len()
}
fn serialize_into_buf(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
if self.name.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.name.len(),
available: u8::MAX as usize,
});
}
buf[0] = self.name.len() as u8;
let mut pos = BINDING_NAME_COUNT_FIELD;
for nc in &self.name {
let written = nc.serialize_8bit(&mut buf[pos..])?;
pos += written;
}
buf[pos] = self.binding_type;
pos += BINDING_TYPE_FIELD;
let written = self.ior.serialize_into(&mut buf[pos..])?;
pos += written;
if self.object_info.len() > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.object_info.len(),
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(self.object_info.len() as u16).to_be_bytes());
pos += BINDING_OBJ_INFO_LEN_FIELD;
buf[pos..pos + self.object_info.len()].copy_from_slice(self.object_info);
pos += self.object_info.len();
Ok(pos)
}
}
fn parse_biop_header(bytes: &[u8]) -> Result<(&[u8], [u8; 4], usize, usize)> {
let total = bytes.len();
if total < BIOP_HEADER_LEN {
return Err(Error::BufferTooShort {
need: BIOP_HEADER_LEN,
have: total,
what: "BIOP message header",
});
}
let magic = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
if magic != BIOP_MAGIC {
return Err(Error::ReservedBitsViolation {
field: "BIOP magic",
reason: "must be 0x42494F50 (\"BIOP\")",
});
}
if bytes[4] != BIOP_VERSION_MAJOR || bytes[5] != BIOP_VERSION_MINOR {
return Err(Error::ReservedBitsViolation {
field: "biop_version",
reason: "must be 1.0",
});
}
if bytes[6] != BYTE_ORDER_BIG_ENDIAN {
return Err(Error::ReservedBitsViolation {
field: "byte_order",
reason: "must be 0x00 (big-endian) per DVB mandatory constraint",
});
}
let message_size = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
let end = BIOP_HEADER_LEN + message_size;
if total < end {
return Err(Error::SectionLengthOverflow {
declared: message_size,
available: total - BIOP_HEADER_LEN,
});
}
let mut pos = BIOP_HEADER_LEN;
if pos + OBJECT_KEY_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + OBJECT_KEY_LEN_FIELD,
have: end,
what: "BIOP objectKey_length",
});
}
let obj_key_len = bytes[pos] as usize;
pos += OBJECT_KEY_LEN_FIELD;
if pos + obj_key_len > end {
return Err(Error::SectionLengthOverflow {
declared: obj_key_len,
available: end - pos,
});
}
let object_key = &bytes[pos..pos + obj_key_len];
pos += obj_key_len;
if pos + OBJECT_KIND_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + OBJECT_KIND_LEN_FIELD,
have: end,
what: "BIOP objectKind_length",
});
}
let kind_len =
u32::from_be_bytes([bytes[pos], bytes[pos + 1], bytes[pos + 2], bytes[pos + 3]]) as usize;
pos += OBJECT_KIND_LEN_FIELD;
if kind_len != OBJECT_KIND_DATA_LEN {
return Err(Error::ValueOutOfRange {
field: "objectKind_length",
reason: "DVB BIOP objectKind must be exactly 4 bytes",
});
}
if pos + OBJECT_KIND_DATA_LEN > end {
return Err(Error::SectionLengthOverflow {
declared: OBJECT_KIND_DATA_LEN,
available: end - pos,
});
}
let mut kind_bytes = [0u8; 4];
kind_bytes.copy_from_slice(&bytes[pos..pos + 4]);
pos += OBJECT_KIND_DATA_LEN;
Ok((object_key, kind_bytes, message_size, pos))
}
fn parse_service_context_list(bytes: &[u8], pos: usize, end: usize) -> Result<(&[u8], usize)> {
if pos + SERVICE_CONTEXT_COUNT_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + SERVICE_CONTEXT_COUNT_FIELD,
have: end,
what: "serviceContextList_count",
});
}
let count = bytes[pos] as usize;
let list_start = pos;
let mut cur = pos + SERVICE_CONTEXT_COUNT_FIELD;
for _ in 0..count {
if cur + SERVICE_CONTEXT_FIXED > end {
return Err(Error::BufferTooShort {
need: cur + SERVICE_CONTEXT_FIXED,
have: end,
what: "serviceContext entry",
});
}
let ctx_data_len = u16::from_be_bytes([bytes[cur + 4], bytes[cur + 5]]) as usize;
cur += SERVICE_CONTEXT_FIXED;
if cur + ctx_data_len > end {
return Err(Error::SectionLengthOverflow {
declared: ctx_data_len,
available: end - cur,
});
}
cur += ctx_data_len;
}
Ok((&bytes[list_start..cur], cur))
}
fn write_biop_header(buf: &mut [u8], message_size: u32) {
buf[0..4].copy_from_slice(&BIOP_MAGIC.to_be_bytes());
buf[4] = BIOP_VERSION_MAJOR;
buf[5] = BIOP_VERSION_MINOR;
buf[6] = BYTE_ORDER_BIG_ENDIAN;
buf[7] = 0x00; buf[8..12].copy_from_slice(&message_size.to_be_bytes());
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DirectoryMessage<'a> {
pub object_kind: [u8; 4],
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_key: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_info: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_context: &'a [u8],
pub bindings: Vec<Binding<'a>>,
}
impl<'a> DirectoryMessage<'a> {
pub fn is_service_gateway(&self) -> bool {
&self.object_kind == b"srg\0"
}
fn parse_from(
bytes: &'a [u8],
object_key: &'a [u8],
object_kind: [u8; 4],
pos: usize,
end: usize,
) -> Result<Self> {
let mut cur = pos;
if cur + OBJECT_INFO_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + OBJECT_INFO_LEN_FIELD,
have: end,
what: "DirectoryMessage objectInfo_length",
});
}
let obj_info_len = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]) as usize;
cur += OBJECT_INFO_LEN_FIELD;
if cur + obj_info_len > end {
return Err(Error::SectionLengthOverflow {
declared: obj_info_len,
available: end - cur,
});
}
let object_info = &bytes[cur..cur + obj_info_len];
cur += obj_info_len;
let (service_context, next) = parse_service_context_list(bytes, cur, end)?;
cur = next;
if cur + MESSAGE_BODY_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + MESSAGE_BODY_LEN_FIELD,
have: end,
what: "DirectoryMessage messageBody_length",
});
}
let body_len =
u32::from_be_bytes([bytes[cur], bytes[cur + 1], bytes[cur + 2], bytes[cur + 3]])
as usize;
cur += MESSAGE_BODY_LEN_FIELD;
let body_end = cur + body_len;
if body_end > end {
return Err(Error::SectionLengthOverflow {
declared: body_len,
available: end - cur,
});
}
if cur + BINDINGS_COUNT_FIELD > body_end {
return Err(Error::BufferTooShort {
need: cur + BINDINGS_COUNT_FIELD,
have: body_end,
what: "DirectoryMessage bindings_count",
});
}
let bindings_count = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]) as usize;
cur += BINDINGS_COUNT_FIELD;
let mut bindings = Vec::with_capacity(bindings_count.min(256));
for _ in 0..bindings_count {
let (binding, next) = Binding::parse_from(bytes, cur, body_end)?;
bindings.push(binding);
cur = next;
}
Ok(DirectoryMessage {
object_kind,
object_key,
object_info,
service_context,
bindings,
})
}
fn body_len(&self) -> usize {
let bindings_len: usize = self.bindings.iter().map(|b| b.serialized_len()).sum();
BINDINGS_COUNT_FIELD + bindings_len
}
fn serialized_len_inner(&self) -> usize {
let key_part = OBJECT_KEY_LEN_FIELD
+ self.object_key.len()
+ OBJECT_KIND_LEN_FIELD
+ OBJECT_KIND_DATA_LEN;
let info_part = OBJECT_INFO_LEN_FIELD + self.object_info.len();
let svc_ctx_part = self.service_context.len();
let body_part = MESSAGE_BODY_LEN_FIELD + self.body_len();
key_part + info_part + svc_ctx_part + body_part
}
pub fn serialized_len_total(&self) -> usize {
BIOP_HEADER_LEN + self.serialized_len_inner()
}
fn serialize_into_buf(&self, buf: &mut [u8]) -> Result<usize> {
let inner_len = self.serialized_len_inner();
let total = BIOP_HEADER_LEN + inner_len;
if buf.len() < total {
return Err(Error::OutputBufferTooSmall {
need: total,
have: buf.len(),
});
}
if inner_len > u32::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: inner_len,
available: u32::MAX as usize,
});
}
write_biop_header(buf, inner_len as u32);
let mut pos = BIOP_HEADER_LEN;
if self.object_key.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.object_key.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.object_key.len() as u8;
pos += OBJECT_KEY_LEN_FIELD;
buf[pos..pos + self.object_key.len()].copy_from_slice(self.object_key);
pos += self.object_key.len();
buf[pos..pos + 4].copy_from_slice(&(OBJECT_KIND_DATA_LEN as u32).to_be_bytes());
pos += OBJECT_KIND_LEN_FIELD;
buf[pos..pos + 4].copy_from_slice(&self.object_kind);
pos += OBJECT_KIND_DATA_LEN;
if self.object_info.len() > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.object_info.len(),
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(self.object_info.len() as u16).to_be_bytes());
pos += OBJECT_INFO_LEN_FIELD;
buf[pos..pos + self.object_info.len()].copy_from_slice(self.object_info);
pos += self.object_info.len();
buf[pos..pos + self.service_context.len()].copy_from_slice(self.service_context);
pos += self.service_context.len();
let body_len = self.body_len();
if body_len > u32::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: body_len,
available: u32::MAX as usize,
});
}
buf[pos..pos + 4].copy_from_slice(&(body_len as u32).to_be_bytes());
pos += MESSAGE_BODY_LEN_FIELD;
if self.bindings.len() > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.bindings.len(),
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(self.bindings.len() as u16).to_be_bytes());
pos += BINDINGS_COUNT_FIELD;
for binding in &self.bindings {
let written = binding.serialize_into_buf(&mut buf[pos..])?;
pos += written;
}
Ok(total)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct FileMessage<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_key: &'a [u8],
pub content_size: u64,
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_info_extra: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_context: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub content: &'a [u8],
}
impl<'a> FileMessage<'a> {
fn parse_from(bytes: &'a [u8], object_key: &'a [u8], pos: usize, end: usize) -> Result<Self> {
let mut cur = pos;
if cur + OBJECT_INFO_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + OBJECT_INFO_LEN_FIELD,
have: end,
what: "FileMessage objectInfo_length",
});
}
let obj_info_len = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]) as usize;
cur += OBJECT_INFO_LEN_FIELD;
if obj_info_len < FILE_CONTENT_SIZE_LEN {
return Err(Error::ValueOutOfRange {
field: "FileMessage.objectInfo_length",
reason: "FileMessage objectInfo must be at least 8 bytes (ContentSize)",
});
}
if cur + obj_info_len > end {
return Err(Error::SectionLengthOverflow {
declared: obj_info_len,
available: end - cur,
});
}
let content_size = u64::from_be_bytes([
bytes[cur],
bytes[cur + 1],
bytes[cur + 2],
bytes[cur + 3],
bytes[cur + 4],
bytes[cur + 5],
bytes[cur + 6],
bytes[cur + 7],
]);
let object_info_extra = &bytes[cur + FILE_CONTENT_SIZE_LEN..cur + obj_info_len];
cur += obj_info_len;
let (service_context, next) = parse_service_context_list(bytes, cur, end)?;
cur = next;
if cur + MESSAGE_BODY_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: cur + MESSAGE_BODY_LEN_FIELD,
have: end,
what: "FileMessage messageBody_length",
});
}
let body_len =
u32::from_be_bytes([bytes[cur], bytes[cur + 1], bytes[cur + 2], bytes[cur + 3]])
as usize;
cur += MESSAGE_BODY_LEN_FIELD;
let body_end = cur + body_len;
if body_end > end {
return Err(Error::SectionLengthOverflow {
declared: body_len,
available: end - cur,
});
}
if cur + FILE_CONTENT_LEN_FIELD > body_end {
return Err(Error::BufferTooShort {
need: cur + FILE_CONTENT_LEN_FIELD,
have: body_end,
what: "FileMessage content_length",
});
}
let content_len =
u32::from_be_bytes([bytes[cur], bytes[cur + 1], bytes[cur + 2], bytes[cur + 3]])
as usize;
cur += FILE_CONTENT_LEN_FIELD;
if cur + content_len > body_end {
return Err(Error::SectionLengthOverflow {
declared: content_len,
available: body_end - cur,
});
}
let content = &bytes[cur..cur + content_len];
Ok(FileMessage {
object_key,
content_size,
object_info_extra,
service_context,
content,
})
}
fn serialized_len_inner(&self) -> usize {
let obj_info_total = FILE_CONTENT_SIZE_LEN + self.object_info_extra.len();
OBJECT_KEY_LEN_FIELD
+ self.object_key.len()
+ OBJECT_KIND_LEN_FIELD
+ OBJECT_KIND_DATA_LEN
+ OBJECT_INFO_LEN_FIELD
+ obj_info_total
+ self.service_context.len()
+ MESSAGE_BODY_LEN_FIELD
+ FILE_CONTENT_LEN_FIELD
+ self.content.len()
}
pub fn serialized_len_total(&self) -> usize {
BIOP_HEADER_LEN + self.serialized_len_inner()
}
fn serialize_into_buf(&self, buf: &mut [u8]) -> Result<usize> {
let inner_len = self.serialized_len_inner();
let total = BIOP_HEADER_LEN + inner_len;
if buf.len() < total {
return Err(Error::OutputBufferTooSmall {
need: total,
have: buf.len(),
});
}
write_biop_header(buf, inner_len as u32);
let mut pos = BIOP_HEADER_LEN;
if self.object_key.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.object_key.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.object_key.len() as u8;
pos += OBJECT_KEY_LEN_FIELD;
buf[pos..pos + self.object_key.len()].copy_from_slice(self.object_key);
pos += self.object_key.len();
buf[pos..pos + 4].copy_from_slice(&(OBJECT_KIND_DATA_LEN as u32).to_be_bytes());
pos += OBJECT_KIND_LEN_FIELD;
buf[pos..pos + 4].copy_from_slice(b"fil\0");
pos += OBJECT_KIND_DATA_LEN;
let obj_info_total = FILE_CONTENT_SIZE_LEN + self.object_info_extra.len();
if obj_info_total > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: obj_info_total,
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(obj_info_total as u16).to_be_bytes());
pos += OBJECT_INFO_LEN_FIELD;
buf[pos..pos + 8].copy_from_slice(&self.content_size.to_be_bytes());
pos += FILE_CONTENT_SIZE_LEN;
buf[pos..pos + self.object_info_extra.len()].copy_from_slice(self.object_info_extra);
pos += self.object_info_extra.len();
buf[pos..pos + self.service_context.len()].copy_from_slice(self.service_context);
pos += self.service_context.len();
let body_len = FILE_CONTENT_LEN_FIELD + self.content.len();
buf[pos..pos + 4].copy_from_slice(&(body_len as u32).to_be_bytes());
pos += MESSAGE_BODY_LEN_FIELD;
buf[pos..pos + 4].copy_from_slice(&(self.content.len() as u32).to_be_bytes());
pos += FILE_CONTENT_LEN_FIELD;
buf[pos..pos + self.content.len()].copy_from_slice(self.content);
Ok(total)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum BiopMessage<'a> {
Directory(DirectoryMessage<'a>),
File(FileMessage<'a>),
ServiceGateway(DirectoryMessage<'a>),
#[cfg_attr(feature = "serde", serde(borrow))]
Stream(&'a [u8]),
#[cfg_attr(feature = "serde", serde(borrow))]
StreamEvent(&'a [u8]),
}
impl<'a> BiopMessage<'a> {
pub fn parse_at(bytes: &'a [u8]) -> Result<(Self, usize)> {
let (object_key, kind_bytes, message_size, pos) = parse_biop_header(bytes)?;
let consumed = BIOP_HEADER_LEN + message_size;
let end = consumed;
let msg = match &kind_bytes {
b"dir\0" => {
let dm = DirectoryMessage::parse_from(bytes, object_key, kind_bytes, pos, end)?;
BiopMessage::Directory(dm)
}
b"srg\0" => {
let dm = DirectoryMessage::parse_from(bytes, object_key, kind_bytes, pos, end)?;
BiopMessage::ServiceGateway(dm)
}
b"fil\0" => {
let fm = FileMessage::parse_from(bytes, object_key, pos, end)?;
BiopMessage::File(fm)
}
b"str\0" => BiopMessage::Stream(&bytes[pos..end]),
b"ste\0" => BiopMessage::StreamEvent(&bytes[pos..end]),
_ => {
BiopMessage::Stream(&bytes[pos..end])
}
};
Ok((msg, consumed))
}
fn serialized_len_total(&self) -> usize {
match self {
Self::Directory(d) | Self::ServiceGateway(d) => d.serialized_len_total(),
Self::File(f) => f.serialized_len_total(),
Self::Stream(raw) | Self::StreamEvent(raw) => BIOP_HEADER_LEN + raw.len(),
}
}
fn kind_bytes(&self) -> [u8; 4] {
match self {
Self::Directory(_) => *b"dir\0",
Self::File(_) => *b"fil\0",
Self::ServiceGateway(_) => *b"srg\0",
Self::Stream(_) => *b"str\0",
Self::StreamEvent(_) => *b"ste\0",
}
}
}
impl Serialize for BiopMessage<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
self.serialized_len_total()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len_total();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
match self {
Self::Directory(d) | Self::ServiceGateway(d) => {
d.serialize_into_buf(buf)?;
}
Self::File(f) => {
f.serialize_into_buf(buf)?;
}
Self::Stream(raw) | Self::StreamEvent(raw) => {
let kind = self.kind_bytes();
let inner_len = OBJECT_KEY_LEN_FIELD + OBJECT_KIND_LEN_FIELD + OBJECT_KIND_DATA_LEN
+ raw.len();
write_biop_header(buf, inner_len as u32);
let mut pos = BIOP_HEADER_LEN;
buf[pos] = 0; pos += OBJECT_KEY_LEN_FIELD;
buf[pos..pos + 4].copy_from_slice(&(OBJECT_KIND_DATA_LEN as u32).to_be_bytes());
pos += OBJECT_KIND_LEN_FIELD;
buf[pos..pos + 4].copy_from_slice(&kind);
pos += OBJECT_KIND_DATA_LEN;
buf[pos..pos + raw.len()].copy_from_slice(raw);
}
}
Ok(len)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ModuleInfo<'a> {
pub module_timeout: u32,
pub block_timeout: u32,
pub min_block_time: u32,
#[cfg_attr(feature = "serde", serde(borrow))]
pub taps: Vec<super::ior::Tap<'a>>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub user_info: &'a [u8],
}
impl<'a> ModuleInfo<'a> {
pub fn descriptors(&self) -> impl Iterator<Item = (u8, &[u8])> {
DescriptorIter {
data: self.user_info,
pos: 0,
}
}
pub fn compressed_module_descriptor(&self) -> Option<CompressedModuleDescriptor<'_>> {
for (tag, data) in self.descriptors() {
if tag == COMPRESSED_MODULE_DESCRIPTOR_TAG {
return Some(CompressedModuleDescriptor { body: data });
}
}
None
}
}
struct DescriptorIter<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Iterator for DescriptorIter<'a> {
type Item = (u8, &'a [u8]);
fn next(&mut self) -> Option<Self::Item> {
let end = self.data.len();
if self.pos + 2 > end {
return None;
}
let tag = self.data[self.pos];
let len = self.data[self.pos + 1] as usize;
self.pos += 2;
if self.pos + len > end {
return None;
}
let d = &self.data[self.pos..self.pos + len];
self.pos += len;
Some((tag, d))
}
}
impl<'a> Parse<'a> for ModuleInfo<'a> {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let end = bytes.len();
if end < MODULE_INFO_FIXED + MODULE_TAPS_COUNT_FIELD {
return Err(Error::BufferTooShort {
need: MODULE_INFO_FIXED + MODULE_TAPS_COUNT_FIELD,
have: end,
what: "ModuleInfo fixed fields",
});
}
let module_timeout = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let block_timeout = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
let min_block_time = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
let taps_count = bytes[12] as usize;
let mut pos = MODULE_INFO_FIXED + MODULE_TAPS_COUNT_FIELD;
let mut taps = Vec::with_capacity(taps_count.min(8));
for _ in 0..taps_count {
let (tap, next) = super::ior::Tap::parse_from(bytes, pos, end)?;
taps.push(tap);
pos = next;
}
if pos + MODULE_USER_INFO_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + MODULE_USER_INFO_LEN_FIELD,
have: end,
what: "ModuleInfo UserInfoLength",
});
}
let user_info_len = bytes[pos] as usize;
pos += MODULE_USER_INFO_LEN_FIELD;
if pos + user_info_len > end {
return Err(Error::SectionLengthOverflow {
declared: user_info_len,
available: end - pos,
});
}
let user_info = &bytes[pos..pos + user_info_len];
Ok(ModuleInfo {
module_timeout,
block_timeout,
min_block_time,
taps,
user_info,
})
}
}
impl Serialize for ModuleInfo<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
let taps_len: usize = self.taps.iter().map(|t| t.serialized_len()).sum();
MODULE_INFO_FIXED
+ MODULE_TAPS_COUNT_FIELD
+ taps_len
+ MODULE_USER_INFO_LEN_FIELD
+ self.user_info.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
if buf.len() < len {
return Err(Error::OutputBufferTooSmall {
need: len,
have: buf.len(),
});
}
buf[0..4].copy_from_slice(&self.module_timeout.to_be_bytes());
buf[4..8].copy_from_slice(&self.block_timeout.to_be_bytes());
buf[8..12].copy_from_slice(&self.min_block_time.to_be_bytes());
if self.taps.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.taps.len(),
available: u8::MAX as usize,
});
}
buf[12] = self.taps.len() as u8;
let mut pos = MODULE_INFO_FIXED + MODULE_TAPS_COUNT_FIELD;
for tap in &self.taps {
let written = tap.serialize_into_buf(&mut buf[pos..])?;
pos += written;
}
if self.user_info.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.user_info.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.user_info.len() as u8;
pos += MODULE_USER_INFO_LEN_FIELD;
buf[pos..pos + self.user_info.len()].copy_from_slice(self.user_info);
pos += self.user_info.len();
Ok(pos)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CompressedModuleDescriptor<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub body: &'a [u8],
}
#[cfg(feature = "flate2")]
pub fn decompress_zlib(data: &[u8]) -> Result<Vec<u8>> {
use std::io::Read;
let mut decoder = flate2::read::ZlibDecoder::new(data);
let mut out = Vec::new();
decoder
.read_to_end(&mut out)
.map_err(|e| Error::ReservedBitsViolation {
field: "compressed_module_descriptor body",
reason: if e.kind() == std::io::ErrorKind::InvalidData {
"zlib decompression failed: invalid data"
} else {
"zlib decompression failed"
},
})?;
Ok(out)
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceGatewayInfo<'a> {
pub ior: Ior<'a>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub download_taps: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_context: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub user_info: &'a [u8],
}
impl<'a> ServiceGatewayInfo<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self> {
let end = bytes.len();
let ior = Ior::parse(bytes)?;
let mut pos = ior.serialized_len();
if pos + SGI_DOWNLOAD_TAPS_COUNT_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + SGI_DOWNLOAD_TAPS_COUNT_FIELD,
have: end,
what: "ServiceGatewayInfo downloadTaps_count",
});
}
let tap_count = bytes[pos] as usize;
let dl_taps_start = pos;
pos += SGI_DOWNLOAD_TAPS_COUNT_FIELD;
for _ in 0..tap_count {
let (_, next) = super::ior::Tap::parse_from(bytes, pos, end)?;
pos = next;
}
let download_taps = &bytes[dl_taps_start..pos];
let (service_context, next) = parse_service_context_list(bytes, pos, end)?;
pos = next;
if pos + SGI_USER_INFO_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + SGI_USER_INFO_LEN_FIELD,
have: end,
what: "ServiceGatewayInfo userInfoLength",
});
}
let ui_len = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
pos += SGI_USER_INFO_LEN_FIELD;
if pos + ui_len > end {
return Err(Error::SectionLengthOverflow {
declared: ui_len,
available: end - pos,
});
}
let user_info = &bytes[pos..pos + ui_len];
Ok(ServiceGatewayInfo {
ior,
download_taps,
service_context,
user_info,
})
}
pub fn to_bytes(&self) -> Vec<u8> {
let len = self.ior.serialized_len()
+ self.download_taps.len()
+ self.service_context.len()
+ SGI_USER_INFO_LEN_FIELD
+ self.user_info.len();
let mut buf = vec![0u8; len];
let mut pos = 0;
let written = self
.ior
.serialize_into(&mut buf[pos..])
.expect("IOR serialize");
pos += written;
buf[pos..pos + self.download_taps.len()].copy_from_slice(self.download_taps);
pos += self.download_taps.len();
buf[pos..pos + self.service_context.len()].copy_from_slice(self.service_context);
pos += self.service_context.len();
buf[pos..pos + 2].copy_from_slice(&(self.user_info.len() as u16).to_be_bytes());
pos += SGI_USER_INFO_LEN_FIELD;
buf[pos..pos + self.user_info.len()].copy_from_slice(self.user_info);
buf
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::carousel::biop::BINDING_NOBJECT;
use dvb_common::Parse;
fn sample_file_message(key: &'static [u8], content: &'static [u8]) -> BiopMessage<'static> {
BiopMessage::File(FileMessage {
object_key: key,
content_size: content.len() as u64,
object_info_extra: &[],
service_context: &[0x00], content,
})
}
fn sample_dir_message() -> BiopMessage<'static> {
use crate::carousel::biop::ior::{
BiopProfileBody, ConnBinder, ObjectLocation, TaggedProfile,
};
let ior = crate::carousel::biop::ior::Ior {
type_id: b"fil\0",
profiles: vec![TaggedProfile::Biop(BiopProfileBody {
object_location: ObjectLocation {
carousel_id: 0xAB,
module_id: 2,
version_major: 1,
version_minor: 0,
object_key: &[0x02],
},
conn_binder: ConnBinder { taps: vec![] },
extra: vec![],
})],
};
BiopMessage::Directory(DirectoryMessage {
object_kind: *b"dir\0",
object_key: &[0x01],
object_info: &[],
service_context: &[0x00], bindings: vec![Binding {
name: vec![NameComponent {
id: b"index.html",
kind: b"fil\0",
}],
binding_type: BINDING_NOBJECT,
ior,
object_info: &[],
}],
})
}
#[test]
fn file_message_round_trip() {
let content: &[u8] = b"Hello, BIOP!";
let msg = sample_file_message(&[0x01], content);
let mut buf = vec![0u8; msg.serialized_len()];
msg.serialize_into(&mut buf).unwrap();
let (parsed, consumed) = BiopMessage::parse_at(&buf).unwrap();
assert_eq!(consumed, buf.len());
assert_eq!(parsed, msg);
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(buf, buf2);
}
#[test]
fn directory_message_round_trip() {
let msg = sample_dir_message();
let mut buf = vec![0u8; msg.serialized_len()];
msg.serialize_into(&mut buf).unwrap();
let (parsed, consumed) = BiopMessage::parse_at(&buf).unwrap();
assert_eq!(consumed, buf.len());
assert_eq!(parsed, msg);
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(buf, buf2, "Directory message byte-exact re-serialize");
}
#[test]
fn module_info_round_trip() {
use crate::carousel::biop::ior::Tap;
let info = ModuleInfo {
module_timeout: 0x00FFFFFF,
block_timeout: 0x00FFFFFF,
min_block_time: 0x00000064,
taps: vec![Tap {
id: 0,
use_: 0x0017,
association_tag: 0x0042,
selector: &[],
}],
user_info: &[],
};
let mut buf = vec![0u8; info.serialized_len()];
info.serialize_into(&mut buf).unwrap();
let parsed = ModuleInfo::parse(&buf).unwrap();
assert_eq!(parsed, info);
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(buf, buf2, "ModuleInfo byte-exact re-serialize");
}
#[test]
fn module_info_byte_anchor() {
use crate::carousel::biop::ior::Tap;
#[rustfmt::skip]
let expected: &[u8] = &[
0x00, 0x0F, 0x42, 0x40, 0x00, 0x0F, 0x42, 0x40, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x47, 0x00, 0x00, ];
let info = ModuleInfo {
module_timeout: 0x000F4240,
block_timeout: 0x000F4240,
min_block_time: 0x00000064,
taps: vec![Tap {
id: 0,
use_: 0x0017,
association_tag: 0x0047,
selector: &[],
}],
user_info: &[],
};
let mut buf = vec![0u8; info.serialized_len()];
info.serialize_into(&mut buf).unwrap();
assert_eq!(buf.as_slice(), expected);
let parsed = ModuleInfo::parse(expected).unwrap();
assert_eq!(parsed, info);
}
#[test]
fn sgi_byte_anchor_m6() {
#[rustfmt::skip]
let raw: &[u8] = &[
0x00, 0x00, 0x00, 0x04, 0x73, 0x72, 0x67, 0x00, 0x00, 0x00, 0x00, 0x01, 0x49, 0x53, 0x4F, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x02, 0x49, 0x53, 0x4F, 0x50, 0x0A, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x49, 0x53, 0x4F, 0x40, 0x12, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x47, 0x0A, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, ];
assert_eq!(raw.len(), 64);
let sgi = ServiceGatewayInfo::parse(raw).unwrap();
assert_eq!(sgi.ior.type_id, b"srg\0");
assert_eq!(sgi.ior.profiles.len(), 1);
let bp = sgi.ior.biop_profile().unwrap();
assert_eq!(bp.object_location.carousel_id, 0xAB);
assert_eq!(bp.object_location.module_id, 1);
assert_eq!(bp.object_location.version_major, 1);
assert_eq!(bp.object_location.version_minor, 0);
assert_eq!(bp.object_location.object_key, &[0x01]);
assert_eq!(bp.conn_binder.taps.len(), 1);
let tap = &bp.conn_binder.taps[0];
assert_eq!(tap.use_, 0x0016);
assert_eq!(tap.association_tag, 0x47);
assert_eq!(tap.transaction_id(), Some(0x80000002));
assert_eq!(tap.timeout(), Some(0xFFFFFFFF));
let out = sgi.to_bytes();
assert_eq!(out.len(), 64, "SGI serialized length");
assert_eq!(out.as_slice(), raw, "SGI byte-exact round-trip");
}
#[cfg(feature = "serde")]
#[test]
fn biop_serde_round_trip() {
let content: &[u8] = b"test content";
let msg = sample_file_message(&[0x01], content);
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("content_size"));
}
#[cfg(feature = "flate2")]
#[test]
fn zlib_round_trip() {
use flate2::{write::ZlibEncoder, Compression};
use std::io::Write;
let original = b"Hello, compressed BIOP world! ".repeat(10);
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
encoder.write_all(&original).unwrap();
let compressed = encoder.finish().unwrap();
let decompressed = decompress_zlib(&compressed).unwrap();
assert_eq!(decompressed.as_slice(), original.as_slice());
}
}