1use std::sync::Arc;
2
3use bitfold_core::shared::SharedBytes;
4use bitfold_protocol::command::ProtocolCommand;
5
6use super::Peer;
7
8impl Peer {
9 pub fn enqueue_reliable_data(&mut self, channel_id: u8, data: Arc<[u8]>, ordered: bool) -> u16 {
12 let sequence = self.acknowledge_handler.local_sequence_num();
13
14 let datagram_cap = std::cmp::min(
17 self.current_fragment_size() as usize,
18 self.config.receive_buffer_max_size,
19 );
20 let compression_overhead = match self.config.compression {
22 bitfold_core::config::CompressionAlgorithm::Lz4 => 5, _ => 1, };
25 let checksum_overhead = if self.config.use_checksums { 4 } else { 0 };
26 let per_packet_overhead = 1 + compression_overhead + checksum_overhead;
27
28 let send_reliable_header = 1 + 1 + 2 + 1 + 2 ; let send_fragment_header = 1 + 1 + 2 + 1 + 1 + 1 + 2 ; let max_payload_reliable = datagram_cap
36 .saturating_sub(per_packet_overhead)
37 .saturating_sub(2 )
38 .saturating_sub(send_reliable_header);
39
40 if max_payload_reliable < 1 {
42 tracing::error!(
43 "MTU too small: datagram_cap={}, overhead={}, can't fit minimum payload",
44 datagram_cap,
45 per_packet_overhead + 2 + send_reliable_header
46 );
47 return sequence;
49 }
50
51 if data.len() <= max_payload_reliable {
52 self.enqueue_command(ProtocolCommand::SendReliable {
54 channel_id,
55 sequence,
56 ordered,
57 data: SharedBytes::from_arc(data),
58 });
59 } else {
60 let fragment_payload = datagram_cap
62 .saturating_sub(per_packet_overhead)
63 .saturating_sub(2 )
64 .saturating_sub(send_fragment_header);
65
66 if fragment_payload < 1 {
67 tracing::error!(
68 "MTU too small for fragmentation: datagram_cap={}, overhead={}, can't fit minimum fragment payload",
69 datagram_cap,
70 per_packet_overhead + 2 + send_fragment_header
71 );
72 return sequence;
73 }
74
75 let total_fragments_usize = data.len().div_ceil(fragment_payload);
77 if total_fragments_usize > u8::MAX as usize {
78 tracing::warn!(
79 "Payload {} bytes too large to fragment: would require {} fragments (max {}), dropping packet",
80 data.len(),
81 total_fragments_usize,
82 u8::MAX
83 );
84 return sequence;
85 }
86 let total_fragments = total_fragments_usize as u8;
87
88 if total_fragments > self.config.max_fragments {
89 tracing::warn!(
90 "Payload requires {} fragments but max allowed is {}, sending first fragment only",
91 total_fragments,
92 self.config.max_fragments
93 );
94 let base = SharedBytes::from_arc(data);
96 let fragment_data = base.slice(0, fragment_payload.min(base.len()));
97 self.enqueue_command(ProtocolCommand::SendFragment {
98 channel_id,
99 sequence,
100 ordered,
101 fragment_id: 0,
102 fragment_count: 1,
103 data: fragment_data,
104 });
105 return sequence;
106 }
107
108 tracing::trace!(
109 "Fragmenting {} byte payload into {} fragments ({} bytes each)",
110 data.len(),
111 total_fragments,
112 fragment_payload
113 );
114
115 for fragment_id in 0..total_fragments {
116 let start = (fragment_id as usize) * fragment_payload;
117 let end = ((fragment_id as usize + 1) * fragment_payload).min(data.len());
118 let base = SharedBytes::from_arc(data.clone());
119 let fragment_data = base.slice(start, end - start);
120 self.enqueue_command(ProtocolCommand::SendFragment {
121 channel_id,
122 sequence,
123 ordered,
124 fragment_id,
125 fragment_count: total_fragments,
126 data: fragment_data,
127 });
128 }
129 }
130
131 sequence
132 }
133
134 pub fn enqueue_unreliable_data(&mut self, channel_id: u8, data: Arc<[u8]>) -> u16 {
137 let sequence = self.next_unreliable_sequence;
139 self.next_unreliable_sequence = self.next_unreliable_sequence.wrapping_add(1);
140
141 let datagram_cap = std::cmp::min(
144 self.current_fragment_size() as usize,
145 self.config.receive_buffer_max_size,
146 );
147 let compression_overhead = match self.config.compression {
148 bitfold_core::config::CompressionAlgorithm::Lz4 => 5,
149 _ => 1,
150 };
151 let checksum_overhead = if self.config.use_checksums { 4 } else { 0 };
152 let per_packet_overhead = 1 + compression_overhead + checksum_overhead;
153
154 let send_unrel_header = 1 + 1 + 2 ; let send_unrel_frag_header = 1 + 1 + 2 + 1 + 1 + 2 ; let max_payload_unreliable = datagram_cap
160 .saturating_sub(per_packet_overhead)
161 .saturating_sub(2 )
162 .saturating_sub(send_unrel_header);
163
164 if max_payload_unreliable < 1 {
166 tracing::error!(
167 "MTU too small: datagram_cap={}, overhead={}, can't fit minimum unreliable payload",
168 datagram_cap,
169 per_packet_overhead + 2 + send_unrel_header
170 );
171 return sequence;
172 }
173
174 if data.len() <= max_payload_unreliable {
175 self.enqueue_command(ProtocolCommand::SendUnreliable {
177 channel_id,
178 data: SharedBytes::from_arc(data),
179 });
180 } else {
181 let fragment_payload = datagram_cap
183 .saturating_sub(per_packet_overhead)
184 .saturating_sub(2 )
185 .saturating_sub(send_unrel_frag_header);
186
187 if fragment_payload < 1 {
188 tracing::error!(
189 "MTU too small for unreliable fragmentation: datagram_cap={}, overhead={}, can't fit minimum fragment payload",
190 datagram_cap,
191 per_packet_overhead + 2 + send_unrel_frag_header
192 );
193 return sequence;
194 }
195
196 let total_fragments_usize = data.len().div_ceil(fragment_payload);
198 if total_fragments_usize > u8::MAX as usize {
199 tracing::warn!(
200 "Unreliable payload {} bytes too large to fragment: would require {} fragments (max {}), dropping packet",
201 data.len(),
202 total_fragments_usize,
203 u8::MAX
204 );
205 return sequence;
206 }
207 let total_fragments = total_fragments_usize as u8;
208
209 if total_fragments > self.config.max_fragments {
210 tracing::warn!(
211 "Unreliable payload requires {} fragments but max allowed is {}, sending first fragment only",
212 total_fragments,
213 self.config.max_fragments
214 );
215 let base = SharedBytes::from_arc(data);
217 let fragment_data = base.slice(0, fragment_payload.min(base.len()));
218 self.enqueue_command(ProtocolCommand::SendUnreliableFragment {
219 channel_id,
220 sequence,
221 fragment_id: 0,
222 fragment_count: 1,
223 data: fragment_data,
224 });
225 return sequence;
226 }
227
228 tracing::trace!(
229 "Fragmenting {} byte unreliable payload into {} fragments ({} bytes each)",
230 data.len(),
231 total_fragments,
232 fragment_payload
233 );
234
235 for fragment_id in 0..total_fragments {
236 let start = (fragment_id as usize) * fragment_payload;
237 let end = ((fragment_id as usize + 1) * fragment_payload).min(data.len());
238 let base = SharedBytes::from_arc(data.clone());
239 let fragment_data = base.slice(start, end - start);
240
241 self.enqueue_command(ProtocolCommand::SendUnreliableFragment {
242 channel_id,
243 sequence,
244 fragment_id,
245 fragment_count: total_fragments,
246 data: fragment_data,
247 });
248 }
249 }
250
251 sequence
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use std::time::Instant;
258
259 use bitfold_core::config::Config;
260 use bitfold_protocol::{command::ProtocolCommand, packet::DeliveryGuarantee};
261
262 use super::*;
263
264 fn create_virtual_connection() -> Peer {
265 Peer::new(get_fake_addr(), &Config::default(), Instant::now())
266 }
267
268 fn get_fake_addr() -> std::net::SocketAddr {
269 "127.0.0.1:0".parse().unwrap()
270 }
271
272 #[test]
275 fn test_enqueue_reliable_data_with_fragmentation() {
276 let mut peer = create_virtual_connection();
277
278 let large_data = vec![42u8; 3000];
280
281 let _sequence = peer.enqueue_reliable_data(0, large_data.into(), true);
283
284 assert!(peer.has_queued_commands());
286 let count = peer.queued_commands_count();
287 assert!(count >= 3); let commands: Vec<_> = peer.drain_commands().collect();
291 for cmd in &commands {
292 assert!(matches!(cmd, ProtocolCommand::SendFragment { .. }));
293 }
294 }
295
296 #[test]
297 fn test_fragment_reassembly() {
298 let mut peer = create_virtual_connection();
299 let time = Instant::now();
300
301 let fragment1 = std::sync::Arc::<[u8]>::from(vec![1, 2, 3].into_boxed_slice());
303 let fragment2 = std::sync::Arc::<[u8]>::from(vec![4, 5, 6].into_boxed_slice());
304 let fragment3 = std::sync::Arc::<[u8]>::from(vec![7, 8, 9].into_boxed_slice());
305
306 let cmd1 = ProtocolCommand::SendFragment {
308 channel_id: 0,
309 sequence: 0,
310 ordered: true,
311 fragment_id: 0,
312 fragment_count: 3,
313 data: fragment1.into(),
314 };
315
316 let cmd2 = ProtocolCommand::SendFragment {
317 channel_id: 0,
318 sequence: 0,
319 ordered: true,
320 fragment_id: 1,
321 fragment_count: 3,
322 data: fragment2.into(),
323 };
324
325 let cmd3 = ProtocolCommand::SendFragment {
326 channel_id: 0,
327 sequence: 0,
328 ordered: true,
329 fragment_id: 2,
330 fragment_count: 3,
331 data: fragment3.into(),
332 };
333
334 let result1 = peer.process_command(&cmd1, time).unwrap();
336 assert_eq!(result1.into_iter().count(), 0);
337
338 let result2 = peer.process_command(&cmd2, time).unwrap();
339 assert_eq!(result2.into_iter().count(), 0);
340
341 let result3 = peer.process_command(&cmd3, time).unwrap();
343 let packets: Vec<_> = result3.into_iter().collect();
344 assert_eq!(packets.len(), 1);
345
346 assert_eq!(packets[0].0.payload(), &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
348
349 assert!(peer.has_queued_commands());
351 }
352
353 #[test]
354 fn test_round_trip_large_data_with_fragmentation() {
355 let mut peer1 = create_virtual_connection();
356 let mut peer2 = create_virtual_connection();
357 let time = Instant::now();
358
359 let large_data = vec![99u8; 2500];
361
362 peer1.enqueue_reliable_data(0, large_data.clone().into(), true);
364 let bytes = peer1.encode_queued_commands().unwrap();
365
366 let result = peer2.process_command_packet(&bytes, time).unwrap();
368 let packets: Vec<_> = result.into_iter().collect();
369
370 assert_eq!(packets.len(), 1);
372 assert_eq!(packets[0].0.payload(), &large_data[..]);
373
374 assert!(peer2.has_queued_commands());
376 }
377
378 #[test]
379 fn test_fragment_out_of_order_delivery() {
380 let mut peer = create_virtual_connection();
381 let time = Instant::now();
382
383 let cmd2 = ProtocolCommand::SendFragment {
385 channel_id: 0,
386 sequence: 0,
387 ordered: true,
388 fragment_id: 1,
389 fragment_count: 3,
390 data: vec![20, 21, 22].into(),
391 };
392
393 let cmd0 = ProtocolCommand::SendFragment {
394 channel_id: 0,
395 sequence: 0,
396 ordered: true,
397 fragment_id: 0,
398 fragment_count: 3,
399 data: vec![10, 11, 12].into(),
400 };
401
402 let cmd1 = ProtocolCommand::SendFragment {
403 channel_id: 0,
404 sequence: 0,
405 ordered: true,
406 fragment_id: 2,
407 fragment_count: 3,
408 data: vec![30, 31, 32].into(),
409 };
410
411 peer.process_command(&cmd2, time).unwrap();
413 peer.process_command(&cmd0, time).unwrap();
414 let result = peer.process_command(&cmd1, time).unwrap();
415
416 let packets: Vec<_> = result.into_iter().collect();
417 assert_eq!(packets.len(), 1);
418
419 assert_eq!(packets[0].0.payload(), &[10, 11, 12, 20, 21, 22, 30, 31, 32]);
421 }
422
423 #[test]
426 fn test_enqueue_unreliable_data_with_fragmentation() {
427 let mut peer = create_virtual_connection();
428
429 let large_data = vec![99u8; 3000];
431
432 let _sequence = peer.enqueue_unreliable_data(0, large_data.into());
434
435 assert!(peer.has_queued_commands());
437 let count = peer.queued_commands_count();
438 assert!(count >= 3); let commands: Vec<_> = peer.drain_commands().collect();
442 for cmd in &commands {
443 assert!(matches!(cmd, ProtocolCommand::SendUnreliableFragment { .. }));
444 }
445 }
446
447 #[test]
448 fn test_unreliable_fragment_reassembly() {
449 let config = Config::default();
450 let mut peer = Peer::new(get_fake_addr(), &config, Instant::now());
451
452 let fragment1 = vec![1, 2, 3];
454 let fragment2 = vec![4, 5, 6];
455 let fragment3 = vec![7, 8, 9];
456
457 let cmd1 = ProtocolCommand::SendUnreliableFragment {
459 channel_id: 0,
460 sequence: 1,
461 fragment_id: 0,
462 fragment_count: 3,
463 data: fragment1.into(),
464 };
465
466 let cmd2 = ProtocolCommand::SendUnreliableFragment {
467 channel_id: 0,
468 sequence: 1,
469 fragment_id: 1,
470 fragment_count: 3,
471 data: fragment2.into(),
472 };
473
474 let cmd3 = ProtocolCommand::SendUnreliableFragment {
475 channel_id: 0,
476 sequence: 1,
477 fragment_id: 2,
478 fragment_count: 3,
479 data: fragment3.into(),
480 };
481
482 let time = Instant::now();
483
484 let result1 = peer.process_command(&cmd1, time).unwrap();
486 assert_eq!(result1.into_iter().count(), 0);
487
488 let result2 = peer.process_command(&cmd2, time).unwrap();
489 assert_eq!(result2.into_iter().count(), 0);
490
491 let result3 = peer.process_command(&cmd3, time).unwrap();
493 let packets: Vec<_> = result3.into_iter().collect();
494 assert_eq!(packets.len(), 1);
495
496 let (packet, _) = &packets[0];
497 assert_eq!(packet.channel_id(), 0);
498 assert_eq!(packet.payload(), &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
499 assert_eq!(packet.delivery_guarantee(), DeliveryGuarantee::Unreliable);
500 }
501
502 #[test]
503 fn test_unreliable_fragment_out_of_order_delivery() {
504 let config = Config::default();
505 let mut peer = Peer::new(get_fake_addr(), &config, Instant::now());
506 let time = Instant::now();
507
508 let cmd2 = ProtocolCommand::SendUnreliableFragment {
510 channel_id: 0,
511 sequence: 5,
512 fragment_id: 1,
513 fragment_count: 3,
514 data: vec![20, 21, 22].into(),
515 };
516
517 let cmd0 = ProtocolCommand::SendUnreliableFragment {
518 channel_id: 0,
519 sequence: 5,
520 fragment_id: 0,
521 fragment_count: 3,
522 data: vec![10, 11, 12].into(),
523 };
524
525 let cmd1 = ProtocolCommand::SendUnreliableFragment {
526 channel_id: 0,
527 sequence: 5,
528 fragment_id: 2,
529 fragment_count: 3,
530 data: vec![30, 31, 32].into(),
531 };
532
533 let result1 = peer.process_command(&cmd2, time).unwrap();
535 assert_eq!(result1.into_iter().count(), 0);
536
537 let result2 = peer.process_command(&cmd0, time).unwrap();
538 assert_eq!(result2.into_iter().count(), 0);
539
540 let result3 = peer.process_command(&cmd1, time).unwrap();
542 let packets: Vec<_> = result3.into_iter().collect();
543 assert_eq!(packets.len(), 1);
544
545 let (packet, _) = &packets[0];
546 assert_eq!(packet.payload(), &[10, 11, 12, 20, 21, 22, 30, 31, 32]);
548 }
549
550 #[test]
551 fn test_round_trip_unreliable_large_data_with_fragmentation() {
552 let config = Config::default();
553 let mut peer1 = Peer::new(get_fake_addr(), &config, Instant::now());
554 let mut peer2 = Peer::new(get_fake_addr(), &config, Instant::now());
555 let time = Instant::now();
556
557 let large_data = vec![123u8; 2500];
559
560 peer1.enqueue_unreliable_data(0, large_data.clone().into());
562 let bytes = peer1.encode_queued_commands().unwrap();
563
564 let result = peer2.process_command_packet(&bytes, time).unwrap();
566 let packets: Vec<_> = result.into_iter().collect();
567
568 assert_eq!(packets.len(), 1);
570
571 let (packet, _) = &packets[0];
572 assert_eq!(packet.payload(), large_data.as_slice());
573 assert_eq!(packet.delivery_guarantee(), DeliveryGuarantee::Unreliable);
574 }
575
576 #[test]
577 fn test_unreliable_fragments_no_ack_sent() {
578 let config = Config::default();
579 let mut peer1 = Peer::new(get_fake_addr(), &config, Instant::now());
580 let mut peer2 = Peer::new(get_fake_addr(), &config, Instant::now());
581 let time = Instant::now();
582
583 peer1.enqueue_unreliable_data(0, vec![55u8; 2000].into());
585 let bytes = peer1.encode_queued_commands().unwrap();
586
587 let _ = peer2.process_command_packet(&bytes, time).unwrap();
589
590 assert!(
592 !peer2.has_queued_commands() || {
593 let commands: Vec<_> = peer2.drain_commands().collect();
595 !commands.iter().any(|cmd| matches!(cmd, ProtocolCommand::Acknowledge { .. }))
596 }
597 );
598 }
599
600 #[test]
603 fn test_stale_fragment_cleanup() {
604 let config = Config::default();
605 let start_time = Instant::now();
606 let mut peer = Peer::new(get_fake_addr(), &config, start_time);
607
608 let fragment1 = vec![1, 2, 3];
610 let cmd = ProtocolCommand::SendFragment {
611 channel_id: 0,
612 sequence: 100,
613 ordered: true,
614 fragment_id: 0,
615 fragment_count: 3, data: fragment1.into(),
617 };
618
619 let result = peer.process_command(&cmd, start_time).unwrap();
621 assert_eq!(result.into_iter().count(), 0); assert_eq!(peer.command_fragments.len(), 1);
625
626 peer.cleanup_stale_fragments(start_time);
628 assert_eq!(peer.command_fragments.len(), 1, "Fragment buffer should still exist");
629
630 let later = start_time + std::time::Duration::from_secs(6);
632 peer.cleanup_stale_fragments(later);
633
634 assert_eq!(peer.command_fragments.len(), 0, "Stale fragment buffer should be cleaned up");
636 }
637
638 #[test]
639 fn test_complete_fragments_not_cleaned() {
640 let config = Config::default();
641 let start_time = Instant::now();
642 let mut peer = Peer::new(get_fake_addr(), &config, start_time);
643
644 for frag_id in 0..3 {
646 let fragment_data = vec![frag_id, frag_id + 1, frag_id + 2];
647 let cmd = ProtocolCommand::SendFragment {
648 channel_id: 0,
649 sequence: 200,
650 ordered: false,
651 fragment_id: frag_id,
652 fragment_count: 3,
653 data: fragment_data.into(),
654 };
655 peer.process_command(&cmd, start_time).unwrap();
656 }
657
658 assert_eq!(peer.command_fragments.len(), 0, "Complete fragments should be removed");
660
661 let later = start_time + std::time::Duration::from_secs(10);
663 peer.cleanup_stale_fragments(later);
664 assert_eq!(peer.command_fragments.len(), 0);
665 }
666
667 #[test]
668 fn test_multiple_stale_fragments_cleanup() {
669 let config = Config::default();
670 let start_time = Instant::now();
671 let mut peer = Peer::new(get_fake_addr(), &config, start_time);
672
673 for seq in 0..5 {
675 let cmd = ProtocolCommand::SendFragment {
676 channel_id: 0,
677 sequence: seq,
678 ordered: true,
679 fragment_id: 0,
680 fragment_count: 2,
681 data: vec![seq as u8].into(),
682 };
683 peer.process_command(&cmd, start_time).unwrap();
684 }
685
686 assert_eq!(peer.command_fragments.len(), 5);
688
689 let later = start_time + std::time::Duration::from_secs(6);
691 peer.cleanup_stale_fragments(later);
692
693 assert_eq!(
695 peer.command_fragments.len(),
696 0,
697 "All stale fragment buffers should be cleaned up"
698 );
699 }
700}