use crate::varint::{MoqtProfile, VarInt};
use bytes::{Buf, BufMut};
#[inline]
pub fn reserve_bounded<T>(count: usize, buf: &impl Buf) -> Vec<T> {
Vec::with_capacity(count.min(buf.remaining()))
}
#[inline]
#[allow(clippy::uninit_vec)]
pub fn read_bytes(buf: &mut impl Buf, len: usize) -> Result<Vec<u8>, crate::error::CodecError> {
if buf.remaining() < len {
return Err(crate::error::CodecError::UnexpectedEnd);
}
let mut v = Vec::with_capacity(len);
unsafe {
v.set_len(len);
}
buf.copy_to_slice(&mut v);
Ok(v)
}
pub fn check_location_range(
start_group: u64,
start_object: u64,
end_group: u64,
end_object: u64,
) -> Result<(), crate::error::CodecError> {
let ends_early = end_group < start_group
|| (end_group == start_group && end_object != 0 && end_object <= start_object);
if ends_early {
return Err(crate::error::CodecError::InvalidRange(
start_group,
start_object,
end_group,
end_object,
));
}
Ok(())
}
pub fn check_group_range(start_group: u64, end_group: u64) -> Result<(), crate::error::CodecError> {
if end_group < start_group {
return Err(crate::error::CodecError::InvalidRange(start_group, 0, end_group, 0));
}
Ok(())
}
pub fn check_open_ended_group_range(
start_group: u64,
end_group: u64,
) -> Result<(), crate::error::CodecError> {
if end_group != 0 && end_group < start_group {
return Err(crate::error::CodecError::InvalidRange(start_group, 0, end_group, 0));
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TrackNamespace(pub Vec<Vec<u8>>);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrackNamespaceRules {
pub min_fields: usize,
pub reject_empty_field: bool,
pub max_namespace_bytes: Option<usize>,
}
impl TrackNamespaceRules {
pub const fn for_draft(draft: u8) -> Self {
TrackNamespaceRules {
min_fields: if draft >= 17 { 0 } else { 1 },
reject_empty_field: draft >= 16,
max_namespace_bytes: if draft >= 16 {
Some(crate::error::MAX_FULL_TRACK_NAME_LENGTH)
} else {
None
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FullTrackName {
pub namespace: TrackNamespace,
pub track_name: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location {
pub group: VarInt,
pub object: VarInt,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ObjectStatus {
Normal = 0x0,
DoesNotExist = 0x1,
EndOfGroup = 0x3,
EndOfTrack = 0x4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum GroupOrder {
Publisher = 0x0,
Ascending = 0x1,
Descending = 0x2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ForwardingPreference {
Object = 0x0,
Datagram = 0x1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ContentExists {
NoLargestLocation = 0,
HasLargestLocation = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Forward {
DontForward = 0,
Forward = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FilterType {
NextGroupStart = 0x1,
LargestObject = 0x2,
AbsoluteStart = 0x3,
AbsoluteRange = 0x4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TokenAliasType {
Delete = 0x0,
Register = 0x1,
UseAlias = 0x2,
UseValue = 0x3,
}
impl TrackNamespace {
pub fn encode(&self, buf: &mut impl BufMut) {
VarInt::from_usize(self.0.len()).encode(buf);
for elem in &self.0 {
VarInt::from_usize(elem.len()).encode(buf);
buf.put_slice(elem);
}
}
pub fn decode(buf: &mut impl Buf) -> Result<Self, crate::error::CodecError> {
Self::decode_rules(
buf,
TrackNamespaceRules {
min_fields: 1,
reject_empty_field: false,
max_namespace_bytes: None,
},
)
}
pub fn decode_allow_empty(buf: &mut impl Buf) -> Result<Self, crate::error::CodecError> {
Self::decode_rules(
buf,
TrackNamespaceRules { min_fields: 0, ..TrackNamespaceRules::for_draft(16) },
)
}
pub fn decode_rules(
buf: &mut impl Buf,
rules: TrackNamespaceRules,
) -> Result<Self, crate::error::CodecError> {
let n = VarInt::decode(buf)?.into_inner() as usize;
if n < rules.min_fields || n > crate::error::MAX_NAMESPACE_TUPLE_SIZE {
return Err(crate::error::CodecError::InvalidNamespaceTupleSize(n));
}
let mut elements = Vec::with_capacity(n);
let mut total = 0usize;
for _ in 0..n {
let len = VarInt::decode(buf)?.into_inner() as usize;
if len == 0 && rules.reject_empty_field {
return Err(crate::error::CodecError::EmptyNamespaceField);
}
total = total.saturating_add(len);
if let Some(max) = rules.max_namespace_bytes {
if total > max {
return Err(crate::error::CodecError::TrackNameTooLong);
}
}
elements.push(read_bytes(buf, len)?);
}
Ok(TrackNamespace(elements))
}
pub fn encode_moqt<P: MoqtProfile>(&self, buf: &mut impl BufMut) {
VarInt::from_usize(self.0.len()).encode_moqt::<P>(buf);
for elem in &self.0 {
VarInt::from_usize(elem.len()).encode_moqt::<P>(buf);
buf.put_slice(elem);
}
}
pub fn decode_moqt<P: MoqtProfile>(
buf: &mut impl Buf,
) -> Result<Self, crate::error::CodecError> {
Self::decode_allow_empty_moqt::<P>(buf)
}
pub fn decode_allow_empty_moqt<P: MoqtProfile>(
buf: &mut impl Buf,
) -> Result<Self, crate::error::CodecError> {
let n = VarInt::decode_moqt::<P>(buf)?.into_inner() as usize;
if n > crate::error::MAX_NAMESPACE_TUPLE_SIZE {
return Err(crate::error::CodecError::InvalidNamespaceTupleSize(n));
}
Self::decode_elements_moqt::<P>(buf, n)
}
fn decode_elements_moqt<P: MoqtProfile>(
buf: &mut impl Buf,
n: usize,
) -> Result<Self, crate::error::CodecError> {
let mut elements = Vec::with_capacity(n);
let mut total = 0usize;
for _ in 0..n {
let len = VarInt::decode_moqt::<P>(buf)?.into_inner() as usize;
if len == 0 {
return Err(crate::error::CodecError::EmptyNamespaceField);
}
total = total.saturating_add(len);
if total > crate::error::MAX_FULL_TRACK_NAME_LENGTH {
return Err(crate::error::CodecError::TrackNameTooLong);
}
elements.push(read_bytes(buf, len)?);
}
Ok(TrackNamespace(elements))
}
pub fn field_bytes_len(&self) -> usize {
self.0.iter().map(|field| field.len()).sum()
}
pub fn validate_moqt(&self) -> Result<(), crate::error::CodecError> {
self.validate(TrackNamespaceRules::for_draft(17))
}
pub fn validate(&self, rules: TrackNamespaceRules) -> Result<(), crate::error::CodecError> {
if self.0.len() < rules.min_fields || self.0.len() > crate::error::MAX_NAMESPACE_TUPLE_SIZE
{
return Err(crate::error::CodecError::InvalidNamespaceTupleSize(self.0.len()));
}
if rules.reject_empty_field && self.0.iter().any(|field| field.is_empty()) {
return Err(crate::error::CodecError::EmptyNamespaceField);
}
if let Some(max) = rules.max_namespace_bytes {
if self.field_bytes_len() > max {
return Err(crate::error::CodecError::TrackNameTooLong);
}
}
Ok(())
}
}
impl Location {
pub fn encode(&self, buf: &mut impl BufMut) {
self.group.encode(buf);
self.object.encode(buf);
}
pub fn decode(buf: &mut impl Buf) -> Result<Self, crate::error::CodecError> {
let group = VarInt::decode(buf)?;
let object = VarInt::decode(buf)?;
Ok(Location { group, object })
}
pub fn encode_moqt<P: MoqtProfile>(&self, buf: &mut impl BufMut) {
self.group.encode_moqt::<P>(buf);
self.object.encode_moqt::<P>(buf);
}
pub fn decode_moqt<P: MoqtProfile>(
buf: &mut impl Buf,
) -> Result<Self, crate::error::CodecError> {
let group = VarInt::decode_moqt::<P>(buf)?;
let object = VarInt::decode_moqt::<P>(buf)?;
Ok(Location { group, object })
}
}
impl ObjectStatus {
pub const ALL: &[ObjectStatus] = &[
ObjectStatus::Normal,
ObjectStatus::DoesNotExist,
ObjectStatus::EndOfGroup,
ObjectStatus::EndOfTrack,
];
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x0 => Some(ObjectStatus::Normal),
0x1 => Some(ObjectStatus::DoesNotExist),
0x3 => Some(ObjectStatus::EndOfGroup),
0x4 => Some(ObjectStatus::EndOfTrack),
_ => None,
}
}
pub fn as_u8(self) -> u8 {
self as u8
}
}
impl GroupOrder {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x0 => Some(GroupOrder::Publisher),
0x1 => Some(GroupOrder::Ascending),
0x2 => Some(GroupOrder::Descending),
_ => None,
}
}
}
impl ForwardingPreference {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0x0 => Some(ForwardingPreference::Object),
0x1 => Some(ForwardingPreference::Datagram),
_ => None,
}
}
}
impl FilterType {
pub fn from_u8(v: u8) -> Option<Self> {
Self::from_u64(v as u64)
}
pub fn from_u64(v: u64) -> Option<Self> {
match v {
0x1 => Some(FilterType::NextGroupStart),
0x2 => Some(FilterType::LargestObject),
0x3 => Some(FilterType::AbsoluteStart),
0x4 => Some(FilterType::AbsoluteRange),
_ => None,
}
}
}