use std::cmp::min;
use bit_cursor::nsw_types::*;
use crate::rtcp::rtcp_fb_tcc::{
PacketStatusSymbol, RunLengthEncodingChunk, SomePacketStatusChunk, StatusVectorChunk,
};
const MAX_ONE_BIT_CAPACITY: usize = 14;
const MAX_TWO_BIT_CAPACITY: usize = 7;
const MAX_RUN_LENGTH_CAPACITY: usize = 0x1FFF;
pub(crate) struct Chunk {
all_same: bool,
has_large_delta: bool,
symbols: Vec<PacketStatusSymbol>,
}
impl Default for Chunk {
fn default() -> Self {
Self {
all_same: true,
has_large_delta: false,
symbols: Default::default(),
}
}
}
impl Chunk {
pub(crate) fn is_empty(&self) -> bool {
self.symbols.is_empty()
}
pub(crate) fn clear(&mut self) {
self.all_same = true;
self.has_large_delta = false;
self.symbols = Default::default();
}
pub(crate) fn can_add(&self, symbol: PacketStatusSymbol) -> bool {
if self.symbols.len() < MAX_TWO_BIT_CAPACITY {
return true;
}
if self.symbols.len() < MAX_ONE_BIT_CAPACITY
&& !self.has_large_delta
&& symbol != PacketStatusSymbol::ReceivedLargeOrNegativeDelta
{
return true;
}
if self.symbols.len() < MAX_RUN_LENGTH_CAPACITY
&& self.all_same
&& self.symbols[0] == symbol
{
return true;
}
false
}
pub(crate) fn add(&mut self, symbol: PacketStatusSymbol) {
self.symbols.push(symbol);
self.all_same = self.all_same && self.symbols[0] == symbol;
self.has_large_delta =
self.has_large_delta || symbol == PacketStatusSymbol::ReceivedLargeOrNegativeDelta;
}
pub(crate) fn add_missing_packets(&mut self, num_missing: usize) {
for i in 0..num_missing {
self.symbols.push(PacketStatusSymbol::NotReceived);
}
}
pub(crate) fn emit(&mut self) -> SomePacketStatusChunk {
if self.all_same {
let chunk = SomePacketStatusChunk::RunLengthEncodingChunk(self.encode_run_length());
self.clear();
return chunk;
}
if self.symbols.len() == MAX_ONE_BIT_CAPACITY {
let chunk = SomePacketStatusChunk::StatusVectorChunk(self.encode_one_bit());
self.clear();
return chunk;
}
let chunk =
SomePacketStatusChunk::StatusVectorChunk(self.encode_two_bit(MAX_TWO_BIT_CAPACITY));
self.symbols
.drain(0..(min(MAX_TWO_BIT_CAPACITY, self.symbols.len())));
self.all_same = self.symbols.iter().all(|s| s == &self.symbols[0]);
self.has_large_delta = self
.symbols
.iter()
.any(|s| s == &PacketStatusSymbol::ReceivedLargeOrNegativeDelta);
chunk
}
fn encode_run_length(&self) -> RunLengthEncodingChunk {
RunLengthEncodingChunk {
symbol: self.symbols[0],
run_length: u13::new(self.symbols.len() as u16),
}
}
fn encode_one_bit(&self) -> StatusVectorChunk {
StatusVectorChunk(self.symbols.clone())
}
fn encode_two_bit(&self, size: usize) -> StatusVectorChunk {
let symbols = self.symbols.iter().cloned().take(size).collect();
StatusVectorChunk(symbols)
}
}
#[cfg(test)]
mod test {
use crate::rtcp::{
rtcp_fb_tcc::{PacketStatusSymbol, SomePacketStatusChunk},
tcc::chunk::MAX_ONE_BIT_CAPACITY,
};
use super::{Chunk, MAX_RUN_LENGTH_CAPACITY};
#[test]
fn test_chunk_default() {
let chunk = Chunk::default();
assert!(chunk.is_empty());
}
#[test]
fn test_chunk_one_bit() {
let mut chunk = Chunk::default();
assert!(chunk.can_add(PacketStatusSymbol::NotReceived));
chunk.add(PacketStatusSymbol::NotReceived);
for _ in 1..14 {
assert!(chunk.can_add(PacketStatusSymbol::ReceivedSmallDelta));
chunk.add(PacketStatusSymbol::ReceivedSmallDelta);
}
assert!(!chunk.can_add(PacketStatusSymbol::NotReceived));
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), 14);
let SomePacketStatusChunk::StatusVectorChunk(sv_chunk) = encoded else {
panic!("Expected status vector chunk");
};
assert_eq!(sv_chunk.0[0], PacketStatusSymbol::NotReceived);
for i in 1..14 {
assert_eq!(sv_chunk.0[i], PacketStatusSymbol::ReceivedSmallDelta);
}
}
#[test]
fn test_chunk_two_bit() {
let mut chunk = Chunk::default();
assert!(chunk.can_add(PacketStatusSymbol::ReceivedLargeOrNegativeDelta));
chunk.add(PacketStatusSymbol::ReceivedLargeOrNegativeDelta);
for _ in 0..6 {
assert!(chunk.can_add(PacketStatusSymbol::ReceivedSmallDelta));
chunk.add(PacketStatusSymbol::ReceivedSmallDelta);
}
assert!(!chunk.can_add(PacketStatusSymbol::ReceivedSmallDelta));
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), 7);
let SomePacketStatusChunk::StatusVectorChunk(sv_chunk) = encoded else {
panic!("Expected status vector chunk");
};
assert_eq!(
sv_chunk.0[0],
PacketStatusSymbol::ReceivedLargeOrNegativeDelta
);
for i in 1..7 {
assert_eq!(sv_chunk.0[i], PacketStatusSymbol::ReceivedSmallDelta);
}
}
#[test]
fn test_chunk_partial() {
let mut chunk = Chunk::default();
chunk.add(PacketStatusSymbol::NotReceived);
for _ in 1..5 {
chunk.add(PacketStatusSymbol::ReceivedSmallDelta);
}
assert!(chunk.can_add(PacketStatusSymbol::NotReceived));
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), 5);
let SomePacketStatusChunk::StatusVectorChunk(sv_chunk) = encoded else {
panic!("Expected status vector chunk");
};
assert_eq!(sv_chunk.0[0], PacketStatusSymbol::NotReceived);
for i in 1..5 {
assert_eq!(sv_chunk.0[i], PacketStatusSymbol::ReceivedSmallDelta);
}
}
#[test]
fn test_chunk_leftover() {
let mut chunk = Chunk::default();
chunk.add(PacketStatusSymbol::NotReceived);
for _ in 1..9 {
chunk.add(PacketStatusSymbol::ReceivedSmallDelta);
}
chunk.add(PacketStatusSymbol::NotReceived);
assert!(chunk.can_add(PacketStatusSymbol::NotReceived));
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), 7);
let SomePacketStatusChunk::StatusVectorChunk(sv_chunk) = encoded else {
panic!("Expected status vector chunk");
};
assert_eq!(sv_chunk.0[0], PacketStatusSymbol::NotReceived);
for i in 1..7 {
assert_eq!(sv_chunk.0[i], PacketStatusSymbol::ReceivedSmallDelta);
}
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), 3);
let SomePacketStatusChunk::StatusVectorChunk(sv_chunk) = encoded else {
panic!("Expected status vector chunk");
};
assert_eq!(sv_chunk.0[0], PacketStatusSymbol::ReceivedSmallDelta);
assert_eq!(sv_chunk.0[1], PacketStatusSymbol::ReceivedSmallDelta);
assert_eq!(sv_chunk.0[2], PacketStatusSymbol::NotReceived);
}
#[test]
fn test_chunk_run_length() {
let mut chunk = Chunk::default();
for i in 0..MAX_RUN_LENGTH_CAPACITY {
assert!(chunk.can_add(PacketStatusSymbol::ReceivedLargeOrNegativeDelta));
if i > MAX_ONE_BIT_CAPACITY {
assert!(!chunk.can_add(PacketStatusSymbol::NotReceived));
}
chunk.add(PacketStatusSymbol::ReceivedLargeOrNegativeDelta);
}
assert!(!chunk.can_add(PacketStatusSymbol::ReceivedLargeOrNegativeDelta));
let encoded = chunk.emit();
assert_eq!(encoded.num_symbols(), MAX_RUN_LENGTH_CAPACITY as u16);
}
}