fusio_core/buf/
mod.rs

1pub mod slice;
2
3#[cfg(feature = "alloc")]
4use alloc::vec::Vec;
5use core::ops::{Bound, RangeBounds};
6
7use slice::{Buf, BufLayout, BufMut, BufMutLayout};
8
9use crate::{maybe::MaybeOwned, MaybeSend};
10
11/// A poll-based I/O and completion-based I/O compatible buffer.
12///
13/// The [`IoBuf`] trait is implemented by buffer types that can be used with [`crate::Read`].
14/// Fusio has already implemented this trait for common buffer types
15/// like `Vec<u8>`, `&[u8]`, `&mut [u8]`, `bytes::Bytes`, `bytes::BytesMut`. Not every buffer type
16/// may be able to be used in all async runtimes; fusio provides compile-time safety to
17/// ensure which buffer types are compatible with the async runtime.
18///
19/// # Examples
20///
21/// ```no_run
22/// use fusio_core::IoBuf;
23///
24/// fn inspect_buffer<B: IoBuf>(buf: &B) {
25///     println!("Buffer has {} bytes", buf.bytes_init());
26///     println!(
27///         "First 10 bytes: {:?}",
28///         &buf.as_slice()[..10.min(buf.bytes_init())]
29///     );
30/// }
31/// ```
32pub trait IoBuf: Unpin + Sized + MaybeOwned + MaybeSend {
33    fn as_ptr(&self) -> *const u8;
34
35    fn bytes_init(&self) -> usize;
36
37    fn as_slice(&self) -> &[u8] {
38        // SAFETY: The buffer is pinned and the bytes are initialized.
39        unsafe { core::slice::from_raw_parts(self.as_ptr(), self.bytes_init()) }
40    }
41
42    #[cfg(feature = "bytes")]
43    fn as_bytes(&self) -> bytes::Bytes {
44        bytes::Bytes::copy_from_slice(self.as_slice())
45    }
46
47    /// Slice the buffer without bounds checking.
48    ///
49    /// # Safety
50    ///
51    /// The caller must ensure that:
52    /// - The range is within the bounds of the buffer
53    /// - The buffer will be recovered using [`IoBuf::recover_from_slice`] before it drops
54    /// - The buffer is not accessed through any other means while sliced
55    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf;
56
57    /// Recover the original buffer from a sliced buffer.
58    ///
59    /// # Safety
60    ///
61    /// The caller must ensure that:
62    /// - The `buf` was created from the same buffer type using `slice_unchecked`
63    /// - The `buf` has not been modified in an incompatible way
64    /// - This is called exactly once for each `slice_unchecked` call
65    unsafe fn recover_from_slice(buf: Buf) -> Self;
66
67    fn calculate_bounds<R: RangeBounds<usize>>(&self, range: R) -> (usize, usize) {
68        let start = match range.start_bound() {
69            Bound::Included(&start) => start,
70            Bound::Excluded(&start) => start + 1,
71            Bound::Unbounded => 0,
72        };
73        let end = match range.end_bound() {
74            Bound::Included(&end) => end + 1,
75            Bound::Excluded(&end) => end,
76            Bound::Unbounded => self.bytes_init(),
77        };
78        (start, end)
79    }
80}
81
82/// Mutable version of [`IoBuf`] which is used with [`crate::Write`].
83pub trait IoBufMut: IoBuf {
84    fn as_mut_ptr(&mut self) -> *mut u8;
85
86    fn as_slice_mut(&mut self) -> &mut [u8] {
87        // SAFETY: The buffer is pinned and the bytes are initialized.
88        unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.bytes_init()) }
89    }
90
91    /// Slice the mutable buffer without bounds checking.
92    ///
93    /// # Safety
94    ///
95    /// The caller must ensure that:
96    /// - The range is within the bounds of the buffer
97    /// - The buffer will be recovered using [`IoBufMut::recover_from_slice_mut`] before it drops
98    /// - The buffer is not accessed through any other means while sliced
99    unsafe fn slice_mut_unchecked(self, range: impl RangeBounds<usize>) -> BufMut;
100
101    /// Recover the original mutable buffer from a sliced buffer.
102    ///
103    /// # Safety
104    ///
105    /// The caller must ensure that:
106    /// - The `buf` was created from the same buffer type using `slice_mut_unchecked`
107    /// - The `buf` has not been modified in an incompatible way
108    /// - This is called exactly once for each `slice_mut_unchecked` call
109    unsafe fn recover_from_slice_mut(buf: BufMut) -> Self;
110}
111
112#[cfg(feature = "alloc")]
113impl IoBuf for Vec<u8> {
114    fn as_ptr(&self) -> *const u8 {
115        self.as_ptr()
116    }
117
118    fn bytes_init(&self) -> usize {
119        self.len()
120    }
121
122    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
123        let (start, end) = self.calculate_bounds(range);
124        Buf {
125            layout: BufLayout::Vec(self),
126            start,
127            end,
128        }
129    }
130
131    unsafe fn recover_from_slice(buf: Buf) -> Self {
132        match buf.layout {
133            BufLayout::Vec(vec) => vec,
134            _ => unreachable!(),
135        }
136    }
137}
138
139#[cfg(feature = "alloc")]
140impl IoBufMut for Vec<u8> {
141    fn as_mut_ptr(&mut self) -> *mut u8 {
142        Vec::as_mut_ptr(self)
143    }
144
145    unsafe fn slice_mut_unchecked(self, range: impl RangeBounds<usize>) -> BufMut {
146        let (start, end) = self.calculate_bounds(range);
147        BufMut {
148            layout: BufMutLayout::Vec(self),
149            start,
150            end,
151        }
152    }
153
154    unsafe fn recover_from_slice_mut(buf: BufMut) -> Self {
155        match buf.layout {
156            BufMutLayout::Vec(vec) => vec,
157            _ => unreachable!(),
158        }
159    }
160}
161
162#[cfg(not(feature = "completion-based"))]
163impl IoBuf for &[u8] {
164    fn as_ptr(&self) -> *const u8 {
165        (*self).as_ptr()
166    }
167
168    fn bytes_init(&self) -> usize {
169        self.len()
170    }
171
172    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
173        let (start, end) = self.calculate_bounds(range);
174        Buf {
175            layout: BufLayout::Slice {
176                ptr: self.as_ptr() as *mut u8,
177                len: self.len(),
178            },
179            start,
180            end,
181        }
182    }
183
184    unsafe fn recover_from_slice(buf: Buf) -> Self {
185        match buf.layout {
186            BufLayout::Slice { ptr, len } => core::slice::from_raw_parts(ptr, len),
187            _ => unreachable!(),
188        }
189    }
190}
191
192#[cfg(not(feature = "completion-based"))]
193impl IoBuf for &mut [u8] {
194    fn as_ptr(&self) -> *const u8 {
195        <[u8]>::as_ptr(self)
196    }
197
198    fn bytes_init(&self) -> usize {
199        self.len()
200    }
201
202    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
203        let (start, end) = self.calculate_bounds(range);
204        Buf {
205            layout: BufLayout::Slice {
206                ptr: self.as_ptr() as *mut u8,
207                len: self.len(),
208            },
209            start,
210            end,
211        }
212    }
213
214    unsafe fn recover_from_slice(buf: Buf) -> Self {
215        match buf.layout {
216            BufLayout::Slice { ptr, len } => core::slice::from_raw_parts_mut(ptr as *mut u8, len),
217            _ => unreachable!(),
218        }
219    }
220}
221
222#[cfg(not(feature = "completion-based"))]
223impl IoBufMut for &mut [u8] {
224    fn as_mut_ptr(&mut self) -> *mut u8 {
225        <[u8]>::as_mut_ptr(self)
226    }
227
228    unsafe fn slice_mut_unchecked(self, range: impl RangeBounds<usize>) -> BufMut {
229        let (start, end) = self.calculate_bounds(range);
230        BufMut {
231            layout: BufMutLayout::Slice {
232                ptr: self.as_mut_ptr(),
233                len: self.len(),
234            },
235            start,
236            end,
237        }
238    }
239
240    unsafe fn recover_from_slice_mut(buf: BufMut) -> Self {
241        match buf.layout {
242            BufMutLayout::Slice { ptr, len } => core::slice::from_raw_parts_mut(ptr, len),
243            _ => unreachable!(),
244        }
245    }
246}
247
248#[cfg(feature = "completion-based")]
249impl IoBuf for &'static [u8] {
250    fn as_ptr(&self) -> *const u8 {
251        (*self).as_ptr()
252    }
253
254    fn bytes_init(&self) -> usize {
255        self.len()
256    }
257
258    #[cfg(feature = "bytes")]
259    fn as_bytes(&self) -> bytes::Bytes {
260        bytes::Bytes::from_static(self)
261    }
262
263    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
264        let (start, end) = self.calculate_bounds(range);
265        Buf {
266            layout: BufLayout::Slice {
267                ptr: self.as_ptr() as *mut u8,
268                len: self.len(),
269            },
270            start,
271            end,
272        }
273    }
274
275    unsafe fn recover_from_slice(buf: Buf) -> Self {
276        match buf.layout {
277            BufLayout::Slice { ptr, len } => core::slice::from_raw_parts(ptr, len),
278            _ => unreachable!(),
279        }
280    }
281}
282
283#[cfg(feature = "bytes")]
284impl IoBuf for bytes::Bytes {
285    fn as_ptr(&self) -> *const u8 {
286        <[u8]>::as_ptr(self)
287    }
288
289    fn bytes_init(&self) -> usize {
290        self.len()
291    }
292    fn as_bytes(&self) -> bytes::Bytes {
293        self.clone()
294    }
295
296    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
297        let start = match range.start_bound() {
298            Bound::Included(&start) => start,
299            Bound::Excluded(&start) => start + 1,
300            Bound::Unbounded => 0,
301        };
302        let end = match range.end_bound() {
303            Bound::Included(&end) => end + 1,
304            Bound::Excluded(&end) => end,
305            Bound::Unbounded => self.len(),
306        };
307        Buf {
308            layout: BufLayout::Bytes(self),
309            start,
310            end,
311        }
312    }
313
314    unsafe fn recover_from_slice(buf: Buf) -> Self {
315        match buf.layout {
316            BufLayout::Bytes(bytes) => bytes,
317            _ => unreachable!(),
318        }
319    }
320}
321
322#[cfg(feature = "bytes")]
323impl IoBuf for bytes::BytesMut {
324    fn as_ptr(&self) -> *const u8 {
325        <[u8]>::as_ptr(self)
326    }
327
328    fn bytes_init(&self) -> usize {
329        self.len()
330    }
331
332    fn as_bytes(&self) -> bytes::Bytes {
333        self.clone().freeze()
334    }
335
336    unsafe fn slice_unchecked(self, range: impl RangeBounds<usize>) -> Buf {
337        let start = match range.start_bound() {
338            Bound::Included(&start) => start,
339            Bound::Excluded(&start) => start + 1,
340            Bound::Unbounded => 0,
341        };
342        let end = match range.end_bound() {
343            Bound::Included(&end) => end + 1,
344            Bound::Excluded(&end) => end,
345            Bound::Unbounded => self.len(),
346        };
347        Buf {
348            layout: BufLayout::BytesMut(self),
349            start,
350            end,
351        }
352    }
353
354    unsafe fn recover_from_slice(buf: Buf) -> Self {
355        match buf.layout {
356            BufLayout::BytesMut(bytes) => bytes,
357            _ => unreachable!(),
358        }
359    }
360}
361
362#[cfg(feature = "bytes")]
363impl IoBufMut for bytes::BytesMut {
364    fn as_mut_ptr(&mut self) -> *mut u8 {
365        <[u8]>::as_mut_ptr(self)
366    }
367
368    unsafe fn slice_mut_unchecked(self, range: impl RangeBounds<usize>) -> BufMut {
369        let start = match range.start_bound() {
370            Bound::Included(&start) => start,
371            Bound::Excluded(&start) => start + 1,
372            Bound::Unbounded => 0,
373        };
374        let end = match range.end_bound() {
375            Bound::Included(&end) => end + 1,
376            Bound::Excluded(&end) => end,
377            Bound::Unbounded => self.len(),
378        };
379        BufMut {
380            layout: BufMutLayout::BytesMut(self),
381            start,
382            end,
383        }
384    }
385
386    unsafe fn recover_from_slice_mut(buf: BufMut) -> Self {
387        match buf.layout {
388            BufMutLayout::BytesMut(bytes) => bytes,
389            _ => unreachable!(),
390        }
391    }
392}