Skip to main content

musli_web/
buf.rs

1use core::cell::Cell;
2use core::cell::RefCell;
3use core::fmt;
4use core::mem;
5use core::mem::ManuallyDrop;
6use core::ops::Range;
7
8use alloc::vec::Vec;
9use musli::Encode;
10use musli::mode::Binary;
11
12use crate::api::{EncodeBody, Format};
13use crate::format;
14
15#[derive(Debug)]
16#[cfg_attr(test, derive(PartialEq))]
17enum InvalidFrameWhat {
18    ReadPosition(usize),
19    LengthPrefix,
20    LengthPrefixOverflow(u32),
21    InsufficientLength(usize),
22    InsufficientFrame(usize),
23}
24
25impl fmt::Display for InvalidFrameWhat {
26    #[inline]
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            Self::ReadPosition(pos) => write!(f, "read position ({pos}) out of bounds"),
30            Self::LengthPrefix => write!(f, "4 byte length prefix out of bounds"),
31            Self::LengthPrefixOverflow(len) => write!(f, "length prefix {len} overflowed usize"),
32            Self::InsufficientLength(len) => {
33                write!(f, "insufficient data for length (needed {len} bytes)")
34            }
35            Self::InsufficientFrame(len) => {
36                write!(f, "insufficient data for frame (needed {len} bytes)")
37            }
38        }
39    }
40}
41
42#[derive(Debug)]
43#[cfg_attr(test, derive(PartialEq))]
44pub(crate) struct InvalidFrame {
45    what: InvalidFrameWhat,
46    range: Range<usize>,
47    size: usize,
48}
49
50impl fmt::Display for InvalidFrame {
51    #[inline]
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(
54            f,
55            "{} {}-{} (has {} bytes)",
56            self.what, self.range.start, self.range.end, self.size
57        )
58    }
59}
60
61#[must_use = "Writer must be consumed with Writer::flush to have an effect on the underlying buffer"]
62pub(crate) struct Writer<'a> {
63    start: usize,
64    buf: &'a mut Buf,
65}
66
67impl Writer<'_> {
68    /// Write the fixed envelope of a message to the current frame.
69    ///
70    /// The envelope never depends on the negotiated format, see the [wire
71    /// format].
72    ///
73    /// [wire format]: crate::api#wire-format
74    #[inline]
75    pub(crate) fn envelope<T>(&mut self, value: &T) -> Result<(), format::Error>
76    where
77        T: ?Sized + Encode<Binary>,
78    {
79        format::encode_envelope(&mut self.buf.buffer, value)
80    }
81
82    /// Write the body of a message to the current frame using `format`.
83    #[inline]
84    pub(crate) fn body<T>(&mut self, format: Format, value: &T) -> Result<(), format::Error>
85    where
86        T: ?Sized + EncodeBody,
87    {
88        format.encode(&mut self.buf.buffer, value)
89    }
90
91    /// Finalize the current frame.
92    #[inline]
93    pub(crate) fn flush(self) {
94        let mut this = ManuallyDrop::new(self);
95        let start = this.start;
96        this.buf.done(start);
97    }
98}
99
100impl Drop for Writer<'_> {
101    #[inline]
102    fn drop(&mut self) {
103        self.buf.reset(self.start);
104    }
105}
106
107/// A length-prefixed buffer which keeps track of the start of each frame and
108/// allows them to be iterated over.
109#[derive(Default)]
110pub(crate) struct Buf {
111    buffer: Vec<u8>,
112    read: Cell<usize>,
113}
114
115impl Buf {
116    /// Start a write.
117    pub(crate) fn writer(&mut self) -> Writer<'_> {
118        if self.read.get() == self.buffer.len() {
119            self.buffer.clear();
120            self.read.set(0);
121        }
122
123        let start = self.buffer.len();
124        self.buffer.extend_from_slice(&[0; mem::size_of::<u32>()]);
125        Writer { start, buf: self }
126    }
127
128    #[inline]
129    #[cfg(test)]
130    pub(crate) fn is_empty(&self) -> bool {
131        // NB: Read should never exceed the length of the buffer.
132        debug_assert!(self.read.get() <= self.buffer.len());
133        self.read.get() >= self.buffer.len()
134    }
135
136    fn len_at_mut(&mut self, at: usize) -> Option<&mut [u8; 4]> {
137        let bytes = self.buffer.get_mut(at..at + mem::size_of::<u32>())?;
138        Some(unsafe { &mut *bytes.as_mut_ptr().cast() })
139    }
140
141    /// Mark an outgoing frame as done from the previous start point.
142    ///
143    /// If no start point is recorded, calling this method does nothing.
144    #[inline]
145    fn done(&mut self, start: usize) {
146        let delta = self
147            .buffer
148            .len()
149            .saturating_sub(start)
150            .saturating_sub(mem::size_of::<u32>());
151
152        let l = u32::try_from(delta).unwrap_or(u32::MAX).to_le_bytes();
153
154        let Some(len) = self.len_at_mut(start) else {
155            return;
156        };
157
158        *len = l;
159    }
160
161    /// Reset the buffer to the previous start point.
162    ///
163    /// If no start point is set, this method does nothing.
164    #[inline]
165    fn reset(&mut self, start: usize) {
166        self.buffer.truncate(start);
167    }
168
169    #[inline]
170    pub(crate) fn clear(&mut self) {
171        self.buffer.clear();
172        self.read.set(0);
173    }
174
175    /// Release any allocation beyond `capacity`.
176    ///
177    /// A single large message would otherwise pin its allocation for as long as
178    /// the buffer is pooled.
179    #[inline]
180    pub(crate) fn shrink_to(&mut self, capacity: usize) {
181        self.buffer.shrink_to(capacity);
182    }
183
184    /// Get the next frame starting at the given location.
185    #[inline]
186    pub(crate) fn read(&self) -> Result<Option<&[u8]>, InvalidFrame> {
187        let read = self.read.get();
188
189        if self.buffer.len() == read {
190            return Ok(None);
191        }
192
193        let Some(tail) = self.buffer.get(read..) else {
194            return Err(InvalidFrame {
195                what: InvalidFrameWhat::ReadPosition(read),
196                range: 0..read,
197                size: self.buffer.len(),
198            });
199        };
200
201        let Some((head, tail)) = tail.split_at_checked(mem::size_of::<u32>()) else {
202            return Err(InvalidFrame {
203                what: InvalidFrameWhat::InsufficientLength(mem::size_of::<u32>()),
204                range: 0..read,
205                size: self.buffer.len(),
206            });
207        };
208
209        let frame = read..read + mem::size_of::<u32>();
210
211        let &[a, b, c, d] = head else {
212            return Err(InvalidFrame {
213                what: InvalidFrameWhat::LengthPrefix,
214                range: frame.clone(),
215                size: self.buffer.len(),
216            });
217        };
218
219        let len = u32::from_le_bytes([a, b, c, d]);
220
221        let Ok(len) = usize::try_from(len) else {
222            return Err(InvalidFrame {
223                what: InvalidFrameWhat::LengthPrefixOverflow(len),
224                range: frame.clone(),
225                size: self.buffer.len(),
226            });
227        };
228
229        let Some(out) = tail.get(..len) else {
230            return Err(InvalidFrame {
231                what: InvalidFrameWhat::InsufficientFrame(len),
232                range: frame.start..frame.end + len,
233                size: self.buffer.len(),
234            });
235        };
236
237        let next = read
238            .saturating_add(mem::size_of::<u32>())
239            .saturating_add(len);
240
241        self.read.set(next);
242        Ok(Some(out))
243    }
244}
245
246pub(crate) struct BufPool {
247    pool: RefCell<Vec<Buf>>,
248    /// Buffers are shrunk back down to this when they are returned, so a single
249    /// large message does not pin its allocation for the lifetime of the
250    /// connection.
251    max_capacity: usize,
252}
253
254impl BufPool {
255    /// Construct a pool which shrinks returned buffers to `max_capacity`.
256    #[inline]
257    pub(crate) fn new(max_capacity: usize) -> Self {
258        Self {
259            pool: RefCell::new(Vec::new()),
260            max_capacity,
261        }
262    }
263
264    /// Try to run the given closure with a pool from the buffer.
265    ///
266    /// If the closure errors, the pool is returned.
267    #[inline]
268    pub(crate) fn with<E>(&self, f: impl FnOnce(&mut Buf) -> Result<(), E>) -> Result<Buf, E> {
269        let mut buf = self.get();
270        let result = f(&mut buf);
271
272        match result {
273            Ok(()) => Ok(buf),
274            Err(err) => {
275                self.put(buf);
276                Err(err)
277            }
278        }
279    }
280
281    #[inline]
282    pub(crate) fn get(&self) -> Buf {
283        self.pool.borrow_mut().pop().unwrap_or_default()
284    }
285
286    #[inline]
287    pub(crate) fn put(&self, mut buf: Buf) {
288        buf.clear();
289        buf.shrink_to(self.max_capacity);
290        self.pool.borrow_mut().push(buf);
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use alloc::string::{String, ToString};
297
298    use musli::Encode;
299
300    use super::{Buf, BufPool};
301    use crate::api::Format;
302
303    /// A pooled buffer which grew past the pool's capacity must give the
304    /// allocation back when it is returned, or one large message would pin it
305    /// for the lifetime of the connection.
306    #[test]
307    fn test_pool_shrinks_returned_buffers() {
308        let pool = BufPool::new(16);
309
310        let mut buf = pool.get();
311        buf.buffer.extend_from_slice(&[0; 1024]);
312        assert!(buf.buffer.capacity() >= 1024);
313
314        pool.put(buf);
315
316        let buf = pool.get();
317        assert!(buf.buffer.is_empty());
318
319        assert!(
320            buf.buffer.capacity() <= 16,
321            "Expected the allocation to be released, got {}",
322            buf.buffer.capacity()
323        );
324    }
325
326    #[test]
327    fn test_empty_buf() {
328        let buf = Buf::default();
329        assert!(buf.is_empty());
330        assert_eq!(buf.read(), Ok(None));
331    }
332
333    #[derive(Encode, musli::Decode)]
334    struct Message {
335        a: u32,
336        b: String,
337    }
338
339    #[test]
340    fn test_two_elements() {
341        let mut buf = Buf::default();
342
343        assert!(buf.is_empty());
344        assert_eq!(buf.read(), Ok(None));
345
346        // Buffer not consumed, so should leave empty.
347        buf.writer()
348            .body(
349                Format::DEFAULT,
350                &Message {
351                    a: 42,
352                    b: "hello".to_string(),
353                },
354            )
355            .unwrap();
356
357        assert!(buf.is_empty());
358        assert_eq!(buf.read(), Ok(None));
359
360        // Buffer consumed, so should be available for reading.
361        let mut writer = buf.writer();
362        writer
363            .body(
364                Format::DEFAULT,
365                &Message {
366                    a: 42,
367                    b: "hello".to_string(),
368                },
369            )
370            .unwrap();
371
372        writer.flush();
373
374        assert!(!buf.is_empty());
375        assert!(matches!(buf.read(), Ok(Some(..))));
376
377        assert!(buf.is_empty());
378        assert_eq!(buf.read(), Ok(None));
379    }
380}