1use alloc::collections::VecDeque;
2use alloc::vec::Vec;
3use core::{cmp, mem};
4#[cfg(feature = "std")]
5use std::io;
6#[cfg(feature = "std")]
7use std::io::Read;
8
9#[cfg(feature = "std")]
10use crate::msgs::message::OutboundChunks;
11
12pub(crate) struct ChunkVecBuffer {
17 prefix_used: usize,
22
23 chunks: VecDeque<Vec<u8>>,
24
25 limit: Option<usize>,
27}
28
29impl ChunkVecBuffer {
30 pub(crate) fn new(limit: Option<usize>) -> Self {
31 Self {
32 prefix_used: 0,
33 chunks: VecDeque::new(),
34 limit,
35 }
36 }
37
38 pub(crate) fn set_limit(&mut self, new_limit: Option<usize>) {
46 self.limit = new_limit;
47 }
48
49 pub(crate) fn is_empty(&self) -> bool {
51 self.chunks.is_empty()
52 }
53
54 pub(crate) fn len(&self) -> usize {
56 self.chunks
57 .iter()
58 .fold(0usize, |acc, chunk| acc + chunk.len())
59 - self.prefix_used
60 }
61
62 pub(crate) fn apply_limit(&self, len: usize) -> usize {
66 if let Some(limit) = self.limit {
67 let space = limit.saturating_sub(self.len());
68 cmp::min(len, space)
69 } else {
70 len
71 }
72 }
73
74 pub(crate) fn append(&mut self, bytes: Vec<u8>) -> usize {
76 let len = bytes.len();
77
78 if !bytes.is_empty() {
79 if self.chunks.is_empty() {
80 debug_assert_eq!(self.prefix_used, 0);
81 }
82
83 self.chunks.push_back(bytes);
84 }
85
86 len
87 }
88
89 pub(crate) fn pop(&mut self) -> Option<Vec<u8>> {
93 let mut first = self.chunks.pop_front();
94
95 if let Some(first) = &mut first {
96 let prefix = mem::take(&mut self.prefix_used);
98 first.drain(0..prefix);
99 }
100
101 first
102 }
103
104 #[cfg(read_buf)]
105 pub(crate) fn read_buf(
107 &mut self,
108 mut cursor: core::io::BorrowedCursor<'_, u8>,
109 ) -> io::Result<()> {
110 while !self.is_empty() && cursor.capacity() > 0 {
111 let chunk = &self.chunks[0][self.prefix_used..];
112 let used = cmp::min(chunk.len(), cursor.capacity());
113 cursor.append(&chunk[..used]);
114 self.consume(used);
115 }
116
117 Ok(())
118 }
119
120 pub(crate) fn peek(&self) -> Option<&[u8]> {
122 self.chunks
123 .front()
124 .map(|ch| ch.as_slice())
125 }
126}
127
128#[cfg(feature = "std")]
129impl ChunkVecBuffer {
130 pub(crate) fn is_full(&self) -> bool {
131 self.limit
132 .map(|limit| self.len() > limit)
133 .unwrap_or_default()
134 }
135
136 pub(crate) fn append_limited_copy(&mut self, payload: OutboundChunks<'_>) -> usize {
139 let take = self.apply_limit(payload.len());
140 self.append(payload.split_at(take).0.to_vec());
141 take
142 }
143
144 pub(crate) fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
147 let mut offs = 0;
148
149 while offs < buf.len() && !self.is_empty() {
150 let used = (&self.chunks[0][self.prefix_used..]).read(&mut buf[offs..])?;
151
152 self.consume(used);
153 offs += used;
154 }
155
156 Ok(offs)
157 }
158
159 pub(crate) fn consume_first_chunk(&mut self, used: usize) {
160 assert!(
163 used <= self
164 .chunk()
165 .map(|ch| ch.len())
166 .unwrap_or_default(),
167 "illegal `BufRead::consume` usage",
168 );
169 self.consume(used);
170 }
171
172 fn consume(&mut self, used: usize) {
173 self.prefix_used += used;
175
176 while let Some(buf) = self.chunks.front() {
179 if self.prefix_used < buf.len() {
180 return;
181 } else {
182 self.prefix_used -= buf.len();
183 self.chunks.pop_front();
184 }
185 }
186
187 debug_assert_eq!(
188 self.prefix_used, 0,
189 "attempted to `ChunkVecBuffer::consume` more than available"
190 );
191 }
192
193 pub(crate) fn write_to(&mut self, wr: &mut dyn io::Write) -> io::Result<usize> {
195 if self.is_empty() {
196 return Ok(0);
197 }
198
199 let mut prefix = self.prefix_used;
200 let mut bufs = [io::IoSlice::new(&[]); 64];
201 for (iov, chunk) in bufs.iter_mut().zip(self.chunks.iter()) {
202 *iov = io::IoSlice::new(&chunk[prefix..]);
203 prefix = 0;
204 }
205 let len = cmp::min(bufs.len(), self.chunks.len());
206 let bufs = &bufs[..len];
207 let used = wr.write_vectored(bufs)?;
208 let available_bytes = bufs.iter().map(|ch| ch.len()).sum();
209
210 if used > available_bytes {
211 self.consume(available_bytes);
216 return Err(io::Error::new(
217 io::ErrorKind::Other,
218 std::format!("illegal write_vectored return value ({used} > {available_bytes})"),
219 ));
220 }
221 self.consume(used);
222 Ok(used)
223 }
224
225 pub(crate) fn chunk(&self) -> Option<&[u8]> {
227 self.chunks
228 .front()
229 .map(|chunk| &chunk[self.prefix_used..])
230 }
231}
232
233#[cfg(all(test, feature = "std"))]
234mod tests {
235 use alloc::vec;
236 use alloc::vec::Vec;
237
238 use super::ChunkVecBuffer;
239
240 #[test]
241 fn short_append_copy_with_limit() {
242 let mut cvb = ChunkVecBuffer::new(Some(12));
243 assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 5);
244 assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 5);
245 assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 2);
246 assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 0);
247
248 let mut buf = [0u8; 12];
249 assert_eq!(cvb.read(&mut buf).unwrap(), 12);
250 assert_eq!(buf.to_vec(), b"helloworldhe".to_vec());
251 }
252
253 #[test]
254 fn read_byte_by_byte() {
255 let mut cvb = ChunkVecBuffer::new(None);
256 cvb.append(b"test fixture data".to_vec());
257 assert!(!cvb.is_empty());
258 for expect in b"test fixture data" {
259 let mut byte = [0];
260 assert_eq!(cvb.read(&mut byte).unwrap(), 1);
261 assert_eq!(byte[0], *expect);
262 }
263
264 assert_eq!(cvb.read(&mut [0]).unwrap(), 0);
265 }
266
267 #[test]
268 fn every_possible_chunk_interleaving() {
269 let input = (0..=0xffu8)
270 .cycle()
271 .take(4096)
272 .collect::<Vec<u8>>();
273
274 for input_chunk_len in 1..64usize {
275 for output_chunk_len in 1..65usize {
276 std::println!("check input={input_chunk_len} output={output_chunk_len}");
277 let mut cvb = ChunkVecBuffer::new(None);
278 for chunk in input.chunks(input_chunk_len) {
279 cvb.append(chunk.to_vec());
280 }
281
282 assert_eq!(cvb.len(), input.len());
283 let mut buf = vec![0u8; output_chunk_len];
284
285 for expect in input.chunks(output_chunk_len) {
286 assert_eq!(expect.len(), cvb.read(&mut buf).unwrap());
287 assert_eq!(expect, &buf[..expect.len()]);
288 }
289
290 assert_eq!(cvb.read(&mut [0]).unwrap(), 0);
291 }
292 }
293 }
294
295 #[cfg(read_buf)]
296 #[test]
297 fn read_buf() {
298 use core::io::BorrowedBuf;
299 use core::mem::MaybeUninit;
300
301 {
302 let mut cvb = ChunkVecBuffer::new(None);
303 cvb.append(b"test ".to_vec());
304 cvb.append(b"fixture ".to_vec());
305 cvb.append(b"data".to_vec());
306
307 let mut buf = [MaybeUninit::<u8>::uninit(); 8];
308 let mut buf: BorrowedBuf<'_, u8> = buf.as_mut_slice().into();
309 cvb.read_buf(buf.unfilled()).unwrap();
310 assert_eq!(buf.filled(), b"test fix");
311 buf.clear();
312 cvb.read_buf(buf.unfilled()).unwrap();
313 assert_eq!(buf.filled(), b"ture dat");
314 buf.clear();
315 cvb.read_buf(buf.unfilled()).unwrap();
316 assert_eq!(buf.filled(), b"a");
317 }
318
319 {
320 let mut cvb = ChunkVecBuffer::new(None);
321 cvb.append(b"short message".to_vec());
322
323 let mut buf = [MaybeUninit::<u8>::uninit(); 1024];
324 let mut buf: BorrowedBuf<'_, u8> = buf.as_mut_slice().into();
325 cvb.read_buf(buf.unfilled()).unwrap();
326 assert_eq!(buf.filled(), b"short message");
327 }
328 }
329}
330
331#[cfg(bench)]
332mod benchmarks {
333 use alloc::vec;
334
335 use super::ChunkVecBuffer;
336
337 #[bench]
338 fn read_one_byte_from_large_message(b: &mut test::Bencher) {
339 b.iter(|| {
340 let mut cvb = ChunkVecBuffer::new(None);
341 cvb.append(vec![0u8; 16_384]);
342 assert_eq!(1, cvb.read(&mut [0u8]).unwrap());
343 });
344 }
345
346 #[bench]
347 fn read_all_individual_from_large_message(b: &mut test::Bencher) {
348 b.iter(|| {
349 let mut cvb = ChunkVecBuffer::new(None);
350 cvb.append(vec![0u8; 16_384]);
351 loop {
352 if let Ok(0) = cvb.read(&mut [0u8]) {
353 break;
354 }
355 }
356 });
357 }
358
359 #[bench]
360 fn read_half_bytes_from_large_message(b: &mut test::Bencher) {
361 b.iter(|| {
362 let mut cvb = ChunkVecBuffer::new(None);
363 cvb.append(vec![0u8; 16_384]);
364 assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap());
365 assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap());
366 });
367 }
368
369 #[bench]
370 fn read_entire_large_message(b: &mut test::Bencher) {
371 b.iter(|| {
372 let mut cvb = ChunkVecBuffer::new(None);
373 cvb.append(vec![0u8; 16_384]);
374 assert_eq!(16_384, cvb.read(&mut [0u8; 16_384]).unwrap());
375 });
376 }
377}