#[derive(Debug, Clone)]
pub struct QuicDatagramConfig {
pub max_datagram_size: usize,
pub congestion_window: u32,
}
impl Default for QuicDatagramConfig {
fn default() -> Self {
Self {
max_datagram_size: 1200,
congestion_window: 65536,
}
}
}
impl QuicDatagramConfig {
#[must_use]
pub fn max_datagrams_in_flight(&self) -> u32 {
if self.max_datagram_size == 0 {
return 0;
}
self.congestion_window / self.max_datagram_size as u32
}
pub fn validate(&self) -> Result<(), String> {
if self.max_datagram_size == 0 {
return Err("max_datagram_size must be > 0".to_owned());
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct QuicDatagram {
pub stream_id: u64,
pub sequence: u64,
pub data: Vec<u8>,
pub priority: u8,
}
impl QuicDatagram {
#[must_use]
pub fn size(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
pub struct QuicDatagramSender {
pub config: QuicDatagramConfig,
pub sequence: u64,
}
impl QuicDatagramSender {
#[must_use]
pub fn new(config: QuicDatagramConfig) -> Self {
Self {
config,
sequence: 0,
}
}
#[must_use]
pub fn with_defaults() -> Self {
Self::new(QuicDatagramConfig::default())
}
#[must_use]
pub fn next_datagram(&mut self, data: Vec<u8>, stream_id: u64) -> QuicDatagram {
self.next_datagram_with_priority(data, stream_id, 128)
}
#[must_use]
pub fn next_datagram_with_priority(
&mut self,
data: Vec<u8>,
stream_id: u64,
priority: u8,
) -> QuicDatagram {
let seq = self.sequence;
self.sequence = self.sequence.wrapping_add(1);
QuicDatagram {
stream_id,
sequence: seq,
data,
priority,
}
}
#[must_use]
pub fn datagrams_sent(&self) -> u64 {
self.sequence
}
pub fn reset(&mut self) {
self.sequence = 0;
}
}
#[must_use]
pub fn should_fragment(data: &[u8], config: &QuicDatagramConfig) -> bool {
config.max_datagram_size > 0 && data.len() > config.max_datagram_size
}
#[must_use]
pub fn fragment(data: &[u8], max_size: usize) -> Vec<Vec<u8>> {
if data.is_empty() {
return Vec::new();
}
if max_size == 0 {
return vec![data.to_vec()];
}
data.chunks(max_size).map(|chunk| chunk.to_vec()).collect()
}
#[must_use]
pub fn reassemble(fragments: &[Vec<u8>]) -> Vec<u8> {
let total: usize = fragments.iter().map(Vec::len).sum();
let mut buf = Vec::with_capacity(total);
for frag in fragments {
buf.extend_from_slice(frag);
}
buf
}
#[cfg(test)]
mod tests {
use super::*;
fn small_config() -> QuicDatagramConfig {
QuicDatagramConfig {
max_datagram_size: 100,
congestion_window: 1000,
}
}
#[test]
fn test_default_max_datagram_size() {
let cfg = QuicDatagramConfig::default();
assert_eq!(cfg.max_datagram_size, 1200);
}
#[test]
fn test_default_congestion_window() {
let cfg = QuicDatagramConfig::default();
assert_eq!(cfg.congestion_window, 65536);
}
#[test]
fn test_max_datagrams_in_flight() {
let cfg = small_config(); assert_eq!(cfg.max_datagrams_in_flight(), 10);
}
#[test]
fn test_validate_zero_size() {
let cfg = QuicDatagramConfig {
max_datagram_size: 0,
congestion_window: 1000,
};
assert!(cfg.validate().is_err());
}
#[test]
fn test_validate_valid() {
assert!(QuicDatagramConfig::default().validate().is_ok());
}
#[test]
fn test_sender_sequence_starts_at_zero() {
let sender = QuicDatagramSender::new(QuicDatagramConfig::default());
assert_eq!(sender.sequence, 0);
}
#[test]
fn test_next_datagram_increments_sequence() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
let d0 = sender.next_datagram(vec![1, 2, 3], 10);
let d1 = sender.next_datagram(vec![4, 5, 6], 10);
assert_eq!(d0.sequence, 0);
assert_eq!(d1.sequence, 1);
}
#[test]
fn test_next_datagram_stream_id() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
let dg = sender.next_datagram(vec![0u8; 50], 42);
assert_eq!(dg.stream_id, 42);
}
#[test]
fn test_next_datagram_default_priority() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
let dg = sender.next_datagram(vec![0], 1);
assert_eq!(dg.priority, 128);
}
#[test]
fn test_next_datagram_custom_priority() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
let dg = sender.next_datagram_with_priority(vec![0], 1, 0);
assert_eq!(dg.priority, 0);
}
#[test]
fn test_datagrams_sent() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
for _ in 0..5 {
sender.next_datagram(vec![], 1);
}
assert_eq!(sender.datagrams_sent(), 5);
}
#[test]
fn test_reset_sequence() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
sender.next_datagram(vec![], 1);
sender.next_datagram(vec![], 1);
sender.reset();
assert_eq!(sender.sequence, 0);
}
#[test]
fn test_should_fragment_small_data() {
let cfg = small_config();
let data = vec![0u8; 50]; assert!(!should_fragment(&data, &cfg));
}
#[test]
fn test_should_fragment_exact_max() {
let cfg = small_config();
let data = vec![0u8; 100]; assert!(!should_fragment(&data, &cfg));
}
#[test]
fn test_should_fragment_large_data() {
let cfg = small_config();
let data = vec![0u8; 101];
assert!(should_fragment(&data, &cfg));
}
#[test]
fn test_fragment_splits_into_chunks() {
let data = vec![0u8; 250];
let chunks = fragment(&data, 100);
assert_eq!(chunks.len(), 3); assert_eq!(chunks[0].len(), 100);
assert_eq!(chunks[1].len(), 100);
assert_eq!(chunks[2].len(), 50);
}
#[test]
fn test_fragment_exact_divisor() {
let data = vec![1u8; 400];
let chunks = fragment(&data, 100);
assert_eq!(chunks.len(), 4);
assert!(chunks.iter().all(|c| c.len() == 100));
}
#[test]
fn test_fragment_empty_data() {
let chunks = fragment(&[], 100);
assert!(chunks.is_empty());
}
#[test]
fn test_reassemble_roundtrip() {
let original: Vec<u8> = (0..=255u8).collect();
let chunks = fragment(&original, 64);
let recovered = reassemble(&chunks);
assert_eq!(recovered, original);
}
#[test]
fn test_quic_datagram_size_is_empty() {
let dg_empty = QuicDatagram {
stream_id: 0,
sequence: 0,
data: vec![],
priority: 128,
};
assert!(dg_empty.is_empty());
assert_eq!(dg_empty.size(), 0);
let dg_full = QuicDatagram {
stream_id: 0,
sequence: 1,
data: vec![1, 2, 3],
priority: 64,
};
assert!(!dg_full.is_empty());
assert_eq!(dg_full.size(), 3);
}
#[test]
fn test_with_defaults() {
let sender = QuicDatagramSender::with_defaults();
assert_eq!(sender.config.max_datagram_size, 1200);
assert_eq!(sender.sequence, 0);
}
#[test]
fn test_multiple_stream_ids() {
let mut sender = QuicDatagramSender::new(QuicDatagramConfig::default());
let d_video = sender.next_datagram(vec![0], 1);
let d_audio = sender.next_datagram(vec![0], 2);
assert_eq!(d_video.stream_id, 1);
assert_eq!(d_audio.stream_id, 2);
assert_eq!(d_video.sequence, 0);
assert_eq!(d_audio.sequence, 1);
}
#[test]
fn test_should_fragment_empty_is_false() {
let cfg = small_config();
assert!(!should_fragment(&[], &cfg));
}
}