Skip to content

Commit 1d3be86

Browse files
committed
BoundedBuf trait to take .slice API out of IoBuf
Introduce new traits, BoundedBuf and BoundedBufMut, taking the slice method and related infrastructure off IoBuf and IoBufMut. Slice no longer implements IoBuf/IoBufMut, instead it does BoundedBuf and BoundedBufMut. These traits are used as bounds in most generic I/O methods in place of IoBuf/IoBufMut. The purpose of this is to get rid of Slice<Slice<T>> arising from chained slice calls. As a side benefit, IoBuf/IoBufMut are now object-safe.
1 parent f71326d commit 1d3be86

File tree

17 files changed

+345
-222
lines changed

17 files changed

+345
-222
lines changed

examples/tcp_listener.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn main() {
2222
tokio_uring::spawn(async move {
2323
// implement ping-pong loop
2424

25-
use tokio_uring::buf::IoBuf; // for slice()
25+
use tokio_uring::buf::BoundedBuf; // for slice()
2626

2727
println!("{} connected", socket_addr);
2828
let mut n = 0;

src/buf/bounded.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use super::{IoBuf, IoBufMut, Slice};
2+
3+
use std::ops;
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
16+
pub trait BoundedBuf: Sized + 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::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 the range bounds for this view.
53+
fn bounds(&self) -> Self::Bounds;
54+
55+
/// Constructs a view from an underlying buffer and range bounds.
56+
fn from_buf_bounds(buf: Self::Buf, bounds: Self::Bounds) -> Self;
57+
58+
/// Like [`IoBuf::stable_ptr`],
59+
/// but possibly offset to the view's starting position.
60+
fn stable_ptr(&self) -> *const u8;
61+
62+
/// Number of initialized bytes available via this view.
63+
fn bytes_init(&self) -> usize;
64+
65+
/// Total size of the view, including uninitialized memory, if any.
66+
fn bytes_total(&self) -> usize;
67+
}
68+
69+
impl<T: IoBuf> BoundedBuf for T {
70+
type Buf = Self;
71+
type Bounds = ops::RangeFull;
72+
73+
fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self> {
74+
use ops::Bound;
75+
76+
let begin = match range.start_bound() {
77+
Bound::Included(&n) => n,
78+
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
79+
Bound::Unbounded => 0,
80+
};
81+
82+
assert!(begin < self.bytes_total());
83+
84+
let end = match range.end_bound() {
85+
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
86+
Bound::Excluded(&n) => n,
87+
Bound::Unbounded => self.bytes_total(),
88+
};
89+
90+
assert!(end <= self.bytes_total());
91+
assert!(begin <= self.bytes_init());
92+
93+
Slice::new(self, begin, end)
94+
}
95+
96+
fn bounds(&self) -> Self::Bounds {
97+
..
98+
}
99+
100+
fn from_buf_bounds(buf: Self, _: ops::RangeFull) -> Self {
101+
buf
102+
}
103+
104+
fn stable_ptr(&self) -> *const u8 {
105+
IoBuf::stable_ptr(self)
106+
}
107+
108+
fn bytes_init(&self) -> usize {
109+
IoBuf::bytes_init(self)
110+
}
111+
112+
fn bytes_total(&self) -> usize {
113+
IoBuf::bytes_total(self)
114+
}
115+
}
116+
117+
/// A possibly bounded view into an owned [`IoBufMut`] buffer.
118+
///
119+
/// This trait provides a generic way to use mutable buffers and `Slice` views
120+
/// into such buffers with `io-uring` operations.
121+
pub trait BoundedBufMut: BoundedBuf<Buf = Self::BufMut> {
122+
/// The type of the underlying buffer.
123+
type BufMut: IoBufMut;
124+
125+
/// Like [`IoBufMut::stable_mut_ptr`],
126+
/// but possibly offset to the view's starting position.
127+
fn stable_mut_ptr(&mut self) -> *mut u8;
128+
129+
/// Like [`IoBufMut::set_init`],
130+
/// but the position is possibly offset to the view's starting position.
131+
unsafe fn set_init(&mut self, pos: usize);
132+
}
133+
134+
impl<T: IoBufMut> BoundedBufMut for T {
135+
type BufMut = T;
136+
137+
fn stable_mut_ptr(&mut self) -> *mut u8 {
138+
IoBufMut::stable_mut_ptr(self)
139+
}
140+
141+
unsafe fn set_init(&mut self, pos: usize) {
142+
IoBufMut::set_init(self, pos)
143+
}
144+
}

src/buf/io_buf.rs

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
use crate::buf::Slice;
2-
3-
use std::ops;
4-
51
/// An `io-uring` compatible buffer.
62
///
7-
/// The `IoBuf` trait is implemented by buffer types that can be passed to
8-
/// io-uring operations. Users will not need to use this trait directly, except
9-
/// for the [`slice`] method.
10-
///
11-
/// # Slicing
12-
///
13-
/// Because buffers are passed by ownership to the runtime, Rust's slice API
14-
/// (`&buf[..]`) cannot be used. Instead, `tokio-uring` provides an owned slice
15-
/// API: [`slice()`]. The method takes ownership fo the buffer and returns a
16-
/// `Slice<Self>` type that tracks the requested offset.
3+
/// The `IoBuf` trait is implemented by buffer types that can be used with
4+
/// io-uring operations. Users will not need to use this trait directly.
5+
/// The [`BoundedBuf`] trait provides some useful methods including `slice`.
176
///
187
/// # Safety
198
///
209
/// Buffers passed to `io-uring` operations must reference a stable memory
2110
/// region. While the runtime holds ownership to a buffer, the pointer returned
2211
/// by `stable_ptr` must remain valid even if the `IoBuf` value is moved.
2312
///
24-
/// [`slice()`]: IoBuf::slice
13+
/// [`BoundedBuf`]: crate::buf::BoundedBuf
2514
pub unsafe trait IoBuf: Unpin + 'static {
2615
/// Returns a raw pointer to the vector’s buffer.
2716
///
@@ -48,45 +37,6 @@ pub unsafe trait IoBuf: Unpin + 'static {
4837
///
4938
/// For `Vec`, this is identical to `capacity()`.
5039
fn bytes_total(&self) -> usize;
51-
52-
/// Returns a view of the buffer with the specified range.
53-
///
54-
/// This method is similar to Rust's slicing (`&buf[..]`), but takes
55-
/// ownership of the buffer.
56-
///
57-
/// # Examples
58-
///
59-
/// ```
60-
/// use tokio_uring::buf::IoBuf;
61-
///
62-
/// let buf = b"hello world".to_vec();
63-
/// buf.slice(5..10);
64-
/// ```
65-
fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self>
66-
where
67-
Self: Sized,
68-
{
69-
use core::ops::Bound;
70-
71-
let begin = match range.start_bound() {
72-
Bound::Included(&n) => n,
73-
Bound::Excluded(&n) => n + 1,
74-
Bound::Unbounded => 0,
75-
};
76-
77-
assert!(begin < self.bytes_total());
78-
79-
let end = match range.end_bound() {
80-
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
81-
Bound::Excluded(&n) => n,
82-
Bound::Unbounded => self.bytes_total(),
83-
};
84-
85-
assert!(end <= self.bytes_total());
86-
assert!(begin <= self.bytes_init());
87-
88-
Slice::new(self, begin, end)
89-
}
9040
}
9141

9242
unsafe impl IoBuf for Vec<u8> {

src/buf/io_buf_mut.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::buf::IoBuf;
22

33
/// A mutable`io-uring` compatible buffer.
44
///
5-
/// The `IoBufMut` trait is implemented by buffer types that can be passed to
5+
/// The `IoBufMut` trait is implemented by buffer types that can be used with
66
/// io-uring operations. Users will not need to use this trait directly.
77
///
88
/// # Safety

src/buf/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ pub use io_buf_mut::IoBufMut;
1313
mod slice;
1414
pub use slice::Slice;
1515

16+
mod bounded;
17+
pub use bounded::{BoundedBuf, BoundedBufMut};
18+
1619
pub(crate) fn deref(buf: &impl IoBuf) -> &[u8] {
1720
// Safety: the `IoBuf` trait is marked as unsafe and is expected to be
1821
// implemented correctly.

src/buf/slice.rs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::buf::{IoBuf, IoBufMut};
1+
use super::{BoundedBuf, BoundedBufMut, IoBuf, IoBufMut};
22

33
use std::cmp;
44
use std::ops;
@@ -9,14 +9,14 @@ use std::ops;
99
/// This type is useful for performing io-uring read and write operations using
1010
/// a subset of a buffer.
1111
///
12-
/// Slices are created using [`IoBuf::slice`].
12+
/// Slices are created using [`BoundedBuf::slice`].
1313
///
1414
/// # Examples
1515
///
1616
/// Creating a slice
1717
///
1818
/// ```
19-
/// use tokio_uring::buf::IoBuf;
19+
/// use tokio_uring::buf::BoundedBuf;
2020
///
2121
/// let buf = b"hello world".to_vec();
2222
/// let slice = buf.slice(..5);
@@ -39,7 +39,7 @@ impl<T> Slice<T> {
3939
/// # Examples
4040
///
4141
/// ```
42-
/// use tokio_uring::buf::IoBuf;
42+
/// use tokio_uring::buf::BoundedBuf;
4343
///
4444
/// let buf = b"hello world".to_vec();
4545
/// let slice = buf.slice(1..5);
@@ -55,7 +55,7 @@ impl<T> Slice<T> {
5555
/// # Examples
5656
///
5757
/// ```
58-
/// use tokio_uring::buf::IoBuf;
58+
/// use tokio_uring::buf::BoundedBuf;
5959
///
6060
/// let buf = b"hello world".to_vec();
6161
/// let slice = buf.slice(1..5);
@@ -73,7 +73,7 @@ impl<T> Slice<T> {
7373
/// # Examples
7474
///
7575
/// ```
76-
/// use tokio_uring::buf::IoBuf;
76+
/// use tokio_uring::buf::BoundedBuf;
7777
///
7878
/// let buf = b"hello world".to_vec();
7979
/// let slice = buf.slice(..5);
@@ -92,7 +92,7 @@ impl<T> Slice<T> {
9292
/// # Examples
9393
///
9494
/// ```
95-
/// use tokio_uring::buf::IoBuf;
95+
/// use tokio_uring::buf::BoundedBuf;
9696
///
9797
/// let buf = b"hello world".to_vec();
9898
/// let mut slice = buf.slice(..5);
@@ -111,7 +111,7 @@ impl<T> Slice<T> {
111111
/// # Examples
112112
///
113113
/// ```
114-
/// use tokio_uring::buf::IoBuf;
114+
/// use tokio_uring::buf::BoundedBuf;
115115
///
116116
/// let buf = b"hello world".to_vec();
117117
/// let slice = buf.slice(..5);
@@ -142,7 +142,51 @@ impl<T: IoBufMut> ops::DerefMut for Slice<T> {
142142
}
143143
}
144144

145-
unsafe impl<T: IoBuf> IoBuf for Slice<T> {
145+
impl<T: IoBuf> BoundedBuf for Slice<T> {
146+
type Buf = T;
147+
type Bounds = ops::Range<usize>;
148+
149+
fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<T> {
150+
use ops::Bound;
151+
152+
let begin = match range.start_bound() {
153+
Bound::Included(&n) => self.begin.checked_add(n).expect("out of range"),
154+
Bound::Excluded(&n) => self
155+
.begin
156+
.checked_add(n)
157+
.and_then(|x| x.checked_add(1))
158+
.expect("out of range"),
159+
Bound::Unbounded => self.begin,
160+
};
161+
162+
assert!(begin <= self.end);
163+
164+
let end = match range.end_bound() {
165+
Bound::Included(&n) => self
166+
.begin
167+
.checked_add(n)
168+
.and_then(|x| x.checked_add(1))
169+
.expect("out of range"),
170+
Bound::Excluded(&n) => self.begin.checked_add(n).expect("out of range"),
171+
Bound::Unbounded => self.end,
172+
};
173+
174+
assert!(end <= self.end);
175+
assert!(begin <= self.buf.bytes_init());
176+
177+
Slice::new(self.buf, begin, end)
178+
}
179+
180+
fn bounds(&self) -> Self::Bounds {
181+
self.begin..self.end
182+
}
183+
184+
fn from_buf_bounds(buf: T, bounds: Self::Bounds) -> Self {
185+
assert!(bounds.start <= buf.bytes_init());
186+
assert!(bounds.end <= buf.bytes_total());
187+
Slice::new(buf, bounds.start, bounds.end)
188+
}
189+
146190
fn stable_ptr(&self) -> *const u8 {
147191
super::deref(&self.buf)[self.begin..].as_ptr()
148192
}
@@ -156,7 +200,9 @@ unsafe impl<T: IoBuf> IoBuf for Slice<T> {
156200
}
157201
}
158202

159-
unsafe impl<T: IoBufMut> IoBufMut for Slice<T> {
203+
impl<T: IoBufMut> BoundedBufMut for Slice<T> {
204+
type BufMut = T;
205+
160206
fn stable_mut_ptr(&mut self) -> *mut u8 {
161207
super::deref_mut(&mut self.buf)[self.begin..].as_mut_ptr()
162208
}

0 commit comments

Comments
 (0)