1use alloc::collections::VecDeque;
2use alloc::vec::Vec;
3use core::cmp;
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 chunks: VecDeque<Vec<u8>>,
18 limit: Option<usize>,
19}
20
21impl ChunkVecBuffer {
22 pub(crate) fn new(limit: Option<usize>) -> Self {
23 Self {
24 chunks: VecDeque::new(),
25 limit,
26 }
27 }
28
29 pub(crate) fn set_limit(&mut self, new_limit: Option<usize>) {
37 self.limit = new_limit;
38 }
39
40 pub(crate) fn is_empty(&self) -> bool {
42 self.chunks.is_empty()
43 }
44
45 pub(crate) fn len(&self) -> usize {
47 let mut len = 0;
48 for ch in &self.chunks {
49 len += ch.len();
50 }
51 len
52 }
53
54 pub(crate) fn apply_limit(&self, len: usize) -> usize {
58 if let Some(limit) = self.limit {
59 let space = limit.saturating_sub(self.len());
60 cmp::min(len, space)
61 } else {
62 len
63 }
64 }
65
66 pub(crate) fn append(&mut self, bytes: Vec<u8>) -> usize {
68 let len = bytes.len();
69
70 if !bytes.is_empty() {
71 self.chunks.push_back(bytes);
72 }
73
74 len
75 }
76
77 pub(crate) fn pop(&mut self) -> Option<Vec<u8>> {
80 self.chunks.pop_front()
81 }
82
83 #[cfg(read_buf)]
84 pub(crate) fn read_buf(&mut self, mut cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> {
86 while !self.is_empty() && cursor.capacity() > 0 {
87 let chunk = self.chunks[0].as_slice();
88 let used = cmp::min(chunk.len(), cursor.capacity());
89 cursor.append(&chunk[..used]);
90 self.consume(used);
91 }
92
93 Ok(())
94 }
95}
96
97#[cfg(feature = "std")]
98impl ChunkVecBuffer {
99 pub(crate) fn is_full(&self) -> bool {
100 self.limit
101 .map(|limit| self.len() > limit)
102 .unwrap_or_default()
103 }
104
105 pub(crate) fn append_limited_copy(&mut self, payload: OutboundChunks<'_>) -> usize {
108 let take = self.apply_limit(payload.len());
109 self.append(payload.split_at(take).0.to_vec());
110 take
111 }
112
113 pub(crate) fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
116 let mut offs = 0;
117
118 while offs < buf.len() && !self.is_empty() {
119 let used = self.chunks[0]
120 .as_slice()
121 .read(&mut buf[offs..])?;
122
123 self.consume(used);
124 offs += used;
125 }
126
127 Ok(offs)
128 }
129
130 fn consume(&mut self, mut used: usize) {
131 while let Some(mut buf) = self.chunks.pop_front() {
132 if used < buf.len() {
133 buf.drain(..used);
134 self.chunks.push_front(buf);
135 break;
136 } else {
137 used -= buf.len();
138 }
139 }
140 }
141
142 pub(crate) fn write_to(&mut self, wr: &mut dyn io::Write) -> io::Result<usize> {
144 if self.is_empty() {
145 return Ok(0);
146 }
147
148 let mut bufs = [io::IoSlice::new(&[]); 64];
149 for (iov, chunk) in bufs.iter_mut().zip(self.chunks.iter()) {
150 *iov = io::IoSlice::new(chunk);
151 }
152 let len = cmp::min(bufs.len(), self.chunks.len());
153 let used = wr.write_vectored(&bufs[..len])?;
154 self.consume(used);
155 Ok(used)
156 }
157}
158
159#[cfg(all(test, feature = "std"))]
160mod tests {
161 use super::ChunkVecBuffer;
162
163 #[test]
164 fn short_append_copy_with_limit() {
165 let mut cvb = ChunkVecBuffer::new(Some(12));
166 assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 5);
167 assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 5);
168 assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 2);
169 assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 0);
170
171 let mut buf = [0u8; 12];
172 assert_eq!(cvb.read(&mut buf).unwrap(), 12);
173 assert_eq!(buf.to_vec(), b"helloworldhe".to_vec());
174 }
175
176 #[cfg(read_buf)]
177 #[test]
178 fn read_buf() {
179 use core::io::BorrowedBuf;
180 use core::mem::MaybeUninit;
181
182 {
183 let mut cvb = ChunkVecBuffer::new(None);
184 cvb.append(b"test ".to_vec());
185 cvb.append(b"fixture ".to_vec());
186 cvb.append(b"data".to_vec());
187
188 let mut buf = [MaybeUninit::<u8>::uninit(); 8];
189 let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into();
190 cvb.read_buf(buf.unfilled()).unwrap();
191 assert_eq!(buf.filled(), b"test fix");
192 buf.clear();
193 cvb.read_buf(buf.unfilled()).unwrap();
194 assert_eq!(buf.filled(), b"ture dat");
195 buf.clear();
196 cvb.read_buf(buf.unfilled()).unwrap();
197 assert_eq!(buf.filled(), b"a");
198 }
199
200 {
201 let mut cvb = ChunkVecBuffer::new(None);
202 cvb.append(b"short message".to_vec());
203
204 let mut buf = [MaybeUninit::<u8>::uninit(); 1024];
205 let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into();
206 cvb.read_buf(buf.unfilled()).unwrap();
207 assert_eq!(buf.filled(), b"short message");
208 }
209 }
210}