blob: bbd46f819f6736cef7745ea0b8a591b7f055e41f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Type-safe bindings for Zircon stream objects.
use {
crate::{
object_get_property, object_set_property, ok, AsHandleRef, Handle, HandleBased, HandleRef,
Property, PropertyQuery, Status, Vmo,
},
bitflags::bitflags,
fuchsia_zircon_sys as sys,
std::{io::SeekFrom, mem::MaybeUninit},
};
/// An object representing a Zircon [stream](https://fuchsia.dev/fuchsia-src/concepts/objects/stream.md).
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Stream(Handle);
impl_handle_based!(Stream);
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamOptions: u32 {
const MODE_READ = sys::ZX_STREAM_MODE_READ;
const MODE_WRITE = sys::ZX_STREAM_MODE_WRITE;
const MODE_APPEND = sys::ZX_STREAM_MODE_APPEND;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamReadOptions: u32 {
}
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamWriteOptions: u32 {
const APPEND = sys::ZX_STREAM_APPEND;
}
}
impl Stream {
/// See [zx_stream_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_create)
pub fn create(options: StreamOptions, vmo: &Vmo, offset: u64) -> Result<Self, Status> {
let mut handle = 0;
let status =
unsafe { sys::zx_stream_create(options.bits(), vmo.raw_handle(), offset, &mut handle) };
ok(status)?;
unsafe { Ok(Stream::from(Handle::from_raw(handle))) }
}
/// Wraps the
/// [`zx_stream_readv`](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv)
/// syscall.
///
/// # Safety
///
/// The caller is responsible for ensuring that the buffers in `iovecs` point to valid (albeit
/// not necessarily initialized) memory.
pub unsafe fn readv(
&self,
options: StreamReadOptions,
iovecs: &mut [sys::zx_iovec_t],
) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_stream_readv(
self.raw_handle(),
options.bits(),
iovecs.as_mut_ptr(),
iovecs.len(),
&mut actual,
)
};
ok(status)?;
Ok(actual)
}
/// Attempts to read `buffer.len()` bytes from the stream starting at the stream's current seek
/// offset. Only the number of bytes read from the stream will be initialized in `buffer`.
/// Returns the number of bytes read from the stream.
///
/// See [zx_stream_readv](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv).
pub fn read_uninit(
&self,
options: StreamReadOptions,
buffer: &mut [MaybeUninit<u8>],
) -> Result<usize, Status> {
// TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_as_mut_ptr when stable
let mut iovec =
[sys::zx_iovec_t { buffer: buffer.as_mut_ptr() as *mut u8, capacity: buffer.len() }];
// SAFETY: The buffer in `iovec` comes from a mutable slice so we know it's safe to pass it
// to `readv`.
unsafe { self.readv(options, &mut iovec) }
}
/// Attempts to read `length` bytes from the stream starting at the stream's current seek
/// offset. Returns the read bytes as a `Vec`.
///
/// See [zx_stream_readv](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv).
pub fn read_to_vec(
&self,
options: StreamReadOptions,
length: usize,
) -> Result<Vec<u8>, Status> {
let mut data = Vec::with_capacity(length);
let buffer = &mut data.spare_capacity_mut()[0..length];
let actual = self.read_uninit(options, buffer)?;
// SAFETY: read_uninit returns the number of bytes that were initialized.
unsafe { data.set_len(actual) };
Ok(data)
}
/// Wraps the
/// [`zx_stream_readv_at`](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv_at)
/// syscall.
///
/// # Safety
///
/// The caller is responsible for ensuring that the buffers in `iovecs` point to valid (albeit
/// not necessarily initialized) memory.
pub unsafe fn readv_at(
&self,
options: StreamReadOptions,
offset: u64,
iovecs: &mut [sys::zx_iovec_t],
) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_stream_readv_at(
self.raw_handle(),
options.bits(),
offset,
iovecs.as_mut_ptr(),
iovecs.len(),
&mut actual,
)
};
ok(status)?;
Ok(actual)
}
/// Attempts to read `buffer.len()` bytes from the stream starting at `offset`. Only the number
/// of bytes read from the stream will be initialized in `buffer`. Returns the number of bytes
/// read from the stream.
///
/// See
/// [zx_stream_readv_at](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv_at).
pub fn read_at_uninit(
&self,
options: StreamReadOptions,
offset: u64,
buffer: &mut [MaybeUninit<u8>],
) -> Result<usize, Status> {
// TODO(https://fxbug.dev/42079723) Use MaybeUninit::slice_as_mut_ptr when stable.
let mut iovec =
[sys::zx_iovec_t { buffer: buffer.as_mut_ptr() as *mut u8, capacity: buffer.len() }];
// SAFETY: The buffer in `iovec` comes from a mutable slice so we know it's safe to pass it
// to `readv_at`.
unsafe { self.readv_at(options, offset, &mut iovec) }
}
/// Attempts to read `length` bytes from the stream starting at `offset`. Returns the read bytes
/// as a `Vec`.
///
/// See
/// [zx_stream_readv_at](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_readv_at).
pub fn read_at_to_vec(
&self,
options: StreamReadOptions,
offset: u64,
length: usize,
) -> Result<Vec<u8>, Status> {
let mut data = Vec::with_capacity(length);
let buffer = &mut data.spare_capacity_mut()[0..length];
let actual = self.read_at_uninit(options, offset, buffer)?;
// SAFETY: read_at_uninit returns the number of bytes that were initialized.
unsafe { data.set_len(actual) };
Ok(data)
}
/// See [zx_stream_seek](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_seek)
pub fn seek(&self, pos: SeekFrom) -> Result<u64, Status> {
let (whence, offset) = match pos {
SeekFrom::Start(start) => (
sys::ZX_STREAM_SEEK_ORIGIN_START,
start.try_into().map_err(|_| Status::OUT_OF_RANGE)?,
),
SeekFrom::End(end) => (sys::ZX_STREAM_SEEK_ORIGIN_END, end),
SeekFrom::Current(current) => (sys::ZX_STREAM_SEEK_ORIGIN_CURRENT, current),
};
let mut pos = 0;
let status = unsafe { sys::zx_stream_seek(self.raw_handle(), whence, offset, &mut pos) };
ok(status)?;
Ok(pos)
}
/// Wraps the
/// [`zx_stream_writev`](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_writev)
/// syscall.
pub fn writev(
&self,
options: StreamWriteOptions,
iovecs: &[sys::zx_iovec_t],
) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_stream_writev(
self.raw_handle(),
options.bits(),
iovecs.as_ptr(),
iovecs.len(),
&mut actual,
)
};
ok(status)?;
Ok(actual)
}
/// Writes `buffer` to the stream at the stream's current seek offset. Returns the number of
/// bytes written.
///
/// See [zx_stream_writev](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_writev).
pub fn write(&self, options: StreamWriteOptions, buffer: &[u8]) -> Result<usize, Status> {
let iovec = [sys::zx_iovec_t { buffer: buffer.as_ptr(), capacity: buffer.len() }];
self.writev(options, &iovec)
}
/// Wraps the
/// [`zx_stream_writev_at`](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_writev_at)
/// syscall.
pub fn writev_at(
&self,
options: StreamWriteOptions,
offset: u64,
iovecs: &[sys::zx_iovec_t],
) -> Result<usize, Status> {
let mut actual = 0;
let status = unsafe {
sys::zx_stream_writev_at(
self.raw_handle(),
options.bits(),
offset,
iovecs.as_ptr(),
iovecs.len(),
&mut actual,
)
};
ok(status)?;
Ok(actual)
}
/// Writes `buffer` to the stream at `offset``. Returns the number of bytes written.
///
/// See
/// [zx_stream_writev_at](https://fuchsia.dev/fuchsia-src/reference/syscalls/stream_writev_at).
pub fn write_at(
&self,
options: StreamWriteOptions,
offset: u64,
buffer: &[u8],
) -> Result<usize, Status> {
let iovec = [sys::zx_iovec_t { buffer: buffer.as_ptr(), capacity: buffer.len() }];
self.writev_at(options, offset, &iovec)
}
}
unsafe_handle_properties!(object: Stream,
props: [
{query_ty: STREAM_MODE_APPEND, tag: StreamModeAppendTag, prop_ty: u8, get: get_mode_append, set: set_mode_append},
]
);
impl std::io::Read for Stream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let mut iovec = [sys::zx_iovec_t { buffer: buf.as_mut_ptr(), capacity: buf.len() }];
// SAFETY: The buffer in `iovec` comes from a mutable slice so we know it's safe to pass it
// to `readv`.
Ok(unsafe { self.readv(StreamReadOptions::empty(), &mut iovec) }?)
}
fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result<usize> {
// SAFETY: `zx_iovec_t` and `IoSliceMut` have the same layout.
let mut iovecs = unsafe {
std::slice::from_raw_parts_mut(bufs.as_mut_ptr() as *mut sys::zx_iovec_t, bufs.len())
};
// SAFETY: `IoSliceMut` can only be constructed from a mutable slice so we know it's safe to
// pass to `readv`.
Ok(unsafe { self.readv(StreamReadOptions::empty(), &mut iovecs) }?)
}
}
impl std::io::Seek for Stream {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
Ok(Self::seek(&self, pos)? as u64)
}
}
impl std::io::Write for Stream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(Self::write(&self, StreamWriteOptions::empty(), buf)?)
}
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
// SAFETY: `zx_iovec_t` and `IoSliceMut` have the same layout.
let iovecs = unsafe {
std::slice::from_raw_parts(bufs.as_ptr() as *const sys::zx_iovec_t, bufs.len())
};
Ok(self.writev(StreamWriteOptions::empty(), &iovecs)?)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate as zx;
#[test]
fn create() {
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
let basic_info = stream.basic_info().unwrap();
assert_eq!(basic_info.object_type, zx::ObjectType::STREAM);
assert!(basic_info.rights.contains(zx::Rights::READ));
assert!(basic_info.rights.contains(zx::Rights::WRITE));
}
#[test]
fn create_readonly() {
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 0).unwrap();
let basic_info = stream.basic_info().unwrap();
assert!(basic_info.rights.contains(zx::Rights::READ));
assert!(!basic_info.rights.contains(zx::Rights::WRITE));
}
#[test]
fn create_offset() {
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 24).unwrap();
assert_eq!(stream.seek(SeekFrom::Current(0)).unwrap(), 24);
}
#[test]
fn create_invalid() {
let result =
Stream::create(StreamOptions::MODE_READ, &zx::Vmo::from(zx::Handle::invalid()), 0);
assert_eq!(result, Err(zx::Status::BAD_HANDLE));
}
#[test]
fn create_with_mode_append() {
let size: u64 = zx::system_get_page_size().into();
let vmo = zx::Vmo::create(size).unwrap();
let stream =
Stream::create(StreamOptions::MODE_WRITE | StreamOptions::MODE_APPEND, &vmo, 0)
.unwrap();
assert_eq!(stream.get_mode_append().unwrap(), 1);
}
#[test]
fn get_and_set_mode_append() {
let size: u64 = zx::system_get_page_size().into();
let vmo = zx::Vmo::create(size).unwrap();
let stream = Stream::create(StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
assert_eq!(stream.get_mode_append().unwrap(), 0);
stream.set_mode_append(&1).unwrap();
assert_eq!(stream.get_mode_append().unwrap(), 1);
stream.set_mode_append(&0).unwrap();
assert_eq!(stream.get_mode_append().unwrap(), 0);
}
#[test]
fn read_uninit() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create(DATA.len() as u64).unwrap();
vmo.write(DATA, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 0).unwrap();
// Read from the stream.
let mut data = Vec::with_capacity(5);
let bytes_read =
stream.read_uninit(StreamReadOptions::empty(), data.spare_capacity_mut()).unwrap();
assert_eq!(bytes_read, 5);
unsafe { data.set_len(5) };
assert_eq!(data, DATA[0..5]);
// Try to read more data than is available in the stream.
let mut data = Vec::with_capacity(10);
let bytes_read =
stream.read_uninit(StreamReadOptions::empty(), data.spare_capacity_mut()).unwrap();
assert_eq!(bytes_read, 7);
unsafe { data.set_len(7) };
assert_eq!(data, DATA[5..]);
// Try to read at the end of the stream.
let mut data = Vec::with_capacity(10);
let bytes_read =
stream.read_uninit(StreamReadOptions::empty(), data.spare_capacity_mut()).unwrap();
assert_eq!(bytes_read, 0);
}
#[test]
fn read_to_vec() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create(DATA.len() as u64).unwrap();
vmo.write(DATA, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 0).unwrap();
let data = stream.read_to_vec(StreamReadOptions::empty(), DATA.len()).unwrap();
assert_eq!(data, DATA);
}
#[test]
fn read_at_uninit() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create(DATA.len() as u64).unwrap();
vmo.write(DATA, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 0).unwrap();
// Read from the stream.
let mut data = Vec::with_capacity(5);
let bytes_read = stream
.read_at_uninit(StreamReadOptions::empty(), 0, data.spare_capacity_mut())
.unwrap();
assert_eq!(bytes_read, 5);
unsafe { data.set_len(5) };
assert_eq!(data, DATA[0..5]);
// Try to read beyond the end of the stream.
let mut data = Vec::with_capacity(10);
let bytes_read = stream
.read_at_uninit(StreamReadOptions::empty(), 5, data.spare_capacity_mut())
.unwrap();
assert_eq!(bytes_read, 7);
unsafe { data.set_len(7) };
assert_eq!(data, DATA[5..]);
// Try to read starting beyond the end of the stream.
let mut data = Vec::with_capacity(10);
let bytes_read = stream
.read_at_uninit(StreamReadOptions::empty(), 20, data.spare_capacity_mut())
.unwrap();
assert_eq!(bytes_read, 0);
}
#[test]
fn read_at_to_vec() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create(DATA.len() as u64).unwrap();
vmo.write(DATA, 0).unwrap();
let stream = Stream::create(StreamOptions::MODE_READ, &vmo, 0).unwrap();
let data = stream.read_at_to_vec(StreamReadOptions::empty(), 5, DATA.len()).unwrap();
assert_eq!(data, DATA[5..]);
}
#[test]
fn write() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
let bytes_written = stream.write(zx::StreamWriteOptions::empty(), DATA).unwrap();
assert_eq!(bytes_written, DATA.len());
let data = stream.read_at_to_vec(StreamReadOptions::empty(), 0, DATA.len()).unwrap();
assert_eq!(data, DATA);
}
#[test]
fn write_at() {
const DATA: &'static [u8] = b"vmo-contents";
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
let bytes_written =
stream.write_at(zx::StreamWriteOptions::empty(), 0, &DATA[0..3]).unwrap();
assert_eq!(bytes_written, 3);
let bytes_written =
stream.write_at(zx::StreamWriteOptions::empty(), 3, &DATA[3..]).unwrap();
assert_eq!(bytes_written, DATA.len() - 3);
let data = stream.read_at_to_vec(StreamReadOptions::empty(), 0, DATA.len()).unwrap();
assert_eq!(data, DATA);
}
#[test]
fn std_io_read_write_seek() {
const DATA: &'static str = "stream-contents";
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let mut stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
std::io::Write::write_all(&mut stream, DATA.as_bytes()).unwrap();
assert_eq!(std::io::Seek::stream_position(&mut stream).unwrap(), DATA.len() as u64);
std::io::Seek::rewind(&mut stream).unwrap();
assert_eq!(std::io::read_to_string(&mut stream).unwrap(), DATA);
assert_eq!(std::io::Seek::stream_position(&mut stream).unwrap(), DATA.len() as u64);
}
#[test]
fn std_io_read_vectored() {
const DATA: &'static [u8] = b"stream-contents";
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let mut stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
assert_eq!(stream.write(StreamWriteOptions::empty(), DATA).unwrap(), DATA.len());
std::io::Seek::rewind(&mut stream).unwrap();
let mut buf1 = [0; 6];
let mut buf2 = [0; 1];
let mut buf3 = [0; 8];
let mut bufs = [
std::io::IoSliceMut::new(&mut buf1),
std::io::IoSliceMut::new(&mut buf2),
std::io::IoSliceMut::new(&mut buf3),
];
assert_eq!(std::io::Read::read_vectored(&mut stream, &mut bufs).unwrap(), DATA.len());
assert_eq!(buf1, DATA[0..6]);
assert_eq!(buf2, DATA[6..7]);
assert_eq!(buf3, DATA[7..]);
}
#[test]
fn std_io_write_vectored() {
let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0).unwrap();
let mut stream =
Stream::create(StreamOptions::MODE_READ | StreamOptions::MODE_WRITE, &vmo, 0).unwrap();
let bufs = [
std::io::IoSlice::new(b"stream"),
std::io::IoSlice::new(b"-"),
std::io::IoSlice::new(b"contents"),
];
assert_eq!(std::io::Write::write_vectored(&mut stream, &bufs).unwrap(), 15);
std::io::Seek::rewind(&mut stream).unwrap();
assert_eq!(stream.read_to_vec(StreamReadOptions::empty(), 15).unwrap(), b"stream-contents");
}
}