Эх сурвалжийг харах

Optimize RingBuffer::enqueue_many_with to an empty buffer.

This optimization makes sure that enqueueing to an empty ring buffer
would never wrap around, in turn ensuring that enqueueing to an empty
packet buffer would never use more than one metadata entry.
Right now, pushing buffer-sized packets into a packet buffer requires
at least two metadata entries, which is surprising and wasteful.
Astro 6 жил өмнө
parent
commit
908ef8d020

+ 9 - 17
src/socket/tcp.rs

@@ -3830,33 +3830,25 @@ mod test {
     #[test]
     fn test_buffer_wraparound_tx() {
         let mut s = socket_established();
-        s.tx_buffer = SocketBuffer::new(vec![0; 6]);
-        assert_eq!(s.send_slice(b"abc"), Ok(3));
+        s.tx_buffer = SocketBuffer::new(vec![b'.'; 9]);
+        assert_eq!(s.send_slice(b"xxxyyy"), Ok(6));
+        assert_eq!(s.tx_buffer.dequeue_many(3), &b"xxx"[..]);
+        assert_eq!(s.tx_buffer.len(), 3);
+
+        // "abcdef" not contiguous in tx buffer
+        assert_eq!(s.send_slice(b"abcdef"), Ok(6));
         recv!(s, Ok(TcpRepr {
             seq_number: LOCAL_SEQ + 1,
             ack_number: Some(REMOTE_SEQ + 1),
-            payload:    &b"abc"[..],
+            payload:    &b"yyyabc"[..],
             ..RECV_TEMPL
         }));
-        send!(s, TcpRepr {
-            seq_number: REMOTE_SEQ + 1,
-            ack_number: Some(LOCAL_SEQ + 1 + 3),
-            ..SEND_TEMPL
-        });
-        assert_eq!(s.send_slice(b"defghi"), Ok(6));
         recv!(s, Ok(TcpRepr {
-            seq_number: LOCAL_SEQ + 1 + 3,
+            seq_number: LOCAL_SEQ + 1 + 6,
             ack_number: Some(REMOTE_SEQ + 1),
             payload:    &b"def"[..],
             ..RECV_TEMPL
         }));
-        // "defghi" not contiguous in tx buffer
-        recv!(s, Ok(TcpRepr {
-            seq_number: LOCAL_SEQ + 1 + 3 + 3,
-            ack_number: Some(REMOTE_SEQ + 1),
-            payload:    &b"ghi"[..],
-            ..RECV_TEMPL
-        }));
     }
 
     // =========================================================================================//

+ 25 - 3
src/storage/ring_buffer.rs

@@ -170,6 +170,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
     /// than the size of the slice passed into it.
     pub fn enqueue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
             where F: FnOnce(&'b mut [T]) -> (usize, R) {
+        if self.length == 0 {
+            // Ring is currently empty. Reset `read_at` to optimize
+            // for contiguous space.
+            self.read_at = 0;
+        }
+
         let write_at = self.get_idx(self.length);
         let max_size = self.contiguous_window();
         let (size, result) = f(&mut self.storage[write_at..write_at + max_size]);
@@ -659,13 +665,13 @@ mod test {
         ring.dequeue_many(6).copy_from_slice(b"ABCDEF");
 
         assert_eq!(ring.write_unallocated(0, b"ghi"), 3);
-        assert_eq!(&ring.storage[..], b"ABCDEFghi...");
+        assert_eq!(ring.get_unallocated(0, 3), b"ghi");
 
         assert_eq!(ring.write_unallocated(3, b"jklmno"), 6);
-        assert_eq!(&ring.storage[..], b"mnoDEFghijkl");
+        assert_eq!(ring.get_unallocated(3, 3), b"jkl");
 
         assert_eq!(ring.write_unallocated(9, b"pqrstu"), 3);
-        assert_eq!(&ring.storage[..], b"mnopqrghijkl");
+        assert_eq!(ring.get_unallocated(9, 3), b"pqr");
     }
 
     #[test]
@@ -721,4 +727,20 @@ mod test {
         assert_eq!(no_capacity.enqueue_one(), Err(Error::Exhausted));
         assert_eq!(no_capacity.contiguous_window(), 0);
     }
+
+    /// Use the buffer a bit. Then empty it and put in an item of
+    /// maximum size. By detecting a length of 0, the implementation
+    /// can reset the current buffer position.
+    #[test]
+    fn test_buffer_write_wholly() {
+        let mut ring = RingBuffer::new(vec![b'.'; 8]);
+        ring.enqueue_many(2).copy_from_slice(b"xx");
+        ring.enqueue_many(2).copy_from_slice(b"xx");
+        assert_eq!(ring.len(), 4);
+        ring.dequeue_many(4);
+        assert_eq!(ring.len(), 0);
+
+        let large = ring.enqueue_many(8);
+        assert_eq!(large.len(), 8);
+    }
 }