blob: b1a87783b17b7cdb0ff198e1cedf32424550b877 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Cross platform [`IoBuf`] and [`IoBufMut`] types wrapping `iovec`/`WSABUF`.
use std::fmt;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::slice;
pub use crate::IoBuf;
pub(crate) trait PlatformIoBuf {
fn new(ptr: *mut u8, len: usize) -> Self;
fn len(&self) -> usize;
fn ptr(&self) -> *mut u8;
fn set_len(&mut self, len: usize);
fn set_ptr(&mut self, ptr: *mut u8);
}
/// Cross-platform mutable buffer.
///
/// This type is essentialy `std::io::IoSliceMut`, and guaranteed to be ABI-compatible with
/// `libc::iovec` on Linux/`WSABUF` on Windows; however, it does NOT automatically deref to `&mut
/// [u8]`, which is critical because it can point to guest memory. (Guest memory is implicitly
/// mutably borrowed by the guest, so another mutable borrow would violate Rust assumptions about
/// references.)
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct IoBufMut<'a> {
iobuf: IoBuf,
phantom: PhantomData<&'a mut [u8]>,
}
impl<'a> IoBufMut<'a> {
pub fn new(buf: &mut [u8]) -> IoBufMut<'a> {
// SAFETY:
// Safe because buf's memory is of the supplied length, and
// guaranteed to exist for the lifetime of the returned value.
unsafe { Self::from_raw_parts(buf.as_mut_ptr(), buf.len()) }
}
/// Creates a `IoBufMut` from a pointer and a length.
///
/// # Safety
///
/// In order to use this method safely, `addr` must be valid for reads and writes of `len` bytes
/// and should live for the entire duration of lifetime `'a`.
pub unsafe fn from_raw_parts(addr: *mut u8, len: usize) -> IoBufMut<'a> {
IoBufMut {
iobuf: IoBuf::new(addr, len),
phantom: PhantomData,
}
}
/// Creates a `IoBufMut` from an IoBuf.
///
/// # Safety
///
/// In order to use this method safely, `iobuf` must be valid for reads and writes through its
/// length and should live for the entire duration of lifetime `'a`.
pub unsafe fn from_iobuf(iobuf: IoBuf) -> IoBufMut<'a> {
IoBufMut {
iobuf,
phantom: PhantomData,
}
}
/// Advance the internal position of the buffer.
///
/// Panics if `count > self.len()`.
pub fn advance(&mut self, count: usize) {
assert!(count <= self.len());
self.iobuf.set_len(self.len() - count);
// SAFETY:
// Safe because we've checked that `count <= self.len()` so both the starting and resulting
// pointer are within the bounds of the allocation.
self.iobuf.set_ptr(unsafe { self.as_mut_ptr().add(count) });
}
/// Shorten the length of the buffer.
///
/// Has no effect if `len > self.len()`.
pub fn truncate(&mut self, len: usize) {
if len < self.len() {
self.iobuf.set_len(len);
}
}
#[inline]
pub fn len(&self) -> usize {
self.iobuf.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Gets a const pointer to this slice's memory.
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.iobuf.ptr() as *const u8
}
/// Gets a mutable pointer to this slice's memory.
#[inline]
pub fn as_mut_ptr(&self) -> *mut u8 {
self.iobuf.ptr()
}
/// Converts a slice of `IoBufMut`s into a slice of `IoBuf`s.
#[allow(clippy::wrong_self_convention)]
#[inline]
pub fn as_iobufs<'slice>(iovs: &'slice [IoBufMut<'_>]) -> &'slice [IoBuf] {
// SAFETY:
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
unsafe { slice::from_raw_parts(iovs.as_ptr() as *const IoBuf, iovs.len()) }
}
/// Converts a mutable slice of `IoBufMut`s into a mutable slice of `IoBuf`s.
#[inline]
pub fn as_iobufs_mut<'slice>(iovs: &'slice mut [IoBufMut<'_>]) -> &'slice mut [IoBuf] {
// SAFETY:
// Safe because `IoBufMut` is ABI-compatible with `IoBuf`.
unsafe { slice::from_raw_parts_mut(iovs.as_mut_ptr() as *mut IoBuf, iovs.len()) }
}
}
impl<'a> AsRef<IoBuf> for IoBufMut<'a> {
fn as_ref(&self) -> &IoBuf {
&self.iobuf
}
}
impl<'a> AsMut<IoBuf> for IoBufMut<'a> {
fn as_mut(&mut self) -> &mut IoBuf {
&mut self.iobuf
}
}
// SAFETY:
// It's safe to implement Send + Sync for this type for the same reason that `std::io::IoSliceMut`
// is Send + Sync. Internally, it contains a pointer and a length. The integer length is safely Send
// + Sync. There's nothing wrong with sending a pointer between threads and de-referencing the
// pointer requires an unsafe block anyway. See also https://github.com/rust-lang/rust/pull/70342.
unsafe impl<'a> Send for IoBufMut<'a> {}
// SAFETY: See comments for impl Send
unsafe impl<'a> Sync for IoBufMut<'a> {}
impl<'a> Debug for IoBufMut<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("IoBufMut")
.field("ptr", &self.iobuf.ptr())
.field("len", &self.iobuf.len())
.finish()
}
}