use alloc::vec;
use alloc::vec::Vec;
use super::{
ior::{Ior, NameComponent},
BINDING_NCONTEXT, BINDING_NOBJECT, 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};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum BindingType {
NObject,
NContext,
Reserved(u8),
}
impl BindingType {
#[must_use]
pub fn from_u8(v: u8) -> Self {
match v {
BINDING_NOBJECT => Self::NObject,
BINDING_NCONTEXT => Self::NContext,
v => Self::Reserved(v),
}
}
#[must_use]
pub fn to_u8(self) -> u8 {
match self {
Self::NObject => BINDING_NOBJECT,
Self::NContext => BINDING_NCONTEXT,
Self::Reserved(v) => v,
}
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::NObject => "nobject",
Self::NContext => "ncontext",
Self::Reserved(_) => "reserved",
}
}
}
dvb_common::impl_spec_display!(BindingType, Reserved);
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 STREAM_ADESC_LEN_FIELD: usize = 1;
const STREAM_INFO_FIXED: usize = 9;
const STREAM_TAPS_COUNT_FIELD: usize = 1;
const STREAM_EVENT_NAMES_COUNT_FIELD: usize = 2;
const STREAM_EVENT_NAME_LEN_FIELD: usize = 1;
const STREAM_EVENT_IDS_COUNT_FIELD: usize = 1;
const STREAM_EVENT_ID_LEN: usize = 2;
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: BindingType,
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 = BindingType::from_u8(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.to_u8();
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)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ServiceContext<'a> {
pub context_id: u32,
#[cfg_attr(feature = "serde", serde(borrow))]
pub data: &'a [u8],
}
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<'a>(
bytes: &'a [u8],
pos: usize,
end: usize,
) -> Result<(Vec<ServiceContext<'a>>, 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 mut cur = pos + SERVICE_CONTEXT_COUNT_FIELD;
let mut list = Vec::with_capacity(count.min(16));
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 context_id =
u32::from_be_bytes([bytes[cur], bytes[cur + 1], bytes[cur + 2], bytes[cur + 3]]);
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,
});
}
let data = &bytes[cur..cur + ctx_data_len];
cur += ctx_data_len;
list.push(ServiceContext { context_id, data });
}
Ok((list, cur))
}
fn service_context_list_len(list: &[ServiceContext]) -> usize {
SERVICE_CONTEXT_COUNT_FIELD
+ list
.iter()
.map(|e| SERVICE_CONTEXT_FIXED + e.data.len())
.sum::<usize>()
}
fn write_service_context_list(buf: &mut [u8], list: &[ServiceContext]) -> Result<usize> {
if list.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: list.len(),
available: u8::MAX as usize,
});
}
buf[0] = list.len() as u8;
let mut pos = SERVICE_CONTEXT_COUNT_FIELD;
for entry in list {
if entry.data.len() > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: entry.data.len(),
available: u16::MAX as usize,
});
}
buf[pos..pos + 4].copy_from_slice(&entry.context_id.to_be_bytes());
buf[pos + 4..pos + 6].copy_from_slice(&(entry.data.len() as u16).to_be_bytes());
pos += SERVICE_CONTEXT_FIXED;
buf[pos..pos + entry.data.len()].copy_from_slice(entry.data);
pos += entry.data.len();
}
Ok(pos)
}
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: Vec<ServiceContext<'a>>,
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 = service_context_list_len(&self.service_context);
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();
pos += write_service_context_list(&mut buf[pos..], &self.service_context)?;
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: Vec<ServiceContext<'a>>,
#[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
+ service_context_list_len(&self.service_context)
+ 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();
pos += write_service_context_list(&mut buf[pos..], &self.service_context)?;
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))]
pub struct DsmStreamInfo<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub description: &'a [u8],
pub duration_seconds: i32,
pub duration_microseconds: u16,
pub audio: u8,
pub video: u8,
pub data: u8,
}
impl<'a> DsmStreamInfo<'a> {
fn serialized_len(&self) -> usize {
STREAM_ADESC_LEN_FIELD + self.description.len() + STREAM_INFO_FIXED
}
fn parse_from(bytes: &'a [u8], pos: usize, end: usize) -> Result<(Self, usize)> {
if pos + STREAM_ADESC_LEN_FIELD > end {
return Err(Error::BufferTooShort {
need: pos + STREAM_ADESC_LEN_FIELD,
have: end,
what: "DsmStreamInfo aDescription_length",
});
}
let desc_len = bytes[pos] as usize;
let mut cur = pos + STREAM_ADESC_LEN_FIELD;
if cur + desc_len > end {
return Err(Error::SectionLengthOverflow {
declared: desc_len,
available: end - cur,
});
}
let description = &bytes[cur..cur + desc_len];
cur += desc_len;
if cur + STREAM_INFO_FIXED > end {
return Err(Error::BufferTooShort {
need: cur + STREAM_INFO_FIXED,
have: end,
what: "DsmStreamInfo fixed fields",
});
}
let duration_seconds =
i32::from_be_bytes([bytes[cur], bytes[cur + 1], bytes[cur + 2], bytes[cur + 3]]);
cur += 4;
let duration_microseconds = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]);
cur += 2;
let audio = bytes[cur];
cur += 1;
let video = bytes[cur];
cur += 1;
let data = bytes[cur];
cur += 1;
Ok((
DsmStreamInfo {
description,
duration_seconds,
duration_microseconds,
audio,
video,
data,
},
cur,
))
}
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.description.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.description.len(),
available: u8::MAX as usize,
});
}
buf[0] = self.description.len() as u8;
let mut pos = STREAM_ADESC_LEN_FIELD;
buf[pos..pos + self.description.len()].copy_from_slice(self.description);
pos += self.description.len();
buf[pos..pos + 4].copy_from_slice(&self.duration_seconds.to_be_bytes());
pos += 4;
buf[pos..pos + 2].copy_from_slice(&self.duration_microseconds.to_be_bytes());
pos += 2;
buf[pos] = self.audio;
pos += 1;
buf[pos] = self.video;
pos += 1;
buf[pos] = self.data;
pos += 1;
Ok(pos)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct StreamMessage<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_key: &'a [u8],
pub stream_info: DsmStreamInfo<'a>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_info_extra: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_context: Vec<ServiceContext<'a>>,
pub taps: Vec<super::ior::Tap<'a>>,
}
impl<'a> StreamMessage<'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: "StreamMessage 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 obj_info_start = cur;
let obj_info_end = cur + obj_info_len;
let (stream_info, _) = DsmStreamInfo::parse_from(bytes, cur, obj_info_end)?;
let info_len = stream_info.serialized_len();
if obj_info_len < info_len {
return Err(Error::ValueOutOfRange {
field: "StreamMessage.objectInfo_length",
reason: "objectInfo too short for DSM::Stream::Info_T",
});
}
let object_info_extra = &bytes[obj_info_start + info_len..obj_info_end];
cur = obj_info_end;
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: "StreamMessage 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 + STREAM_TAPS_COUNT_FIELD > body_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_TAPS_COUNT_FIELD,
have: body_end,
what: "StreamMessage taps_count",
});
}
let taps_count = bytes[cur] as usize;
cur += STREAM_TAPS_COUNT_FIELD;
let mut taps = Vec::with_capacity(taps_count.min(16));
for _ in 0..taps_count {
let (tap, next) = super::ior::Tap::parse_from(bytes, cur, body_end)?;
taps.push(tap);
cur = next;
}
Ok(StreamMessage {
object_key,
stream_info,
object_info_extra,
service_context,
taps,
})
}
fn body_len(&self) -> usize {
let taps_len: usize = self.taps.iter().map(|t| t.serialized_len()).sum();
STREAM_TAPS_COUNT_FIELD + taps_len
}
fn obj_info_len(&self) -> usize {
self.stream_info.serialized_len() + self.object_info_extra.len()
}
fn serialized_len_inner(&self) -> usize {
OBJECT_KEY_LEN_FIELD
+ self.object_key.len()
+ OBJECT_KIND_LEN_FIELD
+ OBJECT_KIND_DATA_LEN
+ OBJECT_INFO_LEN_FIELD
+ self.obj_info_len()
+ service_context_list_len(&self.service_context)
+ MESSAGE_BODY_LEN_FIELD
+ self.body_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"str\0");
pos += OBJECT_KIND_DATA_LEN;
let oi_len = self.obj_info_len();
if oi_len > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: oi_len,
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(oi_len as u16).to_be_bytes());
pos += OBJECT_INFO_LEN_FIELD;
let written = self.stream_info.serialize_into_buf(&mut buf[pos..])?;
pos += written;
buf[pos..pos + self.object_info_extra.len()].copy_from_slice(self.object_info_extra);
pos += self.object_info_extra.len();
pos += write_service_context_list(&mut buf[pos..], &self.service_context)?;
let bl = self.body_len();
buf[pos..pos + 4].copy_from_slice(&(bl as u32).to_be_bytes());
pos += MESSAGE_BODY_LEN_FIELD;
if self.taps.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.taps.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.taps.len() as u8;
pos += STREAM_TAPS_COUNT_FIELD;
for tap in &self.taps {
let written = tap.serialize_into_buf(&mut buf[pos..])?;
pos += written;
}
Ok(total)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct StreamEventMessage<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_key: &'a [u8],
pub stream_info: DsmStreamInfo<'a>,
pub event_names: Vec<&'a [u8]>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub object_info_extra: &'a [u8],
#[cfg_attr(feature = "serde", serde(borrow))]
pub service_context: Vec<ServiceContext<'a>>,
pub taps: Vec<super::ior::Tap<'a>>,
pub event_ids: Vec<u16>,
}
impl<'a> StreamEventMessage<'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: "StreamEventMessage 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 obj_info_end = cur + obj_info_len;
let (stream_info, next_cur) = DsmStreamInfo::parse_from(bytes, cur, obj_info_end)?;
cur = next_cur;
if cur + STREAM_EVENT_NAMES_COUNT_FIELD > obj_info_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_EVENT_NAMES_COUNT_FIELD,
have: obj_info_end,
what: "StreamEventMessage eventNames_count",
});
}
let event_names_count = u16::from_be_bytes([bytes[cur], bytes[cur + 1]]) as usize;
cur += STREAM_EVENT_NAMES_COUNT_FIELD;
let mut event_names = Vec::with_capacity(event_names_count.min(64));
for _ in 0..event_names_count {
if cur + STREAM_EVENT_NAME_LEN_FIELD > obj_info_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_EVENT_NAME_LEN_FIELD,
have: obj_info_end,
what: "StreamEventMessage eventName_length",
});
}
let name_len = bytes[cur] as usize;
cur += STREAM_EVENT_NAME_LEN_FIELD;
if cur + name_len > obj_info_end {
return Err(Error::SectionLengthOverflow {
declared: name_len,
available: obj_info_end - cur,
});
}
event_names.push(&bytes[cur..cur + name_len]);
cur += name_len;
}
let object_info_extra = &bytes[cur..obj_info_end];
cur = obj_info_end;
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: "StreamEventMessage 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 + STREAM_TAPS_COUNT_FIELD > body_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_TAPS_COUNT_FIELD,
have: body_end,
what: "StreamEventMessage taps_count",
});
}
let taps_count = bytes[cur] as usize;
cur += STREAM_TAPS_COUNT_FIELD;
let mut taps = Vec::with_capacity(taps_count.min(16));
for _ in 0..taps_count {
let (tap, next) = super::ior::Tap::parse_from(bytes, cur, body_end)?;
taps.push(tap);
cur = next;
}
if cur + STREAM_EVENT_IDS_COUNT_FIELD > body_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_EVENT_IDS_COUNT_FIELD,
have: body_end,
what: "StreamEventMessage eventIds_count",
});
}
let event_ids_count = bytes[cur] as usize;
cur += STREAM_EVENT_IDS_COUNT_FIELD;
if event_ids_count != event_names_count {
return Err(Error::ValueOutOfRange {
field: "StreamEventMessage.eventIds_count",
reason: "eventIds_count must equal eventNames_count",
});
}
let mut event_ids = Vec::with_capacity(event_ids_count.min(64));
for _ in 0..event_ids_count {
if cur + STREAM_EVENT_ID_LEN > body_end {
return Err(Error::BufferTooShort {
need: cur + STREAM_EVENT_ID_LEN,
have: body_end,
what: "StreamEventMessage eventId",
});
}
event_ids.push(u16::from_be_bytes([bytes[cur], bytes[cur + 1]]));
cur += STREAM_EVENT_ID_LEN;
}
let _ = cur; Ok(StreamEventMessage {
object_key,
stream_info,
event_names,
object_info_extra,
service_context,
taps,
event_ids,
})
}
fn event_list_wire_len(&self) -> usize {
let names_len: usize = self
.event_names
.iter()
.map(|n| STREAM_EVENT_NAME_LEN_FIELD + n.len())
.sum();
STREAM_EVENT_NAMES_COUNT_FIELD + names_len
}
fn body_len(&self) -> usize {
let taps_len: usize = self.taps.iter().map(|t| t.serialized_len()).sum();
STREAM_TAPS_COUNT_FIELD
+ taps_len
+ STREAM_EVENT_IDS_COUNT_FIELD
+ self.event_ids.len() * STREAM_EVENT_ID_LEN
}
fn obj_info_len(&self) -> usize {
self.stream_info.serialized_len()
+ self.event_list_wire_len()
+ self.object_info_extra.len()
}
fn serialized_len_inner(&self) -> usize {
OBJECT_KEY_LEN_FIELD
+ self.object_key.len()
+ OBJECT_KIND_LEN_FIELD
+ OBJECT_KIND_DATA_LEN
+ OBJECT_INFO_LEN_FIELD
+ self.obj_info_len()
+ service_context_list_len(&self.service_context)
+ MESSAGE_BODY_LEN_FIELD
+ self.body_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"ste\0");
pos += OBJECT_KIND_DATA_LEN;
let oi_len = self.obj_info_len();
if oi_len > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: oi_len,
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(oi_len as u16).to_be_bytes());
pos += OBJECT_INFO_LEN_FIELD;
let written = self.stream_info.serialize_into_buf(&mut buf[pos..])?;
pos += written;
if self.event_names.len() > u16::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.event_names.len(),
available: u16::MAX as usize,
});
}
buf[pos..pos + 2].copy_from_slice(&(self.event_names.len() as u16).to_be_bytes());
pos += STREAM_EVENT_NAMES_COUNT_FIELD;
for name in &self.event_names {
if name.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: name.len(),
available: u8::MAX as usize,
});
}
buf[pos] = name.len() as u8;
pos += STREAM_EVENT_NAME_LEN_FIELD;
buf[pos..pos + name.len()].copy_from_slice(name);
pos += name.len();
}
buf[pos..pos + self.object_info_extra.len()].copy_from_slice(self.object_info_extra);
pos += self.object_info_extra.len();
pos += write_service_context_list(&mut buf[pos..], &self.service_context)?;
let bl = self.body_len();
buf[pos..pos + 4].copy_from_slice(&(bl as u32).to_be_bytes());
pos += MESSAGE_BODY_LEN_FIELD;
if self.taps.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.taps.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.taps.len() as u8;
pos += STREAM_TAPS_COUNT_FIELD;
for tap in &self.taps {
let written = tap.serialize_into_buf(&mut buf[pos..])?;
pos += written;
}
if self.event_ids.len() > u8::MAX as usize {
return Err(Error::SectionLengthOverflow {
declared: self.event_ids.len(),
available: u8::MAX as usize,
});
}
buf[pos] = self.event_ids.len() as u8;
pos += STREAM_EVENT_IDS_COUNT_FIELD;
for &id in &self.event_ids {
buf[pos..pos + 2].copy_from_slice(&id.to_be_bytes());
pos += STREAM_EVENT_ID_LEN;
}
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>),
Stream(StreamMessage<'a>),
StreamEvent(StreamEventMessage<'a>),
}
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" => {
let sm = StreamMessage::parse_from(bytes, object_key, pos, end)?;
BiopMessage::Stream(sm)
}
b"ste\0" => {
let se = StreamEventMessage::parse_from(bytes, object_key, pos, end)?;
BiopMessage::StreamEvent(se)
}
_ => {
return Err(Error::ValueOutOfRange {
field: "BiopMessage.objectKind",
reason: "unknown BIOP objectKind",
});
}
};
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(s) => s.serialized_len_total(),
Self::StreamEvent(se) => se.serialized_len_total(),
}
}
}
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(s) => {
s.serialize_into_buf(buf)?;
}
Self::StreamEvent(se) => {
se.serialize_into_buf(buf)?;
}
}
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: Vec<ServiceContext<'a>>,
#[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()
+ service_context_list_len(&self.service_context)
+ 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();
pos += write_service_context_list(&mut buf[pos..], &self.service_context)
.expect("serviceContext fits");
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 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: vec![],
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: vec![],
bindings: vec![Binding {
name: vec![NameComponent {
id: b"index.html",
kind: b"fil\0",
}],
binding_type: BindingType::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());
}
#[test]
fn stream_message_round_trip() {
use crate::carousel::biop::ior::Tap;
let msg = BiopMessage::Stream(StreamMessage {
object_key: &[0x01, 0x02],
stream_info: DsmStreamInfo {
description: b"audio stream",
duration_seconds: -5,
duration_microseconds: 500,
audio: 1,
video: 0,
data: 0,
},
object_info_extra: b"\xDE\xAD",
service_context: vec![],
taps: vec![
Tap {
id: 0,
use_: 0x0018,
association_tag: 0x0010,
selector: &[],
},
Tap {
id: 0,
use_: 0x0019,
association_tag: 0x0011,
selector: &[],
},
],
});
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(), "consumed must equal total buf len");
assert_eq!(parsed, msg);
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(buf, buf2, "StreamMessage byte-exact re-serialize");
}
#[test]
fn stream_event_message_round_trip() {
use crate::carousel::biop::ior::Tap;
let msg = BiopMessage::StreamEvent(StreamEventMessage {
object_key: &[0x03],
stream_info: DsmStreamInfo {
description: b"event stream",
duration_seconds: 3600,
duration_microseconds: 0,
audio: 0,
video: 1,
data: 0,
},
event_names: vec![b"play".as_ref(), b"pause".as_ref(), b"stop".as_ref()],
object_info_extra: &[],
service_context: vec![],
taps: vec![Tap {
id: 0,
use_: 0x000C,
association_tag: 0x0020,
selector: &[],
}],
event_ids: vec![0x0001, 0x0002, 0x0003],
});
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(), "consumed must equal total buf len");
assert_eq!(parsed, msg);
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(buf, buf2, "StreamEventMessage byte-exact re-serialize");
}
#[test]
fn stream_message_byte_anchor() {
use crate::carousel::biop::ior::Tap;
#[rustfmt::skip]
let expected: &[u8] = &[
0x42, 0x49, 0x4F, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0xAB, 0x00, 0x00, 0x00, 0x04, 0x73, 0x74, 0x72, 0x00, 0x00, 0x0D, 0x03, 0x76, 0x69, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x47, 0x00, ];
assert_eq!(expected.len(), 50);
let expected_msg = BiopMessage::Stream(StreamMessage {
object_key: &[0xAB],
stream_info: DsmStreamInfo {
description: b"vid",
duration_seconds: 0,
duration_microseconds: 0,
audio: 1,
video: 1,
data: 0,
},
object_info_extra: &[],
service_context: vec![],
taps: vec![Tap {
id: 0,
use_: 0x0018,
association_tag: 0x0047,
selector: &[],
}],
});
let mut buf = vec![0u8; expected_msg.serialized_len()];
expected_msg.serialize_into(&mut buf).unwrap();
assert_eq!(
buf.as_slice(),
expected,
"StreamMessage serialize must match byte anchor"
);
let (parsed, consumed) = BiopMessage::parse_at(expected).unwrap();
assert_eq!(consumed, expected.len());
assert_eq!(
parsed, expected_msg,
"StreamMessage parse must match byte anchor struct"
);
}
#[test]
fn stream_event_message_byte_anchor() {
#[rustfmt::skip]
let expected: &[u8] = &[
0x42, 0x49, 0x4F, 0x50, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x01, 0xCD, 0x00, 0x00, 0x00, 0x04, 0x73, 0x74, 0x65, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x66, 0x6F, 0x6F, 0x03, 0x62, 0x61, 0x72, 0x00,
0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, ];
assert_eq!(expected.len(), 55);
let expected_msg = BiopMessage::StreamEvent(StreamEventMessage {
object_key: &[0xCD],
stream_info: DsmStreamInfo {
description: &[],
duration_seconds: 0,
duration_microseconds: 0,
audio: 0,
video: 0,
data: 0,
},
event_names: vec![b"foo".as_ref(), b"bar".as_ref()],
object_info_extra: &[],
service_context: vec![],
taps: vec![],
event_ids: vec![1, 2],
});
let mut buf = vec![0u8; expected_msg.serialized_len()];
expected_msg.serialize_into(&mut buf).unwrap();
assert_eq!(
buf.as_slice(),
expected,
"StreamEventMessage serialize must match byte anchor"
);
let (parsed, consumed) = BiopMessage::parse_at(expected).unwrap();
assert_eq!(consumed, expected.len());
assert_eq!(
parsed, expected_msg,
"StreamEventMessage parse must match byte anchor struct"
);
}
#[test]
fn service_context_typed_round_trip() {
let msg = BiopMessage::File(FileMessage {
object_key: &[0x01],
content_size: 3,
object_info_extra: &[],
service_context: vec![
ServiceContext {
context_id: 0xDEADBEEF,
data: &[1, 2, 3],
},
ServiceContext {
context_id: 0x11223344,
data: &[],
},
],
content: b"abc",
});
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(), "consumed must equal total buf len");
assert_eq!(parsed, msg, "parsed must equal original");
let mut buf2 = vec![0u8; parsed.serialized_len()];
parsed.serialize_into(&mut buf2).unwrap();
assert_eq!(
buf, buf2,
"serviceContext typed round-trip must be byte-exact"
);
assert_eq!(buf[32], 2, "serviceContextList_count must be 2");
assert_eq!(&buf[33..37], &[0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(&buf[37..39], &[0x00, 0x03]);
assert_eq!(&buf[39..42], &[0x01, 0x02, 0x03]);
assert_eq!(&buf[42..46], &[0x11, 0x22, 0x33, 0x44]);
assert_eq!(&buf[46..48], &[0x00, 0x00]);
}
#[test]
fn binding_type_full_range_round_trip() {
for v in 0u8..=0xFF {
let bt = BindingType::from_u8(v);
assert_eq!(bt.to_u8(), v, "BindingType round-trip failed for 0x{v:02X}");
}
}
#[test]
fn binding_type_known_values() {
assert_eq!(BindingType::from_u8(0x01), BindingType::NObject);
assert_eq!(BindingType::from_u8(0x02), BindingType::NContext);
assert_eq!(BindingType::NObject.name(), "nobject");
assert_eq!(BindingType::NContext.name(), "ncontext");
assert_eq!(BindingType::Reserved(0x05).name(), "reserved");
}
#[test]
fn directory_message_binding_type_round_trip() {
let msg = sample_dir_message();
let mut buf = vec![0u8; msg.serialized_len()];
msg.serialize_into(&mut buf).unwrap();
let (parsed, _) = BiopMessage::parse_at(&buf).unwrap();
match parsed {
BiopMessage::Directory(d) => {
assert_eq!(d.bindings[0].binding_type, BindingType::NObject);
}
other => panic!("expected Directory, got {other:?}"),
}
}
}