Skip to content
2 changes: 1 addition & 1 deletion examples/tcp_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() {
tokio_uring::spawn(async move {
// implement ping-pong loop

use tokio_uring::buf::IoBuf; // for slice()
use tokio_uring::buf::BoundedBuf; // for slice()

println!("{} connected", socket_addr);
let mut n = 0;
Expand Down
156 changes: 156 additions & 0 deletions src/buf/bounded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use super::{IoBuf, IoBufMut, Slice};

use std::ops;

/// A possibly bounded view into an owned [`IoBuf`] buffer.
///
/// Because buffers are passed by ownership to the runtime, Rust's slice API
/// (`&buf[..]`) cannot be used. Instead, `tokio-uring` provides an owned slice
/// API: [`.slice()`]. The method takes ownership of the buffer and returns a
/// [`Slice`] value that tracks the requested range.
///
/// This trait provides a generic way to use buffers and `Slice` views
/// into such buffers with `io-uring` operations.
///
/// [`.slice()`]: BoundedBuf::slice
pub trait BoundedBuf: Sized + Unpin + 'static {
/// The type of the underlying buffer.
type Buf: IoBuf;

/// The type representing the range bounds of the view.
type Bounds: ops::RangeBounds<usize>;

/// Returns a view of the buffer with the specified range.
///
/// This method is similar to Rust's slicing (`&buf[..]`), but takes
/// ownership of the buffer. The range bounds are specified against
/// the possibly offset beginning of the `self` view into the buffer
/// and the end bound, if specified, must not exceed the view's total size.
/// Note that the range may extend into the uninitialized part of the
/// buffer, but it must start (if so bounded) in the initialized part
/// or immediately adjacent to it.
///
/// # Panics
///
/// If the range is invalid with regard to the recipient's total size or
/// the length of its initialized part, the implementation of this method
/// should panic.
///
/// # Examples
///
/// ```
/// use tokio_uring::buf::BoundedBuf;
///
/// let buf = b"hello world".to_vec();
/// let slice = buf.slice(5..10);
/// assert_eq!(&slice[..], b" worl");
/// let slice = slice.slice(1..3);
/// assert_eq!(&slice[..], b"wo");
/// ```
fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self::Buf>;

/// Gets a reference to the underlying buffer.
fn get_buf(&self) -> &Self::Buf;

/// Returns the range bounds for this view.
fn bounds(&self) -> Self::Bounds;

/// Constructs a view from an underlying buffer and range bounds.
fn from_buf_bounds(buf: Self::Buf, bounds: Self::Bounds) -> Self;

/// Like [`IoBuf::stable_ptr`],
/// but possibly offset to the view's starting position.
fn stable_ptr(&self) -> *const u8;

/// Number of initialized bytes available via this view.
fn bytes_init(&self) -> usize;

/// Total size of the view, including uninitialized memory, if any.
fn bytes_total(&self) -> usize;
}

impl<T: IoBuf> BoundedBuf for T {
type Buf = Self;
type Bounds = ops::RangeFull;

fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self> {
use ops::Bound;

let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
Bound::Unbounded => 0,
};

assert!(begin < self.bytes_total());

let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => self.bytes_total(),
};

assert!(end <= self.bytes_total());
assert!(begin <= self.bytes_init());

Slice::new(self, begin, end)
}

fn get_buf(&self) -> &Self {
self
}

fn bounds(&self) -> Self::Bounds {
..
}

fn from_buf_bounds(buf: Self, _: ops::RangeFull) -> Self {
buf
}

fn stable_ptr(&self) -> *const u8 {
IoBuf::stable_ptr(self)
}

fn bytes_init(&self) -> usize {
IoBuf::bytes_init(self)
}

fn bytes_total(&self) -> usize {
IoBuf::bytes_total(self)
}
}

/// A possibly bounded view into an owned [`IoBufMut`] buffer.
///
/// This trait provides a generic way to use mutable buffers and `Slice` views
/// into such buffers with `io-uring` operations.
pub trait BoundedBufMut: BoundedBuf<Buf = Self::BufMut> {
/// The type of the underlying buffer.
type BufMut: IoBufMut;

/// Like [`IoBufMut::stable_mut_ptr`],
/// but possibly offset to the view's starting position.
fn stable_mut_ptr(&mut self) -> *mut u8;

/// Like [`IoBufMut::set_init`],
/// but the position is possibly offset to the view's starting position.
///
/// # Safety
///
/// The caller must ensure that all bytes starting at `stable_mut_ptr()` up
/// to `pos` are initialized and owned by the buffer.
unsafe fn set_init(&mut self, pos: usize);
}

impl<T: IoBufMut> BoundedBufMut for T {
type BufMut = T;

fn stable_mut_ptr(&mut self) -> *mut u8 {
IoBufMut::stable_mut_ptr(self)
}

unsafe fn set_init(&mut self, pos: usize) {
IoBufMut::set_init(self, pos)
}
}
58 changes: 4 additions & 54 deletions src/buf/io_buf.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
use crate::buf::Slice;

use std::ops;

/// An `io-uring` compatible buffer.
///
/// The `IoBuf` trait is implemented by buffer types that can be passed to
/// io-uring operations. Users will not need to use this trait directly, except
/// for the [`slice`] method.
///
/// # Slicing
///
/// Because buffers are passed by ownership to the runtime, Rust's slice API
/// (`&buf[..]`) cannot be used. Instead, `tokio-uring` provides an owned slice
/// API: [`slice()`]. The method takes ownership fo the buffer and returns a
/// `Slice<Self>` type that tracks the requested offset.
/// The `IoBuf` trait is implemented by buffer types that can be used with
/// io-uring operations. Users will not need to use this trait directly.
/// The [`BoundedBuf`] trait provides some useful methods including `slice`.
///
/// # Safety
///
/// Buffers passed to `io-uring` operations must reference a stable memory
/// region. While the runtime holds ownership to a buffer, the pointer returned
/// by `stable_ptr` must remain valid even if the `IoBuf` value is moved.
///
/// [`slice()`]: IoBuf::slice
/// [`BoundedBuf`]: crate::buf::BoundedBuf
pub unsafe trait IoBuf: Unpin + 'static {
/// Returns a raw pointer to the vector’s buffer.
///
Expand All @@ -48,45 +37,6 @@ pub unsafe trait IoBuf: Unpin + 'static {
///
/// For `Vec`, this is identical to `capacity()`.
fn bytes_total(&self) -> usize;

/// Returns a view of the buffer with the specified range.
///
/// This method is similar to Rust's slicing (`&buf[..]`), but takes
/// ownership of the buffer.
///
/// # Examples
///
/// ```
/// use tokio_uring::buf::IoBuf;
///
/// let buf = b"hello world".to_vec();
/// buf.slice(5..10);
/// ```
fn slice(self, range: impl ops::RangeBounds<usize>) -> Slice<Self>
where
Self: Sized,
{
use core::ops::Bound;

let begin = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n + 1,
Bound::Unbounded => 0,
};

assert!(begin < self.bytes_total());

let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
Bound::Excluded(&n) => n,
Bound::Unbounded => self.bytes_total(),
};

assert!(end <= self.bytes_total());
assert!(begin <= self.bytes_init());

Slice::new(self, begin, end)
}
}

unsafe impl IoBuf for Vec<u8> {
Expand Down
5 changes: 3 additions & 2 deletions src/buf/io_buf_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::buf::IoBuf;

/// A mutable`io-uring` compatible buffer.
///
/// The `IoBufMut` trait is implemented by buffer types that can be passed to
/// The `IoBufMut` trait is implemented by buffer types that can be used with
/// io-uring operations. Users will not need to use this trait directly.
///
/// # Safety
Expand All @@ -23,7 +23,8 @@ pub unsafe trait IoBufMut: IoBuf {

/// Updates the number of initialized bytes.
///
/// The specified `pos` becomes the new value returned by
/// If the specified `pos` is greater than the value returned by
/// [`IoBuf::bytes_init`], it becomes the new water mark as returned by
/// `IoBuf::bytes_init`.
///
/// # Safety
Expand Down
3 changes: 3 additions & 0 deletions src/buf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub use io_buf_mut::IoBufMut;
mod slice;
pub use slice::Slice;

mod bounded;
pub use bounded::{BoundedBuf, BoundedBufMut};

pub(crate) fn deref(buf: &impl IoBuf) -> &[u8] {
// Safety: the `IoBuf` trait is marked as unsafe and is expected to be
// implemented correctly.
Expand Down
Loading