Skip to main content

wasmtime/runtime/component/concurrent/futures_and_streams/
buffers.rs

1use crate::prelude::*;
2#[cfg(feature = "component-model-bytes")]
3use bytes::{Bytes, BytesMut};
4use core::mem::{self, MaybeUninit};
5use core::slice;
6
7// Inner module here to restrict possible readers of the fields of
8// `UntypedWriteBuffer`.
9pub use untyped::*;
10mod untyped {
11    use super::WriteBuffer;
12    use crate::vm::SendSyncPtr;
13    use crate::{Result, bail};
14    use core::any::TypeId;
15    use core::marker;
16    use core::mem;
17    use core::ptr::NonNull;
18
19    /// Helper structure to type-erase the `T` in `WriteBuffer<T>`.
20    ///
21    /// This is constructed with a `&mut dyn WriteBuffer<T>` and then can only
22    /// be viewed as `&mut dyn WriteBuffer<T>` as well. The `T`, however, is
23    /// carried through methods rather than the struct itself.
24    ///
25    /// Note that this structure has a lifetime `'a` which forces an active
26    /// borrow on the original buffer passed in.
27    pub struct UntypedWriteBuffer<'a> {
28        element_type_id: TypeId,
29        buf: SendSyncPtr<dyn WriteBuffer<()>>,
30        _marker: marker::PhantomData<&'a mut dyn WriteBuffer<()>>,
31    }
32
33    /// Helper structure to transmute between `WriteBuffer<T>` and
34    /// `WriteBuffer<()>`.
35    union ReinterpretWriteBuffer<T> {
36        typed: *mut dyn WriteBuffer<T>,
37        untyped: *mut dyn WriteBuffer<()>,
38    }
39
40    impl<'a> UntypedWriteBuffer<'a> {
41        /// Creates a new `UntypedWriteBuffer` from the `buf` provided.
42        ///
43        /// The returned value can be used with the `get_mut` method to get the
44        /// original write buffer back.
45        pub fn new<T: 'static>(buf: &'a mut dyn WriteBuffer<T>) -> UntypedWriteBuffer<'a> {
46            UntypedWriteBuffer {
47                element_type_id: TypeId::of::<T>(),
48                // SAFETY: this is `unsafe` due to reading union fields. That
49                // is safe here because `typed` and `untyped` have the same size
50                // and we're otherwise reinterpreting a raw pointer with a type
51                // parameter to one without one.
52                buf: SendSyncPtr::new(
53                    NonNull::new(unsafe {
54                        let r = ReinterpretWriteBuffer { typed: buf };
55                        assert_eq!(mem::size_of_val(&r.typed), mem::size_of_val(&r.untyped));
56                        r.untyped
57                    })
58                    .unwrap(),
59                ),
60                _marker: marker::PhantomData,
61            }
62        }
63
64        /// Acquires the underlying `WriteBuffer<T>` this was created with.
65        ///
66        /// Returns an error if `T` does not match the type that this was
67        /// created with.
68        pub fn get_mut<T: 'static>(&mut self) -> Result<&mut dyn WriteBuffer<T>> {
69            if self.element_type_id != TypeId::of::<T>() {
70                bail!("host stream item type mismatch")
71            }
72
73            // SAFETY: the `T` has been checked with `TypeId` and this
74            // structure also is proof of valid existence of the original
75            // `&mut WriteBuffer<T>`, so taking the raw pointer back to a safe
76            // reference is valid.
77            Ok(unsafe {
78                &mut *ReinterpretWriteBuffer {
79                    untyped: self.buf.as_ptr(),
80                }
81                .typed
82            })
83        }
84    }
85}
86
87/// Trait representing a buffer which may be written to a `StreamWriter`.
88///
89/// See also [`crate::component::StreamProducer`].
90///
91/// # Unsafety
92///
93/// This trait is unsafe due to the contract of the `take` function. This trait
94/// is only safe to implement if the `take` function is implemented correctly,
95/// namely that all the items passed to the closure are fully initialized for
96/// `T`.
97pub unsafe trait WriteBuffer<T>: Send + Sync + 'static {
98    /// Slice of items remaining to be read.
99    fn remaining(&self) -> &[T];
100
101    /// Skip and drop the specified number of items.
102    fn skip(&mut self, count: usize);
103
104    /// Take ownership of the specified number of items.
105    ///
106    /// This function will take `count` items from `self` and pass them as a
107    /// contiguous slice to the closure `fun` provided. The `fun` closure may
108    /// assume that the items are all fully initialized and available to read.
109    /// It is expected that `fun` will read all the items provided. Any items
110    /// that aren't read by `fun` will be leaked.
111    ///
112    /// # Panics
113    ///
114    /// Panics if `count` is larger than `self.remaining()`. If `fun` panics
115    /// then items may be leaked.
116    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<T>]));
117}
118
119/// Trait representing a buffer which may be used to read from a `StreamReader`.
120///
121/// See also [`crate::component::Source`].
122pub trait ReadBuffer<T>: Send + Sync + 'static {
123    /// Move the specified items into this buffer.
124    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I);
125
126    /// Number of items which may be read before this buffer is full.
127    fn remaining_capacity(&self) -> usize;
128
129    /// Move (i.e. take ownership of) the specified items into this buffer.
130    ///
131    /// This method will drain `count` items from the `input` provided and move
132    /// ownership into this buffer.
133    ///
134    /// # Panics
135    ///
136    /// This method will panic if `count` is larger than
137    /// `self.remaining_capacity()` or if it's larger than `input.remaining()`.
138    fn move_from(&mut self, input: &mut dyn WriteBuffer<T>, count: usize);
139}
140
141pub(super) struct Extender<'a, B>(pub(super) &'a mut B);
142
143impl<T, B: ReadBuffer<T>> Extend<T> for Extender<'_, B> {
144    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
145        self.0.extend(iter)
146    }
147}
148
149// SAFETY: the `take` implementation below guarantees that the `fun` closure is
150// provided with fully initialized items.
151unsafe impl<T: Send + Sync + 'static> WriteBuffer<T> for Option<T> {
152    fn remaining(&self) -> &[T] {
153        if let Some(me) = self {
154            slice::from_ref(me)
155        } else {
156            &[]
157        }
158    }
159
160    fn skip(&mut self, count: usize) {
161        match count {
162            0 => {}
163            1 => {
164                assert!(self.is_some());
165                *self = None;
166            }
167            _ => panic!("cannot skip more than {} item(s)", self.remaining().len()),
168        }
169    }
170
171    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<T>])) {
172        match count {
173            0 => fun(&mut []),
174            1 => {
175                let mut item = MaybeUninit::new(self.take().unwrap());
176                fun(slice::from_mut(&mut item));
177            }
178            _ => panic!("cannot forget more than {} item(s)", self.remaining().len()),
179        }
180    }
181}
182
183impl<T: Send + Sync + 'static> ReadBuffer<T> for Option<T> {
184    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
185        let mut iter = iter.into_iter();
186        if self.is_none() {
187            *self = iter.next();
188        }
189        assert!(iter.next().is_none());
190    }
191
192    fn remaining_capacity(&self) -> usize {
193        if self.is_some() { 0 } else { 1 }
194    }
195
196    fn move_from(&mut self, input: &mut dyn WriteBuffer<T>, count: usize) {
197        match count {
198            0 => {}
199            1 => {
200                assert!(self.is_none());
201                input.take(1, &mut |slice| {
202                    // SAFETY: Per the `WriteBuffer` trait contract this block
203                    // has ownership of the items in `slice` and they're all
204                    // valid to take.
205                    unsafe {
206                        *self = Some(slice[0].assume_init_read());
207                    }
208                });
209            }
210            _ => panic!(
211                "cannot take more than {} item(s)",
212                self.remaining_capacity()
213            ),
214        }
215    }
216}
217
218/// A `WriteBuffer` implementation, backed by a `Vec<u8>`, a position, and a limit.
219pub struct SliceBuffer {
220    buffer: Vec<u8>,
221    offset: usize,
222    limit: usize,
223}
224
225impl SliceBuffer {
226    pub fn new(buffer: Vec<u8>, offset: usize, limit: usize) -> Self {
227        assert!(offset <= limit);
228        assert!(limit <= buffer.len());
229        Self {
230            buffer,
231            offset,
232            limit,
233        }
234    }
235
236    pub fn into_parts(self) -> (Vec<u8>, usize, usize) {
237        (self.buffer, self.offset, self.limit)
238    }
239}
240
241// SAFETY: the `take` implementation below guarantees that the `fun` closure is
242// provided with fully initialized items due to all elements in the slice being
243// initialized.
244unsafe impl WriteBuffer<u8> for SliceBuffer {
245    fn remaining(&self) -> &[u8] {
246        &self.buffer[self.offset..self.limit]
247    }
248
249    fn skip(&mut self, count: usize) {
250        assert!(self.offset + count <= self.limit);
251        self.offset += count;
252    }
253
254    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<u8>])) {
255        assert!(count <= self.remaining().len());
256        self.offset += count;
257        // SAFETY: Transmuting from `&[u8]` to `&[MaybeUninit<u8>]` should
258        // always be sound.
259        fun(unsafe {
260            mem::transmute::<&[u8], &[MaybeUninit<u8>]>(
261                &self.buffer[self.offset - count..self.offset],
262            )
263        });
264    }
265}
266
267/// A `WriteBuffer` implementation, backed by a `Vec`.
268pub struct VecBuffer<T> {
269    buffer: Vec<MaybeUninit<T>>,
270    offset: usize,
271}
272
273impl<T> Default for VecBuffer<T> {
274    fn default() -> Self {
275        Self::with_capacity(0)
276    }
277}
278
279impl<T> VecBuffer<T> {
280    /// Create a new instance with the specified capacity.
281    pub fn with_capacity(capacity: usize) -> Self {
282        Self {
283            buffer: Vec::with_capacity(capacity),
284            offset: 0,
285        }
286    }
287
288    /// Reset the state of this buffer, removing all items and preserving its
289    /// capacity.
290    pub fn reset(&mut self) {
291        self.skip_(self.remaining_().len());
292        self.buffer.clear();
293        self.offset = 0;
294    }
295
296    fn remaining_(&self) -> &[T] {
297        // SAFETY: This relies on the invariant (upheld in the other methods of
298        // this type) that all the elements from `self.offset` onward are
299        // initialized and valid for `self.buffer`.
300        unsafe { mem::transmute::<&[MaybeUninit<T>], &[T]>(&self.buffer[self.offset..]) }
301    }
302
303    fn skip_(&mut self, count: usize) {
304        assert!(count <= self.remaining_().len());
305        for item in &mut self.buffer[self.offset..][..count] {
306            // Note that the offset is incremented first here to ensure that if
307            // any destructors panic we don't attempt to re-drop the item.
308            self.offset += 1;
309            // SAFETY: See comment in `Self::remaining`
310            unsafe {
311                item.assume_init_drop();
312            }
313        }
314    }
315}
316
317// SAFETY: the `take` implementation below guarantees that the `fun` closure is
318// provided with fully initialized items due to `self.offset`-and-onwards being
319// always initialized.
320unsafe impl<T: Send + Sync + 'static> WriteBuffer<T> for VecBuffer<T> {
321    fn remaining(&self) -> &[T] {
322        self.remaining_()
323    }
324
325    fn skip(&mut self, count: usize) {
326        self.skip_(count)
327    }
328
329    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<T>])) {
330        assert!(count <= self.remaining().len());
331        // Note that the offset here is incremented before `fun` is called to
332        // ensure that if `fun` panics that the items are still considered
333        // transferred.
334        self.offset += count;
335        fun(&self.buffer[self.offset - count..self.offset]);
336    }
337}
338
339impl<T> From<Vec<T>> for VecBuffer<T> {
340    fn from(buffer: Vec<T>) -> Self {
341        Self {
342            // SAFETY: Transmuting from `Vec<T>` to `Vec<MaybeUninit<T>>` should
343            // be sound for any `T`.
344            buffer: unsafe { mem::transmute::<Vec<T>, Vec<MaybeUninit<T>>>(buffer) },
345            offset: 0,
346        }
347    }
348}
349
350impl<T> Drop for VecBuffer<T> {
351    fn drop(&mut self) {
352        self.reset();
353    }
354}
355
356impl<T: Send + Sync + 'static> ReadBuffer<T> for Vec<T> {
357    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
358        Extend::extend(self, iter)
359    }
360
361    fn remaining_capacity(&self) -> usize {
362        self.capacity().checked_sub(self.len()).unwrap()
363    }
364
365    fn move_from(&mut self, input: &mut dyn WriteBuffer<T>, count: usize) {
366        assert!(count <= self.remaining_capacity());
367        input.take(count, &mut |slice| {
368            for item in slice {
369                // SAFETY: Per the `WriteBuffer` implementation contract this
370                // function has exclusive ownership of all items in `slice` so
371                // this is safe to take and transfer them here.
372                self.push(unsafe { item.assume_init_read() });
373            }
374        });
375    }
376}
377
378// SAFETY: the `take` implementation below guarantees that the `fun` closure is
379// provided with fully initialized items.
380#[cfg(feature = "component-model-bytes")]
381unsafe impl WriteBuffer<u8> for Bytes {
382    fn remaining(&self) -> &[u8] {
383        self
384    }
385
386    fn skip(&mut self, count: usize) {
387        let _prefix = self.split_to(count);
388    }
389
390    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<u8>])) {
391        let prefix = self.split_to(count);
392        fun(unsafe_byte_slice(&prefix));
393    }
394}
395
396// SAFETY: the `take` implementation below guarantees that the `fun` closure is
397// provided with fully initialized items.
398#[cfg(feature = "component-model-bytes")]
399unsafe impl WriteBuffer<u8> for BytesMut {
400    fn remaining(&self) -> &[u8] {
401        self
402    }
403
404    fn skip(&mut self, count: usize) {
405        let _prefix = self.split_to(count);
406    }
407
408    fn take(&mut self, count: usize, fun: &mut dyn FnMut(&[MaybeUninit<u8>])) {
409        let prefix = self.split_to(count);
410        fun(unsafe_byte_slice(&prefix));
411    }
412}
413
414#[cfg(feature = "component-model-bytes")]
415impl ReadBuffer<u8> for BytesMut {
416    fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
417        Extend::extend(self, iter)
418    }
419
420    fn remaining_capacity(&self) -> usize {
421        self.capacity().checked_sub(self.len()).unwrap()
422    }
423
424    fn move_from(&mut self, input: &mut dyn WriteBuffer<u8>, count: usize) {
425        assert!(count <= self.remaining_capacity());
426        input.take(count, &mut |slice| {
427            // SAFETY: per the contract of `WriteBuffer` all the elements of
428            // the input `slice` are fully initialized so this is safe
429            // to reinterpret the slice.
430            let slice = unsafe { mem::transmute::<&[MaybeUninit<u8>], &[u8]>(slice) };
431            self.extend_from_slice(slice);
432        });
433    }
434}
435
436#[cfg(feature = "component-model-bytes")]
437fn unsafe_byte_slice(slice: &[u8]) -> &[MaybeUninit<u8>] {
438    // SAFETY: it's always safe to interpret a slice of items as a
439    // possibly-initialized slice of items.
440    unsafe { mem::transmute::<&[u8], &[MaybeUninit<u8>]>(slice) }
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446
447    #[test]
448    fn test_vec_buffer_take() {
449        let mut buf = VecBuffer::from(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
450        let mut dst = Vec::new();
451        dst.reserve(1);
452        dst.move_from(&mut buf, 1);
453        assert_eq!(buf.remaining().len(), 2);
454        assert_eq!(dst.len(), 1);
455        None.move_from(&mut buf, 1);
456        assert_eq!(buf.remaining().len(), 1);
457        assert_eq!(dst.len(), 1);
458    }
459
460    #[test]
461    fn test_slice_buffer_take() {
462        let mut buf = SliceBuffer::new(vec![1, 2, 3], 0, 3);
463        let mut dst = Vec::new();
464        dst.reserve(1);
465        dst.move_from(&mut buf, 1);
466        assert_eq!(buf.remaining().len(), 2);
467        assert_eq!(dst.len(), 1);
468    }
469
470    #[test]
471    #[cfg(feature = "component-model-bytes")]
472    fn test_cursor_bytes_take() {
473        let mut buf = Bytes::from(&b"123"[..]);
474        let mut dst = Vec::new();
475        dst.reserve(1);
476        dst.move_from(&mut buf, 1);
477        assert_eq!(buf.remaining().len(), 2);
478        assert_eq!(dst.len(), 1);
479
480        let mut dst = BytesMut::new();
481        dst.reserve(1);
482        dst.move_from(&mut buf, 1);
483        assert_eq!(buf.remaining().len(), 1);
484        assert_eq!(dst.len(), 1);
485    }
486
487    #[test]
488    #[cfg(feature = "component-model-bytes")]
489    fn test_cursor_bytes_mut_take() {
490        let mut buf = BytesMut::from(&b"123"[..]);
491        let mut dst = Vec::new();
492        dst.reserve(1);
493        dst.move_from(&mut buf, 1);
494        assert_eq!(buf.remaining().len(), 2);
495        assert_eq!(dst.len(), 1);
496
497        let mut dst = BytesMut::new();
498        dst.reserve(1);
499        dst.move_from(&mut buf, 1);
500        assert_eq!(buf.remaining().len(), 1);
501        assert_eq!(dst.len(), 1);
502    }
503}