1 use std::io::{Cursor, Read, Result as IoResult};
2
3 use bytes::Buf;
4 use criterion::*;
5 use input_buffer::InputBuffer;
6
7 use tungstenite::buffer::ReadBuffer;
8
9 const CHUNK_SIZE: usize = 4096;
10
11 /// A FIFO buffer for reading packets from the network.
12 #[derive(Debug)]
13 pub struct StackReadBuffer<const CHUNK_SIZE: usize> {
14 storage: Cursor<Vec<u8>>,
15 chunk: [u8; CHUNK_SIZE],
16 }
17
18 impl<const CHUNK_SIZE: usize> StackReadBuffer<CHUNK_SIZE> {
19 /// Create a new empty input buffer.
new() -> Self20 pub fn new() -> Self {
21 Self::with_capacity(CHUNK_SIZE)
22 }
23
24 /// Create a new empty input buffer with a given `capacity`.
with_capacity(capacity: usize) -> Self25 pub fn with_capacity(capacity: usize) -> Self {
26 Self::from_partially_read(Vec::with_capacity(capacity))
27 }
28
29 /// Create a input buffer filled with previously read data.
from_partially_read(part: Vec<u8>) -> Self30 pub fn from_partially_read(part: Vec<u8>) -> Self {
31 Self { storage: Cursor::new(part), chunk: [0; CHUNK_SIZE] }
32 }
33
34 /// Get a cursor to the data storage.
as_cursor(&self) -> &Cursor<Vec<u8>>35 pub fn as_cursor(&self) -> &Cursor<Vec<u8>> {
36 &self.storage
37 }
38
39 /// Get a cursor to the mutable data storage.
as_cursor_mut(&mut self) -> &mut Cursor<Vec<u8>>40 pub fn as_cursor_mut(&mut self) -> &mut Cursor<Vec<u8>> {
41 &mut self.storage
42 }
43
44 /// Consume the `ReadBuffer` and get the internal storage.
into_vec(mut self) -> Vec<u8>45 pub fn into_vec(mut self) -> Vec<u8> {
46 // Current implementation of `tungstenite-rs` expects that the `into_vec()` drains
47 // the data from the container that has already been read by the cursor.
48 self.clean_up();
49
50 // Now we can safely return the internal container.
51 self.storage.into_inner()
52 }
53
54 /// Read next portion of data from the given input stream.
read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize>55 pub fn read_from<S: Read>(&mut self, stream: &mut S) -> IoResult<usize> {
56 self.clean_up();
57 let size = stream.read(&mut self.chunk)?;
58 self.storage.get_mut().extend_from_slice(&self.chunk[..size]);
59 Ok(size)
60 }
61
62 /// Cleans ups the part of the vector that has been already read by the cursor.
clean_up(&mut self)63 fn clean_up(&mut self) {
64 let pos = self.storage.position() as usize;
65 self.storage.get_mut().drain(0..pos).count();
66 self.storage.set_position(0);
67 }
68 }
69
70 impl<const CHUNK_SIZE: usize> Buf for StackReadBuffer<CHUNK_SIZE> {
remaining(&self) -> usize71 fn remaining(&self) -> usize {
72 Buf::remaining(self.as_cursor())
73 }
74
chunk(&self) -> &[u8]75 fn chunk(&self) -> &[u8] {
76 Buf::chunk(self.as_cursor())
77 }
78
advance(&mut self, cnt: usize)79 fn advance(&mut self, cnt: usize) {
80 Buf::advance(self.as_cursor_mut(), cnt)
81 }
82 }
83
84 impl<const CHUNK_SIZE: usize> Default for StackReadBuffer<CHUNK_SIZE> {
default() -> Self85 fn default() -> Self {
86 Self::new()
87 }
88 }
89
90 #[inline]
input_buffer(mut stream: impl Read)91 fn input_buffer(mut stream: impl Read) {
92 let mut buffer = InputBuffer::with_capacity(CHUNK_SIZE);
93 while buffer.read_from(&mut stream).unwrap() != 0 {}
94 }
95
96 #[inline]
stack_read_buffer(mut stream: impl Read)97 fn stack_read_buffer(mut stream: impl Read) {
98 let mut buffer = StackReadBuffer::<CHUNK_SIZE>::new();
99 while buffer.read_from(&mut stream).unwrap() != 0 {}
100 }
101
102 #[inline]
heap_read_buffer(mut stream: impl Read)103 fn heap_read_buffer(mut stream: impl Read) {
104 let mut buffer = ReadBuffer::<CHUNK_SIZE>::new();
105 while buffer.read_from(&mut stream).unwrap() != 0 {}
106 }
107
benchmark(c: &mut Criterion)108 fn benchmark(c: &mut Criterion) {
109 const STREAM_SIZE: usize = 1024 * 1024 * 4;
110 let data: Vec<u8> = (0..STREAM_SIZE).map(|_| rand::random()).collect();
111 let stream = Cursor::new(data);
112
113 let mut group = c.benchmark_group("buffers");
114 group.throughput(Throughput::Bytes(STREAM_SIZE as u64));
115 group.bench_function("InputBuffer", |b| b.iter(|| input_buffer(black_box(stream.clone()))));
116 group.bench_function("ReadBuffer (stack)", |b| {
117 b.iter(|| stack_read_buffer(black_box(stream.clone())))
118 });
119 group.bench_function("ReadBuffer (heap)", |b| {
120 b.iter(|| heap_read_buffer(black_box(stream.clone())))
121 });
122 group.finish();
123 }
124
125 criterion_group!(benches, benchmark);
126 criterion_main!(benches);
127