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}