Skip to main content

tokio_uring_xitca/buf/
bounded.rs

1use super::{IoBuf, IoBufMut, Slice};
2
3use core::{ops, ptr, slice};
4
5/// A possibly bounded view into an owned [`IoBuf`] buffer.
6///
7/// Because buffers are passed by ownership to the runtime, Rust's slice API
8/// (`&buf[..]`) cannot be used. Instead, `tokio-uring` provides an owned slice
9/// API: [`.slice()`]. The method takes ownership of the buffer and returns a
10/// [`Slice`] value that tracks the requested range.
11///
12/// This trait provides a generic way to use buffers and `Slice` views
13/// into such buffers with `io-uring` operations.
14///
15/// [`.slice()`]: BoundedBuf::slice
16pub trait BoundedBuf: Unpin + 'static {
17    /// The type of the underlying buffer.
18    type Buf: IoBuf;
19
20    /// The type representing the range bounds of the view.
21    type Bounds: ops::RangeBounds<usize>;
22
23    /// Returns a view of the buffer with the specified range.
24    ///
25    /// This method is similar to Rust's slicing (`&buf[..]`), but takes
26    /// ownership of the buffer. The range bounds are specified against
27    /// the possibly offset beginning of the `self` view into the buffer
28    /// and the end bound, if specified, must not exceed the view's total size.
29    /// Note that the range may extend into the uninitialized part of the
30    /// buffer, but it must start (if so bounded) in the initialized part
31    /// or immediately adjacent to it.
32    ///
33    /// # Panics
34    ///
35    /// If the range is invalid with regard to the recipient's total size or
36    /// the length of its initialized part, the implementation of this method
37    /// should panic.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use tokio_uring_xitca::buf::BoundedBuf;
43    ///
44    /// let buf = b"hello world".to_vec();
45    /// let slice = buf.slice(5..10);
46    /// assert_eq!(&slice[..], b" worl");
47    /// let slice = slice.slice(1..3);
48    /// assert_eq!(&slice[..], b"wo");
49    /// ```
50    fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self::Buf>;
51
52    /// Returns a `Slice` with the view's full range.
53    ///
54    /// This method is to be used by the `tokio-uring` runtime and it is not
55    /// expected for users to call it directly.
56    fn slice_full(self) -> Slice<Self::Buf>;
57
58    /// Gets a reference to the underlying buffer.
59    fn get_buf(&self) -> &Self::Buf;
60
61    /// Returns the range bounds for this view.
62    fn bounds(&self) -> Self::Bounds;
63
64    /// Constructs a view from an underlying buffer and range bounds.
65    fn from_buf_bounds(buf: Self::Buf, bounds: Self::Bounds) -> Self;
66
67    /// Like [`IoBuf::stable_ptr`],
68    /// but possibly offset to the view's starting position.
69    fn stable_ptr(&self) -> *const u8;
70
71    /// Number of initialized bytes available via this view.
72    fn bytes_init(&self) -> usize;
73
74    /// Total size of the view, including uninitialized memory, if any.
75    fn bytes_total(&self) -> usize;
76
77    /// Returns a shared reference to the initialized portion of the buffer.
78    fn chunk(&self) -> &[u8] {
79        // Safety: BoundedBuf implementor guarantees stable_ptr points to valid
80        // memory and bytes_init bytes starting from that pointer are initialized.
81        unsafe { slice::from_raw_parts(self.stable_ptr(), self.bytes_init()) }
82    }
83}
84
85impl<T: IoBuf> BoundedBuf for T {
86    type Buf = Self;
87    type Bounds = ops::RangeFull;
88
89    fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self> {
90        use ops::Bound;
91
92        let begin = match range.start_bound() {
93            Bound::Included(&n) => n,
94            Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
95            Bound::Unbounded => 0,
96        };
97
98        assert!(begin < self.bytes_total());
99
100        let end = match range.end_bound() {
101            Bound::Included(&n) => n.checked_add(1).expect("out of range"),
102            Bound::Excluded(&n) => n,
103            Bound::Unbounded => self.bytes_total(),
104        };
105
106        assert!(end <= self.bytes_total());
107        assert!(begin <= self.bytes_init());
108
109        Slice::new(self, begin, end)
110    }
111
112    fn slice_full(self) -> Slice<Self> {
113        let end = self.bytes_total();
114        Slice::new(self, 0, end)
115    }
116
117    fn get_buf(&self) -> &Self {
118        self
119    }
120
121    fn bounds(&self) -> Self::Bounds {
122        ..
123    }
124
125    fn from_buf_bounds(buf: Self, _: ops::RangeFull) -> Self {
126        buf
127    }
128
129    fn stable_ptr(&self) -> *const u8 {
130        IoBuf::stable_ptr(self)
131    }
132
133    fn bytes_init(&self) -> usize {
134        IoBuf::bytes_init(self)
135    }
136
137    fn bytes_total(&self) -> usize {
138        IoBuf::bytes_total(self)
139    }
140}
141
142/// A possibly bounded view into an owned [`IoBufMut`] buffer.
143///
144/// This trait provides a generic way to use mutable buffers and `Slice` views
145/// into such buffers with `io-uring` operations.
146pub trait BoundedBufMut: BoundedBuf<Buf = Self::BufMut> {
147    /// The type of the underlying buffer.
148    type BufMut: IoBufMut;
149
150    /// Like [`IoBufMut::stable_mut_ptr`],
151    /// but possibly offset to the view's starting position.
152    fn stable_mut_ptr(&mut self) -> *mut u8;
153
154    /// Like [`IoBufMut::set_init`],
155    /// but the position is possibly offset to the view's starting position.
156    ///
157    /// # Safety
158    ///
159    /// The caller must ensure that all bytes starting at `stable_mut_ptr()` up
160    /// to `pos` are initialized and owned by the buffer.
161    unsafe fn set_init(&mut self, pos: usize);
162
163    /// Copies the given byte slice into the buffer, starting at
164    /// this view's offset.
165    ///
166    /// # Panics
167    ///
168    /// If the slice's length exceeds the destination's remaining capacity,
169    /// this method panics.
170    fn put_slice(&mut self, src: &[u8]) {
171        let init = self.bytes_init();
172        assert!(self.bytes_total() - init >= src.len());
173
174        // Safety:
175        // dst pointer validity is ensured by stable_mut_ptr, offset by
176        // bytes_init() to write after already-initialized data;
177        // the length is checked to not exceed the remaining capacity;
178        // src (immutable) and dst (mutable) cannot point to overlapping memory;
179        // after copying, the new initialized watermark is set accordingly.
180        unsafe {
181            let dst = self.stable_mut_ptr().add(init);
182            ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
183            self.set_init(init + src.len());
184        }
185    }
186}
187
188impl<T: IoBufMut> BoundedBufMut for T {
189    type BufMut = T;
190
191    fn stable_mut_ptr(&mut self) -> *mut u8 {
192        IoBufMut::stable_mut_ptr(self)
193    }
194
195    unsafe fn set_init(&mut self, pos: usize) {
196        // # Safety
197        //
198        // implementor of T must make sure it's soundness
199        unsafe { IoBufMut::set_init(self, pos) }
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    #[test]
208    fn put_slice_appends() {
209        let mut buf = Vec::with_capacity(64);
210        buf.put_slice(b"hello");
211        assert_eq!(&buf, b"hello");
212
213        buf.put_slice(b" world");
214        assert_eq!(&buf, b"hello world");
215    }
216
217    #[test]
218    fn put_slice_empty() {
219        let mut buf = Vec::with_capacity(16);
220        buf.put_slice(b"");
221        assert!(buf.is_empty());
222
223        buf.put_slice(b"abc");
224        assert_eq!(&buf, b"abc");
225
226        buf.put_slice(b"");
227        assert_eq!(&buf, b"abc");
228    }
229
230    #[test]
231    fn put_slice_fills_capacity() {
232        let mut buf = Vec::with_capacity(5);
233        buf.put_slice(b"ab");
234        buf.put_slice(b"cde");
235        assert_eq!(&buf, b"abcde");
236        assert_eq!(buf.len(), 5);
237    }
238
239    #[test]
240    #[should_panic]
241    fn put_slice_exceeds_capacity() {
242        let mut buf = Vec::with_capacity(4);
243        buf.put_slice(b"abcde");
244    }
245
246    #[test]
247    fn chunk_returns_initialized() {
248        let buf = b"hello".to_vec();
249        assert_eq!(buf.chunk(), b"hello");
250    }
251
252    #[test]
253    fn chunk_empty() {
254        let buf = Vec::<u8>::with_capacity(16);
255        assert_eq!(buf.chunk(), b"");
256    }
257
258    #[test]
259    fn chunk_after_put_slice() {
260        let mut buf = Vec::with_capacity(32);
261        buf.put_slice(b"foo");
262        buf.put_slice(b"bar");
263        assert_eq!(buf.chunk(), b"foobar");
264    }
265}