1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
// 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));
}
}