blob: a49e0302d482e30bcdd8e7e8cee045f2f0346417 [file] [log] [blame]
// Copyright 2017 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 fifo objects.
use crate::ok;
use crate::{AsHandleRef, Handle, HandleBased, HandleRef, Status};
use fuchsia_zircon_sys as sys;
/// An object representing a Zircon fifo.
///
/// As essentially a subtype of `Handle`, it can be freely interconverted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Fifo(Handle);
impl_handle_based!(Fifo);
impl Fifo {
/// Create a pair of fifos and return their endpoints. Writing to one endpoint enqueues an
/// element into the fifo from which the opposing endpoint reads. Wraps the
/// [zx_fifo_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_create.md)
/// syscall.
pub fn create(elem_count: usize, elem_size: usize) -> Result<(Fifo, Fifo), Status> {
let mut out0 = 0;
let mut out1 = 0;
let options = 0;
let status =
unsafe { sys::zx_fifo_create(elem_count, elem_size, options, &mut out0, &mut out1) };
ok(status)?;
unsafe { Ok((Self::from(Handle::from_raw(out0)), Self::from(Handle::from_raw(out1)))) }
}
/// Attempts to write some number of elements into the fifo. The length of `bytes` must be
/// divisible by `elem_size`, which must match the fifo's element size.
/// On success, returns the number of elements actually written.
///
/// Wraps
/// [zx_fifo_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_write.md).
pub fn write(&self, elem_size: usize, bytes: &[u8]) -> Result<usize, Status> {
let count = bytes.len() / elem_size;
debug_assert!(
count * elem_size == bytes.len(),
"bytes.len() must be divisible by elem_size"
);
unsafe { self.write_ptr(elem_size, bytes.as_ptr(), count) }
}
/// Attempts to write some number of elements into the fifo. `bytes` must
/// be at least `elem_size * count` bytes long. On success, returns the
/// number of elements actually written.
///
/// Wraps
/// [zx_fifo_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_write.md).
///
/// # Safety
///
/// The caller is responsible for ensuring `bytes` points to valid and
/// initialized memory at least `elem_size * count` bytes long.
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn write_ptr(
&self,
elem_size: usize,
bytes: *const u8,
count: usize,
) -> Result<usize, Status> {
let mut actual_count = 0;
let status =
sys::zx_fifo_write(self.raw_handle(), elem_size, bytes, count, &mut actual_count);
ok(status).map(|()| actual_count)
}
/// Attempts to read some number of elements out of the fifo. The length of `bytes` must be
/// divisible by `elem_size`, which must match the fifo's element size.
/// On success, returns the number of elements actually read.
///
/// Wraps
/// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
pub fn read(&self, elem_size: usize, bytes: &mut [u8]) -> Result<usize, Status> {
let count = bytes.len() / elem_size;
debug_assert!(
count * elem_size == bytes.len(),
"bytes.len() must be divisible by elem_size"
);
unsafe { self.read_ptr(elem_size, bytes.as_mut_ptr(), count) }
}
/// Attempts to read some number of elements out of the fifo. `bytes` must
/// be at least `elem_size * count` bytes long. On success, returns the
/// number of elements actually read.
///
/// Wraps
/// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
///
/// # Safety
///
/// The caller is responsible for ensuring `bytes` points to valid (albeit
/// not necessarily initialized) memory at least `elem_size * count` bytes
/// long.
pub unsafe fn read_ptr(
&self,
elem_size: usize,
bytes: *mut u8,
count: usize,
) -> Result<usize, Status> {
let mut actual_count = 0;
let status =
sys::zx_fifo_read(self.raw_handle(), elem_size, bytes, count, &mut actual_count);
ok(status).map(|()| actual_count)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fifo_basic() {
let (fifo1, fifo2) = Fifo::create(4, 2).unwrap();
// Trying to write less than one element should fail.
assert_eq!(fifo1.write(2, b""), Err(Status::OUT_OF_RANGE));
// Trying to write using a wrong elem_size should fail
assert_eq!(fifo1.write(1, b"hi"), Err(Status::OUT_OF_RANGE));
// Should write one element "he"
assert_eq!(fifo1.write(2, b"he").unwrap(), 1);
// Should write three elements "ll" "o " "wo" and drop the rest as it is full.
assert_eq!(fifo1.write(2, b"llo worlds").unwrap(), 3);
// Now that the fifo is full any further attempts to write should fail.
assert_eq!(fifo1.write(2, b"blahblah"), Err(Status::SHOULD_WAIT));
// Reading with a wrong elem_size should fail
let mut read_vec = vec![0; 8];
assert_eq!(fifo2.read(1, &mut read_vec), Err(Status::OUT_OF_RANGE));
// Read all 4 entries from the other end.
assert_eq!(fifo2.read(2, &mut read_vec).unwrap(), 4);
assert_eq!(read_vec, b"hello wo");
// Reading again should fail as the fifo is empty.
assert_eq!(fifo2.read(2, &mut read_vec), Err(Status::SHOULD_WAIT));
}
}