reactor_webrtc_dc_chunking/
send.rs1use std::collections::VecDeque;
12use std::fmt;
13
14use crate::frame::Header;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct SendConfig {
19 pub chunk_size: usize,
21 pub high_water: u64,
23 pub low_water: u64,
25 pub queue_limit: u64,
27 pub max_message_size: u64,
30}
31
32impl Default for SendConfig {
33 fn default() -> Self {
34 Self {
35 chunk_size: crate::DEFAULT_CHUNK_SIZE,
36 high_water: crate::DEFAULT_HIGH_WATER,
37 low_water: crate::DEFAULT_LOW_WATER,
38 queue_limit: crate::DEFAULT_SEND_BUFFER_LIMIT,
39 max_message_size: crate::DEFAULT_MAX_MESSAGE_SIZE,
40 }
41 }
42}
43
44impl SendConfig {
45 pub fn validate(&self) -> Result<(), ConfigError> {
47 if self.chunk_size < 2 || self.chunk_size as u64 > crate::LEGACY_MAX_MESSAGE_SIZE {
50 return Err(ConfigError::ChunkSize(self.chunk_size));
51 }
52 if self.low_water >= self.high_water {
53 return Err(ConfigError::Watermarks {
54 low: self.low_water,
55 high: self.high_water,
56 });
57 }
58 if self.high_water > crate::NATIVE_SEND_BUFFER_LIMIT - self.chunk_size as u64 {
62 return Err(ConfigError::HighWater(self.high_water));
63 }
64 if self.queue_limit < self.max_message_size {
65 return Err(ConfigError::QueueBelowMessage {
66 queue: self.queue_limit,
67 message: self.max_message_size,
68 });
69 }
70 Ok(())
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum ConfigError {
77 ChunkSize(usize),
78 Watermarks { low: u64, high: u64 },
79 HighWater(u64),
80 QueueBelowMessage { queue: u64, message: u64 },
81}
82
83impl fmt::Display for ConfigError {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::ChunkSize(n) => write!(f, "chunk_size {n} must be between 2 bytes and 256 KiB"),
87 Self::Watermarks { low, high } => {
88 write!(f, "low_water {low} must be below high_water {high}")
89 }
90 Self::HighWater(n) => write!(
91 f,
92 "high_water {n} plus one frame must stay under libwebrtc's 16 MiB send buffer"
93 ),
94 Self::QueueBelowMessage { queue, message } => {
95 write!(
96 f,
97 "queue_limit {queue} must be at least max_message_size {message}"
98 )
99 }
100 }
101 }
102}
103
104impl std::error::Error for ConfigError {}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum SendError {
109 TooLarge { size: u64, max: u64 },
111 QueueFull { queued: u64, size: u64, limit: u64 },
113}
114
115impl fmt::Display for SendError {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Self::TooLarge { size, max } => write!(
119 f,
120 "message of {size} bytes is larger than the {max}-byte limit"
121 ),
122 Self::QueueFull {
123 queued,
124 size,
125 limit,
126 } => {
127 write!(
128 f,
129 "queue holds {queued} bytes; adding {size} would pass its {limit}-byte limit"
130 )
131 }
132 }
133 }
134}
135
136impl std::error::Error for SendError {}
137
138struct Pending {
139 data: Vec<u8>,
140 text: bool,
141 offset: usize,
143}
144
145pub struct SendQueue {
147 config: SendConfig,
148 pending: VecDeque<Pending>,
149 queued: u64,
150}
151
152impl SendQueue {
153 pub fn new(config: SendConfig) -> Self {
160 if let Err(e) = config.validate() {
161 panic!("invalid SendConfig: {e}");
162 }
163 Self {
164 config,
165 pending: VecDeque::new(),
166 queued: 0,
167 }
168 }
169
170 pub fn config(&self) -> &SendConfig {
171 &self.config
172 }
173
174 pub fn push(&mut self, data: Vec<u8>, binary: bool) -> Result<(), SendError> {
176 let size = data.len() as u64;
177 if size > self.config.max_message_size {
178 return Err(SendError::TooLarge {
179 size,
180 max: self.config.max_message_size,
181 });
182 }
183 if self.queued + size > self.config.queue_limit {
184 return Err(SendError::QueueFull {
185 queued: self.queued,
186 size,
187 limit: self.config.queue_limit,
188 });
189 }
190 self.queued += size;
191 self.pending.push_back(Pending {
192 data,
193 text: !binary,
194 offset: 0,
195 });
196 Ok(())
197 }
198
199 pub fn next_frame(&mut self, native_buffered: u64) -> Option<Vec<u8>> {
203 if native_buffered >= self.config.high_water {
204 return None;
205 }
206 let head = self.pending.front_mut()?;
207 let room = self.config.chunk_size - 1;
208 let take = room.min(head.data.len() - head.offset);
209 let more = head.offset + take < head.data.len();
210 let mut frame = Vec::with_capacity(take + 1);
211 frame.push(
212 Header {
213 more,
214 text: head.text,
215 }
216 .encode(),
217 );
218 frame.extend_from_slice(&head.data[head.offset..head.offset + take]);
219 head.offset += take;
220 self.queued -= take as u64;
221 if !more {
222 self.pending.pop_front();
223 }
224 Some(frame)
225 }
226
227 pub fn queued(&self) -> u64 {
229 self.queued
230 }
231
232 pub fn is_empty(&self) -> bool {
233 self.pending.is_empty()
234 }
235
236 pub fn mid_message(&self) -> bool {
239 self.pending.front().is_some_and(|p| p.offset > 0)
240 }
241
242 pub fn clear(&mut self) {
244 self.pending.clear();
245 self.queued = 0;
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 fn small() -> SendConfig {
254 SendConfig {
255 chunk_size: 5,
256 high_water: 100,
257 low_water: 50,
258 queue_limit: 64,
259 max_message_size: 32,
260 }
261 }
262
263 fn drain(q: &mut SendQueue) -> Vec<Vec<u8>> {
264 std::iter::from_fn(|| q.next_frame(0)).collect()
265 }
266
267 #[test]
268 fn default_config_is_valid() {
269 assert_eq!(SendConfig::default().validate(), Ok(()));
270 }
271
272 #[test]
273 #[should_panic(expected = "invalid SendConfig: chunk_size 1")]
274 fn a_queue_refuses_a_config_that_cannot_drain() {
275 SendQueue::new(SendConfig {
276 chunk_size: 1,
277 ..SendConfig::default()
278 });
279 }
280
281 #[test]
282 fn an_extreme_high_water_is_rejected_without_overflow() {
283 let c = SendConfig {
284 high_water: u64::MAX,
285 ..SendConfig::default()
286 };
287 assert_eq!(c.validate(), Err(ConfigError::HighWater(u64::MAX)));
288 }
289
290 #[test]
291 fn rejects_inconsistent_configs() {
292 let ok = SendConfig::default();
293 assert!(SendConfig {
294 chunk_size: 1,
295 ..ok
296 }
297 .validate()
298 .is_err());
299 assert!(SendConfig {
300 chunk_size: 300 * 1024,
301 ..ok
302 }
303 .validate()
304 .is_err());
305 assert!(SendConfig {
306 low_water: ok.high_water,
307 ..ok
308 }
309 .validate()
310 .is_err());
311 assert!(SendConfig {
312 high_water: crate::NATIVE_SEND_BUFFER_LIMIT,
313 ..ok
314 }
315 .validate()
316 .is_err());
317 assert!(SendConfig {
318 queue_limit: ok.max_message_size - 1,
319 ..ok
320 }
321 .validate()
322 .is_err());
323 }
324
325 #[test]
326 fn splits_into_frames_of_chunk_size_with_header() {
327 let mut q = SendQueue::new(small());
328 q.push(b"abcdefghij".to_vec(), true).unwrap();
329 assert_eq!(
331 drain(&mut q),
332 vec![
333 b"\x01abcd".to_vec(),
334 b"\x01efgh".to_vec(),
335 b"\x00ij".to_vec()
336 ]
337 );
338 assert!(q.is_empty());
339 assert_eq!(q.queued(), 0);
340 }
341
342 #[test]
343 fn exact_multiple_ends_with_a_full_last_frame() {
344 let mut q = SendQueue::new(small());
345 q.push(b"abcdefgh".to_vec(), true).unwrap();
346 assert_eq!(
347 drain(&mut q),
348 vec![b"\x01abcd".to_vec(), b"\x00efgh".to_vec()]
349 );
350 }
351
352 #[test]
353 fn empty_message_is_one_header_only_frame() {
354 let mut q = SendQueue::new(small());
355 q.push(Vec::new(), false).unwrap();
356 assert_eq!(drain(&mut q), vec![vec![0x02]]);
357 }
358
359 #[test]
360 fn text_flag_is_on_every_frame() {
361 let mut q = SendQueue::new(small());
362 q.push(b"hello".to_vec(), false).unwrap();
363 assert_eq!(drain(&mut q), vec![b"\x03hell".to_vec(), b"\x02o".to_vec()]);
364 }
365
366 #[test]
367 fn messages_leave_in_order_and_never_interleave() {
368 let mut q = SendQueue::new(small());
369 q.push(b"aaaaaa".to_vec(), true).unwrap();
370 q.push(b"bb".to_vec(), true).unwrap();
371 assert_eq!(
372 drain(&mut q),
373 vec![b"\x01aaaa".to_vec(), b"\x00aa".to_vec(), b"\x00bb".to_vec()]
374 );
375 }
376
377 #[test]
378 fn holds_frames_at_the_high_water_mark() {
379 let mut q = SendQueue::new(small());
380 q.push(b"abcdefgh".to_vec(), true).unwrap();
381 assert_eq!(q.next_frame(100), None);
382 assert_eq!(q.next_frame(99), Some(b"\x01abcd".to_vec()));
383 assert!(q.mid_message());
384 assert_eq!(q.queued(), 4);
385 }
386
387 #[test]
388 fn rejects_oversized_messages_and_a_full_queue() {
389 let mut q = SendQueue::new(small());
390 assert_eq!(
391 q.push(vec![0; 33], true),
392 Err(SendError::TooLarge { size: 33, max: 32 })
393 );
394 q.push(vec![0; 32], true).unwrap();
395 q.push(vec![0; 32], true).unwrap();
396 assert_eq!(
397 q.push(vec![0; 1], true),
398 Err(SendError::QueueFull {
399 queued: 64,
400 size: 1,
401 limit: 64
402 })
403 );
404 assert_eq!(q.queued(), 64);
405 }
406
407 #[test]
408 fn clear_drops_everything() {
409 let mut q = SendQueue::new(small());
410 q.push(vec![1; 10], true).unwrap();
411 q.next_frame(0);
412 q.clear();
413 assert!(q.is_empty());
414 assert!(!q.mid_message());
415 assert_eq!(q.queued(), 0);
416 }
417}