1use std::collections::VecDeque;
8use std::fmt::Debug;
9use std::io::{self, ErrorKind, IoSliceMut};
10use std::net::{SocketAddr, SocketAddrV4};
11use std::time::{Duration, Instant};
12use tokio::io::Interest;
13use tokio::net::UdpSocket;
14use tracing::{debug, trace, warn};
15
16use crate::common::{ErrorSpecific, Message, MessageType, RequestSpecific, ResponseSpecific};
17#[cfg(not(test))]
18use crate::core::VERSION;
19
20use super::config::Config;
21
22const MTU: usize = 2048;
23
24const DEFAULT_PORT: u16 = 6881;
25
26const MIN_REQUEST_TIMEOUT: Duration = Duration::from_millis(500);
27
28#[cfg(test)]
30const LEGACY_VERSION: [u8; 4] = [82, 83, 0, 5]; #[derive(Debug)]
42struct UdpIo {
43 socket: UdpSocket,
44 state: noq_udp::UdpSocketState,
45 batch: Box<[u8]>,
47}
48
49impl UdpIo {
50 fn bind(addr: SocketAddr) -> io::Result<Self> {
52 let std_socket = std::net::UdpSocket::bind(addr)?;
53 let state = noq_udp::UdpSocketState::new((&std_socket).into())?;
54 let socket = UdpSocket::from_std(std_socket)?;
55 let batch = vec![0; MTU * state.gro_segments().get()].into_boxed_slice();
57 Ok(Self {
58 socket,
59 state,
60 batch,
61 })
62 }
63
64 fn local_addr(&self) -> io::Result<SocketAddr> {
65 self.socket.local_addr()
66 }
67
68 async fn send_to(&self, buf: &[u8], dest: SocketAddr) -> io::Result<()> {
71 let transmit = noq_udp::Transmit {
72 destination: dest,
73 ecn: None,
74 contents: buf,
75 segment_size: None,
76 src_ip: None,
77 };
78 loop {
79 self.socket.writable().await?;
80 match self.socket.try_io(Interest::WRITABLE, || {
81 self.state.send((&self.socket).into(), &transmit)
82 }) {
83 Ok(()) => return Ok(()),
84 Err(ref e) if e.kind() == ErrorKind::WouldBlock => continue,
85 Err(e) => return Err(e),
86 }
87 }
88 }
89
90 async fn recv_batch(&mut self) -> io::Result<(SocketAddr, usize, usize)> {
96 loop {
97 self.socket.readable().await?;
98 match self.try_recv() {
99 Ok(result) => return Ok(result),
100 Err(ref e) if e.kind() == ErrorKind::WouldBlock => continue,
101 Err(e) => return Err(e),
102 }
103 }
104 }
105
106 fn try_recv(&mut self) -> io::Result<(SocketAddr, usize, usize)> {
108 let Self {
109 socket,
110 state,
111 batch,
112 } = self;
113 socket.try_io(Interest::READABLE, || {
114 let mut iov = [IoSliceMut::new(batch)];
115 let mut meta = [noq_udp::RecvMeta::default()];
116 let n = state.recv((&*socket).into(), &mut iov, &mut meta)?;
117 if n > 0 {
118 Ok((meta[0].addr, meta[0].len, meta[0].stride))
119 } else {
120 Err(io::Error::new(ErrorKind::WouldBlock, "no data"))
121 }
122 })
123 }
124}
125
126fn datagrams(batch: &[u8], len: usize, stride: usize) -> impl Iterator<Item = &[u8]> {
133 let stride = if stride == 0 { len.max(1) } else { stride };
134 batch[..len].chunks(stride)
135}
136
137#[derive(Debug)]
146pub struct KrpcSocket {
147 io: UdpIo,
148 pub(crate) server_mode: bool,
149 local_addr: SocketAddrV4,
150
151 inflight_requests: InflightRequests,
152 outbox: VecDeque<(Vec<u8>, SocketAddr)>,
154 datagram_hook: Option<DatagramHook>,
155
156 #[cfg(test)]
157 version: [u8; 4],
158}
159
160type DatagramHookFn = dyn FnMut(&[u8], SocketAddrV4) -> bool + Send + 'static;
161
162pub struct DatagramHook(Box<DatagramHookFn>);
164
165impl DatagramHook {
166 pub fn new(filter: impl FnMut(&[u8], SocketAddrV4) -> bool + Send + 'static) -> Self {
168 Self(Box::new(filter))
169 }
170
171 fn accept(&mut self, bytes: &[u8], from: SocketAddrV4) -> bool {
172 (self.0)(bytes, from)
173 }
174}
175
176impl Debug for DatagramHook {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 f.write_str("DatagramHook(..)")
179 }
180}
181
182impl KrpcSocket {
183 pub(crate) fn new(config: &Config) -> io::Result<Self> {
184 let port = config.port;
185
186 let io = if let Some(port) = port {
187 UdpIo::bind(SocketAddr::from(([0, 0, 0, 0], port)))?
188 } else {
189 match UdpIo::bind(SocketAddr::from(([0, 0, 0, 0], DEFAULT_PORT))) {
190 Ok(io) => Ok(io),
191 Err(_) => UdpIo::bind(SocketAddr::from(([0, 0, 0, 0], 0))),
192 }?
193 };
194
195 let local_addr = match io.local_addr()? {
196 SocketAddr::V4(addr) => addr,
197 SocketAddr::V6(_) => unimplemented!("KrpcSocket does not support Ipv6"),
198 };
199
200 Ok(Self {
201 io,
202 server_mode: config.server_mode,
203 inflight_requests: InflightRequests::new(),
204 local_addr,
205 outbox: VecDeque::new(),
206 datagram_hook: None,
207
208 #[cfg(test)]
209 version: if config.disable_announce_signed_peers {
210 LEGACY_VERSION
211 } else {
212 crate::core::VERSION
213 },
214 })
215 }
216
217 #[cfg(test)]
218 pub(crate) fn server() -> io::Result<Self> {
219 Self::new(&Config {
220 server_mode: true,
221 ..Default::default()
222 })
223 }
224
225 #[cfg(test)]
226 pub(crate) fn client() -> io::Result<Self> {
227 Self::new(&Config::default())
228 }
229
230 #[inline]
234 pub fn local_addr(&self) -> SocketAddrV4 {
235 self.local_addr
236 }
237
238 pub fn inflight(&self, transaction_id: &u32) -> bool {
242 self.inflight_requests.get(*transaction_id).is_some()
243 }
244
245 pub fn request(&mut self, address: SocketAddrV4, request: RequestSpecific) -> u32 {
248 let transaction_id = self.inflight_requests.add(address);
249
250 let message = self.request_message(transaction_id, request);
251 trace!(context = "socket_message_sending", message = ?message);
252
253 let tid = message.transaction_id;
254 let _ = self.enqueue(address, message).map_err(|e| {
255 debug!(?e, "Error encoding request message");
256 });
257
258 tid
259 }
260
261 pub fn response(
264 &mut self,
265 address: SocketAddrV4,
266 transaction_id: u32,
267 response: ResponseSpecific,
268 ) {
269 let message =
270 self.response_message(MessageType::Response(response), address, transaction_id);
271 trace!(context = "socket_message_sending", message = ?message);
272 let _ = self.enqueue(address, message).map_err(|e| {
273 debug!(?e, "Error encoding response message");
274 });
275 }
276
277 pub fn error(&mut self, address: SocketAddrV4, transaction_id: u32, error: ErrorSpecific) {
280 let message = self.response_message(MessageType::Error(error), address, transaction_id);
281 let _ = self.enqueue(address, message).map_err(|e| {
282 debug!(?e, "Error encoding error message");
283 });
284 }
285
286 pub async fn flush(&mut self) {
288 while let Some((bytes, addr)) = self.outbox.pop_front() {
289 if let Err(e) = self.io.send_to(&bytes, addr).await {
290 debug!(?e, "Error sending UDP packet");
291 }
292 }
293 }
294
295 pub(crate) fn send_datagram(&mut self, bytes: Vec<u8>, address: SocketAddrV4) {
296 self.outbox.push_back((bytes, address.into()));
297 }
298
299 pub(crate) fn set_datagram_hook(&mut self, hook: Option<DatagramHook>) {
300 self.datagram_hook = hook;
301 }
302
303 pub async fn recv_batch(&mut self) -> Vec<(Message, SocketAddrV4)> {
310 self.inflight_requests.cleanup();
311
312 let (from, len, stride) = match self.io.recv_batch().await {
313 Ok(received) => received,
314 Err(error) => {
315 warn!("IO error {error}");
316 return Vec::new();
317 }
318 };
319 let SocketAddr::V4(from) = from else {
320 return Vec::new();
322 };
323 let batch = std::mem::take(&mut self.io.batch);
325 let messages = datagrams(&batch, len, stride)
326 .filter_map(|bytes| self.handle_datagram(bytes, from))
327 .collect();
328 self.io.batch = batch;
329 messages
330 }
331
332 fn handle_datagram(
334 &mut self,
335 bytes: &[u8],
336 from: SocketAddrV4,
337 ) -> Option<(Message, SocketAddrV4)> {
338 if from.port() == 0 {
339 trace!(
340 context = "socket_validation",
341 message = "Response from port 0"
342 );
343 return None;
344 }
345
346 if self
347 .datagram_hook
348 .as_mut()
349 .is_some_and(|hook| hook.accept(bytes, from))
350 {
351 return None;
352 }
353
354 match Message::from_bytes(bytes) {
355 Ok(message) => {
356 let should_return = match message.message_type {
358 MessageType::Request(ref _request_specific) => {
359 #[cfg(test)]
361 let should_return = supports_request(&self.version, _request_specific);
362 #[cfg(not(test))]
363 let should_return = true;
364
365 trace!(
366 context = "socket_message_receiving",
367 ?message,
368 ?from,
369 "Received request message"
370 );
371
372 should_return
373 }
374 MessageType::Response(_) => {
375 trace!(
376 context = "socket_message_receiving",
377 ?message,
378 ?from,
379 "Received response message"
380 );
381
382 self.is_expected_response(&message, &from)
383 }
384 MessageType::Error(_) => {
385 trace!(
386 context = "socket_message_receiving",
387 ?message,
388 ?from,
389 "Received error message"
390 );
391
392 self.is_expected_response(&message, &from)
393 }
394 };
395
396 if should_return {
397 return Some((message, from));
398 }
399 }
400 Err(error) => {
401 trace!(context = "socket_error", ?error, ?from, message = ?String::from_utf8_lossy(bytes), "Received invalid Bencode message.");
402 }
403 };
404 None
405 }
406
407 fn is_expected_response(&mut self, message: &Message, from: &SocketAddrV4) -> bool {
410 match self.inflight_requests.remove(message.transaction_id) {
412 Some(request) => {
413 if compare_socket_addr(&request.to, from) {
414 return true;
415 } else {
416 trace!(
417 context = "socket_validation",
418 message = "Response from wrong address"
419 );
420 }
421 }
422 None => {
423 trace!(
424 context = "socket_validation",
425 message = "Unexpected response id, or timedout request"
426 );
427 }
428 }
429
430 false
431 }
432
433 fn request_message(&mut self, transaction_id: u32, message: RequestSpecific) -> Message {
435 Message {
436 transaction_id,
437 message_type: MessageType::Request(message),
438 version: Some(self.version()),
439 read_only: !self.server_mode,
440 requester_ip: None,
441 }
442 }
443
444 fn response_message(
446 &mut self,
447 message: MessageType,
448 requester_ip: SocketAddrV4,
449 request_tid: u32,
450 ) -> Message {
451 Message {
452 transaction_id: request_tid,
453 message_type: message,
454 version: Some(self.version()),
455 read_only: !self.server_mode,
456 requester_ip: Some(requester_ip),
458 }
459 }
460
461 fn version(&self) -> [u8; 4] {
462 #[cfg(test)]
463 return self.version;
464 #[cfg(not(test))]
465 return VERSION;
466 }
467
468 fn enqueue(&mut self, address: SocketAddrV4, message: Message) -> Result<(), SendMessageError> {
470 let bytes = message.to_bytes()?;
471 trace!(context = "socket_message_sending", message = ?message);
472 self.outbox.push_back((bytes, SocketAddr::from(address)));
473 Ok(())
474 }
475}
476
477#[n0_error::stack_error(derive, from_sources, std_sources)]
478enum SendMessageError {
480 #[error(transparent)]
482 BencodeError(serde_bencode::Error),
483
484 #[error(transparent)]
485 IO(std::io::Error),
487}
488
489fn compare_socket_addr(a: &SocketAddrV4, b: &SocketAddrV4) -> bool {
491 if a.port() != b.port() {
492 return false;
493 }
494
495 if a.ip().is_unspecified() {
496 return true;
497 }
498
499 a.ip() == b.ip()
500}
501
502struct InflightRequest {
503 tid: u32,
504 to: SocketAddrV4,
505 sent_at: Instant,
506}
507
508impl Debug for InflightRequest {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 f.debug_struct("InflightRequest")
511 .field("tid", &self.tid)
512 .field("to", &self.to)
513 .field("elapsed", &self.sent_at.elapsed())
514 .finish()
515 }
516}
517
518struct InflightRequests {
521 next_tid: u32,
522 requests: Vec<InflightRequest>,
523 estimated_rtt: Duration,
524 deviation_rtt: Duration,
525}
526
527impl InflightRequests {
528 fn new() -> Self {
529 Self {
530 next_tid: 0,
531 requests: Vec::new(),
532 estimated_rtt: MIN_REQUEST_TIMEOUT,
533 deviation_rtt: Duration::from_secs(0),
534 }
535 }
536
537 fn tid(&mut self) -> u32 {
539 let tid = self.next_tid;
542 self.next_tid = self.next_tid.wrapping_add(1);
543 tid
544 }
545
546 fn request_timeout(&self) -> Duration {
547 self.estimated_rtt + self.deviation_rtt.mul_f64(4.0)
548 }
549
550 fn get(&self, key: u32) -> Option<&InflightRequest> {
551 if let Ok(index) = self.find_by_tid(key)
552 && let Some(request) = self.requests.get(index)
553 && request.sent_at.elapsed() < self.request_timeout()
554 {
555 return Some(request);
556 };
557
558 None
559 }
560
561 fn add(&mut self, to: SocketAddrV4) -> u32 {
563 let tid = self.tid();
564 self.requests.push(InflightRequest {
565 tid,
566 to,
567 sent_at: Instant::now(),
568 });
569
570 tid
571 }
572
573 fn remove(&mut self, key: u32) -> Option<InflightRequest> {
574 match self.find_by_tid(key) {
575 Ok(index) => {
576 let request = self.requests.remove(index);
577
578 self.update_rtt_estimates(request.sent_at.elapsed());
579 trace!(
580 "Updated estimated round trip time {:?} and request timeout {:?}",
581 self.estimated_rtt,
582 self.request_timeout()
583 );
584
585 Some(request)
586 }
587 Err(_) => None,
588 }
589 }
590
591 fn update_rtt_estimates(&mut self, sample_rtt: Duration) {
592 if sample_rtt < MIN_REQUEST_TIMEOUT {
593 return;
594 }
595
596 let alpha = 0.125;
598 let beta = 0.25;
599
600 let sample_rtt_secs = sample_rtt.as_secs_f64();
601 let est_rtt_secs = self.estimated_rtt.as_secs_f64();
602 let dev_rtt_secs = self.deviation_rtt.as_secs_f64();
603
604 let new_est_rtt = (1.0 - alpha) * est_rtt_secs + alpha * sample_rtt_secs;
605 let new_dev_rtt =
606 (1.0 - beta) * dev_rtt_secs + beta * (sample_rtt_secs - new_est_rtt).abs();
607
608 self.estimated_rtt = Duration::from_secs_f64(new_est_rtt);
609 self.deviation_rtt = Duration::from_secs_f64(new_dev_rtt);
610 }
611
612 fn find_by_tid(&self, tid: u32) -> Result<usize, usize> {
613 self.requests
614 .binary_search_by(|request| request.tid.cmp(&tid))
615 }
616
617 fn cleanup(&mut self) {
619 if self.requests.len() < self.requests.capacity() {
620 return;
621 }
622
623 let index = match self
624 .requests
625 .binary_search_by(|request| self.request_timeout().cmp(&request.sent_at.elapsed()))
626 {
627 Ok(index) => index,
628 Err(index) => index,
629 };
630
631 self.requests.drain(0..index);
632 }
633}
634
635impl Debug for InflightRequests {
636 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
637 let timeout = self.request_timeout();
638 let non_expired: Vec<_> = self
639 .requests
640 .iter()
641 .filter(|req| req.sent_at.elapsed() < timeout)
642 .collect();
643
644 f.debug_struct("InflightRequests")
645 .field("next_tid", &self.next_tid)
646 .field("requests", &non_expired)
647 .field("estimated_rtt", &self.estimated_rtt)
648 .field("deviation_rtt", &self.deviation_rtt)
649 .field("request_timeout", &timeout)
650 .finish()
651 }
652}
653
654#[cfg(test)]
655fn supports_request(version: &[u8; 4], request_specific: &crate::common::RequestSpecific) -> bool {
656 use crate::{
657 PutRequestSpecific,
658 common::{PutRequest, RequestTypeSpecific},
659 };
660 match request_specific.request_type {
661 RequestTypeSpecific::GetSignedPeers(_)
662 | RequestTypeSpecific::Put(PutRequest {
663 put_request_type: PutRequestSpecific::AnnounceSignedPeer(_),
664 ..
665 }) => version != &LEGACY_VERSION,
666 _ => true,
667 }
668}
669
670#[cfg(test)]
671mod test {
672 use crate::{
673 common::{Id, PingResponseArguments, RequestTypeSpecific},
674 core::VERSION,
675 };
676
677 use super::*;
678
679 #[tokio::test]
680 async fn tid() {
681 let socket = KrpcSocket::server().unwrap();
682
683 let mut requests = socket.inflight_requests;
684 assert_eq!(requests.tid(), 0);
685 assert_eq!(requests.tid(), 1);
686 assert_eq!(requests.tid(), 2);
687
688 requests.next_tid = u32::MAX;
689
690 assert_eq!(requests.tid(), u32::MAX);
691 assert_eq!(requests.tid(), 0);
692 }
693
694 #[tokio::test]
695 async fn recv_request() {
696 let mut server = KrpcSocket::server().unwrap();
697 let server_address = SocketAddrV4::new([127, 0, 0, 1].into(), server.local_addr().port());
698
699 let mut client = KrpcSocket::client().unwrap();
700 client.inflight_requests.next_tid = 120;
701
702 let client_address = client.local_addr();
703 let request = RequestSpecific {
704 requester_id: Id::random(),
705 request_type: RequestTypeSpecific::Ping,
706 };
707
708 let expected_request = request.clone();
709
710 client.request(server_address, request);
711 client.flush().await;
712
713 let (message, from) = loop {
714 if let Some(result) = server.recv_batch().await.into_iter().next() {
715 break result;
716 }
717 };
718
719 assert_eq!(from.port(), client_address.port());
720 assert_eq!(message.transaction_id, 120);
721 assert!(message.read_only, "Read-only should be true");
722 assert_eq!(message.version, Some(VERSION), "Version should be 'RS'");
723 assert_eq!(message.message_type, MessageType::Request(expected_request));
724 }
725
726 #[tokio::test]
727 async fn datagram_filter_runs_before_krpc_decode() {
728 let mut server = KrpcSocket::server().unwrap();
729 let server_address = SocketAddrV4::new([127, 0, 0, 1].into(), server.local_addr().port());
730 let (sender, mut receiver) = tokio::sync::mpsc::channel::<(Box<[u8]>, SocketAddrV4)>(1);
731 server.set_datagram_hook(Some(DatagramHook::new(move |bytes, from| {
732 let _ = sender.try_send((Box::from(bytes), from));
733 true
734 })));
735
736 let mut client = KrpcSocket::client().unwrap();
737 client.request(
738 server_address,
739 RequestSpecific {
740 requester_id: Id::random(),
741 request_type: RequestTypeSpecific::Ping,
742 },
743 );
744 client.flush().await;
745
746 assert!(server.recv_batch().await.is_empty());
747 let (bytes, _) = receiver.recv().await.unwrap();
748 assert!(Message::from_bytes(&bytes).is_ok());
749 }
750
751 #[tokio::test]
752 async fn recv_response() {
753 let mut client = KrpcSocket::client().unwrap();
754 let client_address = client.local_addr();
755
756 let mut server = KrpcSocket::client().unwrap();
757 let server_address = SocketAddrV4::new([127, 0, 0, 1].into(), server.local_addr().port());
758
759 let responder_id = Id::random();
760 let response = ResponseSpecific::Ping(PingResponseArguments { responder_id });
761
762 server.inflight_requests.requests.push(InflightRequest {
763 tid: 8,
764 to: client_address,
765 sent_at: Instant::now(),
766 });
767
768 client.response(server_address, 8, response);
769 client.flush().await;
770
771 let (message, from) = loop {
772 if let Some(result) = server.recv_batch().await.into_iter().next() {
773 break result;
774 }
775 };
776
777 assert_eq!(from.port(), client_address.port());
778 assert_eq!(message.transaction_id, 8);
779 assert!(message.read_only, "Read-only should be true");
780 assert_eq!(message.version, Some(VERSION), "Version should be 'RS'");
781 assert_eq!(
782 message.message_type,
783 MessageType::Response(ResponseSpecific::Ping(PingResponseArguments {
784 responder_id,
785 }))
786 );
787 }
788
789 #[tokio::test]
790 async fn ignore_response_from_wrong_address() {
791 let mut server = KrpcSocket::client().unwrap();
792 let server_address = server.local_addr();
793
794 let mut client = KrpcSocket::client().unwrap();
795
796 let client_address = client.local_addr();
797
798 server.inflight_requests.requests.push(InflightRequest {
799 tid: 8,
800 to: SocketAddrV4::new([127, 0, 0, 1].into(), client_address.port() + 1),
801 sent_at: Instant::now(),
802 });
803
804 let response = ResponseSpecific::Ping(PingResponseArguments {
805 responder_id: Id::random(),
806 });
807
808 client.response(server_address, 8, response);
809 client.flush().await;
810
811 let result = tokio::time::timeout(Duration::from_millis(50), server.recv_batch()).await;
813
814 assert!(
815 result.is_err() || result.unwrap().is_empty(),
816 "Should not receive a response from wrong address"
817 );
818 }
819
820 #[tokio::test]
821 async fn inflight_request_timeout() {
822 let mut server = KrpcSocket::client().unwrap();
823
824 let tid = 8;
825 let sent_at = Instant::now();
826
827 server.inflight_requests.requests.push(InflightRequest {
828 tid,
829 to: SocketAddrV4::new([0, 0, 0, 0].into(), 0),
830 sent_at,
831 });
832
833 tokio::time::sleep(server.inflight_requests.request_timeout()).await;
834
835 assert!(!server.inflight(&tid));
836 }
837
838 #[tokio::test]
844 async fn gro_batches_are_split_into_datagrams() {
845 let mut receiver = UdpIo::bind(SocketAddr::from(([127, 0, 0, 1], 0))).unwrap();
846 let destination = receiver.local_addr().unwrap();
847 let sender = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
848 let state = noq_udp::UdpSocketState::new((&sender).into()).unwrap();
849 let sent = [vec![1u8; 100], vec![2; 100], vec![3; 40]];
851 if state.max_gso_segments().get() > 1 {
852 let contents = sent.concat();
853 let transmit = noq_udp::Transmit {
854 destination,
855 ecn: None,
856 contents: &contents,
857 segment_size: Some(100),
858 src_ip: None,
859 };
860 state.send((&sender).into(), &transmit).unwrap();
861 } else {
862 for datagram in &sent {
863 sender.send_to(datagram, destination).unwrap();
864 }
865 }
866 let mut received = Vec::new();
867 while received.len() < sent.len() {
868 let (from, len, stride) =
869 tokio::time::timeout(Duration::from_secs(5), receiver.recv_batch())
870 .await
871 .expect("datagrams in time")
872 .unwrap();
873 assert_eq!(from, sender.local_addr().unwrap());
874 received.extend(datagrams(&receiver.batch, len, stride).map(<[u8]>::to_vec));
875 }
876 assert_eq!(received, sent);
877 }
878
879 #[test]
880 fn datagrams_split_at_the_stride() {
881 let batch: Vec<u8> = (0..=255u8).cycle().take(300).collect();
882 let lengths = |len, stride| {
883 datagrams(&batch, len, stride)
884 .map(<[u8]>::len)
885 .collect::<Vec<_>>()
886 };
887 assert_eq!(lengths(300, 100), vec![100, 100, 100]);
889 assert_eq!(lengths(240, 100), vec![100, 100, 40]);
890 assert_eq!(lengths(57, 57), vec![57]);
892 assert_eq!(lengths(57, 100), vec![57]);
894 assert_eq!(lengths(0, 0), Vec::<usize>::new());
896 assert_eq!(lengths(57, 0), vec![57]);
897 let parts: Vec<&[u8]> = datagrams(&batch, 5, 2).collect();
899 assert_eq!(parts, vec![&[0, 1][..], &[2, 3][..], &[4][..]]);
900 }
901}