tokio_uring/buf/
bounded.rs

1use super::{IoBuf, IoBufMut, Slice};
2
3use std::ops;
4use std::ptr;
5
6/// A possibly bounded view into an owned [`IoBuf`] buffer.
7///
8/// Because buffers are passed by ownership to the runtime, Rust's slice API
9/// (`&buf[..]`) cannot be used. Instead, `tokio-uring` provides an owned slice
10/// API: [`.slice()`]. The method takes ownership of the buffer and returns a
11/// [`Slice`] value that tracks the requested range.
12///
13/// This trait provides a generic way to use buffers and `Slice` views
14/// into such buffers with `io-uring` operations.
15///
16/// [`.slice()`]: BoundedBuf::slice
17pub trait BoundedBuf: Unpin + 'static {
18    /// The type of the underlying buffer.
19    type Buf: IoBuf;
20
21    /// The type representing the range bounds of the view.
22    type Bounds: ops::RangeBounds<usize>;
23
24    /// Returns a view of the buffer with the specified range.
25    ///
26    /// This method is similar to Rust's slicing (`&buf[..]`), but takes
27    /// ownership of the buffer. The range bounds are specified against
28    /// the possibly offset beginning of the `self` view into the buffer
29    /// and the end bound, if specified, must not exceed the view's total size.
30    /// Note that the range may extend into the uninitialized part of the
31    /// buffer, but it must start (if so bounded) in the initialized part
32    /// or immediately adjacent to it.
33    ///
34    /// # Panics
35    ///
36    /// If the range is invalid with regard to the recipient's total size or
37    /// the length of its initialized part, the implementation of this method
38    /// should panic.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use tokio_uring::buf::BoundedBuf;
44    ///
45    /// let buf = b"hello world".to_vec();
46    /// let slice = buf.slice(5..10);
47    /// assert_eq!(&slice[..], b" worl");
48    /// let slice = slice.slice(1..3);
49    /// assert_eq!(&slice[..], b"wo");
50    /// ```
51    fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self::Buf>;
52
53    /// Returns a `Slice` with the view's full range.
54    ///
55    /// This method is to be used by the `tokio-uring` runtime and it is not
56    /// expected for users to call it directly.
57    fn slice_full(self) -> Slice<Self::Buf>;
58
59    /// Gets a reference to the underlying buffer.
60    fn get_buf(&self) -> &Self::Buf;
61
62    /// Returns the range bounds for this view.
63    fn bounds(&self) -> Self::Bounds;
64
65    /// Constructs a view from an underlying buffer and range bounds.
66    fn from_buf_bounds(buf: Self::Buf, bounds: Self::Bounds) -> Self;
67
68    /// Like [`IoBuf::stable_ptr`],
69    /// but possibly offset to the view's starting position.
70    fn stable_ptr(&self) -> *const u8;
71
72    /// Number of initialized bytes available via this view.
73    fn bytes_init(&self) -> usize;
74
75    /// Total size of the view, including uninitialized memory, if any.
76    fn bytes_total(&self) -> usize;
77}
78
79impl<T: IoBuf> BoundedBuf for T {
80    type Buf = Self;
81    type Bounds = ops::RangeFull;
82
83    fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self> {
84        use ops::Bound;
85
86        let begin = match range.start_bound() {
87            Bound::Included(&n) => n,
88            Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
89            Bound::Unbounded => 0,
90        };
91
92        assert!(begin < self.bytes_total());
93
94        let end = match range.end_bound() {
95            Bound::Included(&n) => n.checked_add(1).expect("out of range"),
96            Bound::Excluded(&n) => n,
97            Bound::Unbounded => self.bytes_total(),
98        };
99
100        assert!(end <= self.bytes_total());
101        assert!(begin <= self.bytes_init());
102
103        Slice::new(self, begin, end)
104    }
105
106    fn slice_full(self) -> Slice<Self> {
107        let end = self.bytes_total();
108        Slice::new(self, 0, end)
109    }
110
111    fn get_buf(&self) -> &Self {
112        self
113    }
114
115    fn bounds(&self) -> Self::Bounds {
116        ..
117    }
118
119    fn from_buf_bounds(buf: Self, _: ops::RangeFull) -> Self {
120        buf
121    }
122
123    fn stable_ptr(&self) -> *const u8 {
124        IoBuf::stable_ptr(self)
125    }
126
127    fn bytes_init(&self) -> usize {
128        IoBuf::bytes_init(self)
129    }
130
131    fn bytes_total(&self) -> usize {
132        IoBuf::bytes_total(self)
133    }
134}
135
136/// A possibly bounded view into an owned [`IoBufMut`] buffer.
137///
138/// This trait provides a generic way to use mutable buffers and `Slice` views
139/// into such buffers with `io-uring` operations.
140pub trait BoundedBufMut: BoundedBuf<Buf = Self::BufMut> {
141    /// The type of the underlying buffer.
142    type BufMut: IoBufMut;
143
144    /// Like [`IoBufMut::stable_mut_ptr`],
145    /// but possibly offset to the view's starting position.
146    fn stable_mut_ptr(&mut self) -> *mut u8;
147
148    /// Like [`IoBufMut::set_init`],
149    /// but the position is possibly offset to the view's starting position.
150    ///
151    /// # Safety
152    ///
153    /// The caller must ensure that all bytes starting at `stable_mut_ptr()` up
154    /// to `pos` are initialized and owned by the buffer.
155    unsafe fn set_init(&mut self, pos: usize);
156
157    /// Copies the given byte slice into the buffer, starting at
158    /// this view's offset.
159    ///
160    /// # Panics
161    ///
162    /// If the slice's length exceeds the destination's total capacity,
163    /// this method panics.
164    fn put_slice(&mut self, src: &[u8]) {
165        assert!(self.bytes_total() >= src.len());
166        let dst = self.stable_mut_ptr();
167
168        // Safety:
169        // dst pointer validity is ensured by stable_mut_ptr;
170        // the length is checked to not exceed the view's total capacity;
171        // src (immutable) and dst (mutable) cannot point to overlapping memory;
172        // after copying the amount of bytes given by the slice, it's safe
173        // to mark them as initialized in the buffer.
174        unsafe {
175            ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len());
176            self.set_init(src.len());
177        }
178    }
179}
180
181impl<T: IoBufMut> BoundedBufMut for T {
182    type BufMut = T;
183
184    fn stable_mut_ptr(&mut self) -> *mut u8 {
185        IoBufMut::stable_mut_ptr(self)
186    }
187
188    unsafe fn set_init(&mut self, pos: usize) {
189        IoBufMut::set_init(self, pos)
190    }
191}