Skip to main content

zx/
fifo.rs

1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon fifo objects.
6
7use crate::{NullableHandle, Status, ok, sys};
8use std::mem::MaybeUninit;
9use std::num::NonZeroUsize;
10use zerocopy::{FromBytes, IntoBytes};
11
12/// An object representing a Zircon fifo.
13///
14/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
15///
16/// Encodes the element type in the type. Defaults to `()` for the entry type to allow for untyped
17/// IPC. Use `Fifo::cast()` to convert an IPC-transferred fifo to one of the specific type required
18/// that will support reads and writes.
19#[repr(transparent)]
20pub struct Fifo<R = UnspecifiedFifoElement, W = R>(
21    NullableHandle,
22    std::marker::PhantomData<(R, W)>,
23);
24
25impl_handle_based!(Fifo, [R, W], [R, W]);
26
27impl<R: IntoBytes + FromBytes, W: IntoBytes + FromBytes> Fifo<R, W> {
28    /// Create a pair of fifos and return their endpoints. Writing to one endpoint enqueues an
29    /// element into the fifo from which the opposing endpoint reads.
30    ///
31    /// Wraps the
32    /// [zx_fifo_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_create.md)
33    /// syscall.
34    pub fn create(elem_count: usize) -> Result<(Self, Fifo<W, R>), Status> {
35        if std::mem::size_of::<R>() != std::mem::size_of::<W>() {
36            return Err(Status::INVALID_ARGS);
37        }
38        let mut out0 = 0;
39        let mut out1 = 0;
40        let options = 0;
41
42        // SAFETY: this is a basic FFI call, and the mutable references are valid pointers.
43        let status = unsafe {
44            sys::zx_fifo_create(elem_count, std::mem::size_of::<R>(), options, &mut out0, &mut out1)
45        };
46        ok(status)?;
47
48        // SAFETY: if the above call succeeded, these are valid handle numbers.
49        unsafe {
50            Ok((
51                Fifo::from(NullableHandle::from_raw(out0)),
52                Fifo::from(NullableHandle::from_raw(out1)),
53            ))
54        }
55    }
56
57    /// Attempts to write some number of elements into the fifo. On success, returns the number of
58    /// elements actually written.
59    ///
60    /// Wraps
61    /// [zx_fifo_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_write.md).
62    pub fn write(&self, buf: &[W]) -> Result<NonZeroUsize, Status> {
63        // SAFETY: this pointer is valid for the length of the slice
64        unsafe { self.write_raw(buf.as_ptr(), buf.len()) }
65    }
66
67    /// Attempts to write a single element into the fifo.
68    ///
69    /// Wraps
70    /// [zx_fifo_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_write.md).
71    pub fn write_one(&self, elem: &W) -> Result<(), Status> {
72        // SAFETY: this pointer is valid for a single element
73        unsafe { self.write_raw(elem, 1).map(|n| debug_assert_eq!(n.get(), 1)) }
74    }
75
76    /// Attempts to write some number of elements into the fifo. On success, returns the number of
77    /// elements actually written.
78    ///
79    /// Wraps
80    /// [zx_fifo_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_write.md).
81    ///
82    /// # Safety
83    ///
84    /// The caller is responsible for ensuring `buf` is valid to write to for `count` elements.
85    pub unsafe fn write_raw(&self, buf: *const W, count: usize) -> Result<NonZeroUsize, Status> {
86        let mut actual_count = 0;
87        // SAFETY: safety requirements for this call are upheld by our caller.
88        let status = unsafe {
89            sys::zx_fifo_write(
90                self.raw_handle(),
91                std::mem::size_of::<W>(),
92                buf.cast::<u8>(),
93                count,
94                &mut actual_count,
95            )
96        };
97        ok(status)?;
98        debug_assert_ne!(actual_count, 0);
99        // SAFETY: `zx_fifo_write` returning `ZX_OK` guarantees `actual_count > 0`.
100        Ok(unsafe { NonZeroUsize::new_unchecked(actual_count) })
101    }
102
103    /// Attempts to read some elements out of the fifo. On success, returns the number of elements
104    /// actually read.
105    ///
106    /// Wraps
107    /// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
108    pub fn read(&self, buf: &mut [R]) -> Result<NonZeroUsize, Status> {
109        // SAFETY: the pointer is valid for the length of the slice
110        unsafe { self.read_raw(buf.as_mut_ptr(), buf.len()) }
111    }
112
113    /// Attempts to read a single element out of the fifo.
114    ///
115    /// Wraps
116    /// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
117    pub fn read_one(&self) -> Result<R, Status> {
118        let mut elem = MaybeUninit::uninit();
119
120        // SAFETY: the reference is valid to write to, and this call will not read from the bytes.
121        let valid_count = unsafe { self.read_raw(elem.as_mut_ptr(), 1)? };
122        debug_assert_eq!(valid_count.get(), 1);
123
124        // SAFETY: if the previous call succeeded, the kernel has initialized this value.
125        Ok(unsafe { elem.assume_init() })
126    }
127
128    /// Attempts to read some number of elements out of the fifo. On success, returns a slice of
129    /// initialized elements.
130    ///
131    /// Wraps
132    /// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
133    pub fn read_uninit<'a>(&self, bytes: &'a mut [MaybeUninit<R>]) -> Result<&'a mut [R], Status> {
134        // SAFETY: the slice is valid to write to for its entire length, and this call will not
135        // read from the bytes
136        let valid_count = unsafe { self.read_raw(bytes.as_mut_ptr().cast::<R>(), bytes.len())? };
137        let (valid, _uninit) = bytes.split_at_mut(valid_count.get());
138
139        // SAFETY: the kernel initialized all bytes, strip out MaybeUninit
140        unsafe { Ok(std::slice::from_raw_parts_mut(valid.as_mut_ptr().cast::<R>(), valid.len())) }
141    }
142
143    /// Attempts to read some number of elements out of the fifo. On success, returns the number of
144    /// elements actually read.
145    ///
146    /// Wraps
147    /// [zx_fifo_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/fifo_read.md).
148    ///
149    /// # Safety
150    ///
151    /// The caller is responsible for ensuring `bytes` points to valid (albeit
152    /// not necessarily initialized) memory at least `len` bytes long.
153    pub unsafe fn read_raw(&self, buf: *mut R, count: usize) -> Result<NonZeroUsize, Status> {
154        let mut actual_count = 0;
155        // SAFETY: this call's invariants must be upheld by our caller.
156        let status = unsafe {
157            sys::zx_fifo_read(
158                self.raw_handle(),
159                std::mem::size_of::<R>(),
160                buf.cast::<u8>(),
161                count,
162                &mut actual_count,
163            )
164        };
165        ok(status)?;
166        debug_assert_ne!(actual_count, 0);
167        // SAFETY: `zx_fifo_read` returning `ZX_OK` guarantees `actual_count > 0`.
168        Ok(unsafe { NonZeroUsize::new_unchecked(actual_count) })
169    }
170}
171
172impl Fifo<UnspecifiedFifoElement> {
173    /// Give a `Fifo` specific read/write types. The size of `R2` and `W2` must match
174    /// the element size the underlying handle was created with for reads and writes to succeed.
175    pub fn cast<R2, W2>(self) -> Fifo<R2, W2> {
176        Fifo::<R2, W2>::from(self.0)
177    }
178}
179
180impl<R, W> Fifo<R, W> {
181    /// Convert a fifo from having a specific element type to a fifo without any element type that
182    /// will not support reads or writes.
183    pub fn downcast(self) -> Fifo {
184        Fifo::from(self.0)
185    }
186}
187
188impl<R: FromBytes + IntoBytes, W: FromBytes + IntoBytes> From<Fifo> for Fifo<R, W> {
189    fn from(untyped: Fifo) -> Self {
190        untyped.cast()
191    }
192}
193
194impl<R, W> std::fmt::Debug for Fifo<R, W> {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        let read_name = std::any::type_name::<R>();
197        let (_, short_read_name) = read_name.rsplit_once("::").unwrap();
198        let write_name = std::any::type_name::<W>();
199        let (_, short_write_name) = write_name.rsplit_once("::").unwrap();
200        f.debug_tuple(&format!("Fifo<{short_read_name}, {short_write_name}>"))
201            .field(&self.0)
202            .finish()
203    }
204}
205
206impl<R, W> std::cmp::PartialEq for Fifo<R, W> {
207    fn eq(&self, other: &Self) -> bool {
208        self.0 == other.0
209    }
210}
211impl<R, W> std::cmp::Eq for Fifo<R, W> {}
212
213impl<R, W> std::cmp::PartialOrd for Fifo<R, W> {
214    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
215        self.0.partial_cmp(&other.0)
216    }
217}
218impl<R, W> std::cmp::Ord for Fifo<R, W> {
219    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
220        self.0.cmp(&other.0)
221    }
222}
223
224impl<R, W> std::hash::Hash for Fifo<R, W> {
225    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
226        self.0.hash(state)
227    }
228}
229
230/// The default element for fifos, does not support reading or writing. Only used for IPC transfer.
231#[derive(Copy, Clone, Debug)]
232pub struct UnspecifiedFifoElement;
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[test]
239    fn fifo_basic() {
240        let (fifo1, fifo2) = Fifo::<[u8; 2]>::create(4).unwrap();
241
242        // Trying to write less than one element should fail.
243        assert_eq!(fifo1.write(&[]), Err(Status::OUT_OF_RANGE));
244
245        // Should write one element "he"
246        fifo1.write_one(b"he").unwrap();
247
248        // Should write three elements "ll" "o " "wo" and drop the rest as it is full.
249        assert_eq!(fifo1.write(&[*b"ll", *b"o ", *b"wo", *b"rl", *b"ds"]).unwrap().get(), 3);
250
251        // Now that the fifo is full any further attempts to write should fail.
252        assert_eq!(fifo1.write(&[*b"bl", *b"ah", *b"bl", *b"ah"]), Err(Status::SHOULD_WAIT));
253
254        assert_eq!(fifo2.read_one().unwrap(), *b"he");
255
256        // Read remaining 3 entries from the other end.
257        let mut read_vec = vec![[0; 2]; 8];
258        assert_eq!(fifo2.read(&mut read_vec).unwrap().get(), 3);
259        assert_eq!(read_vec, &[*b"ll", *b"o ", *b"wo", [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]);
260
261        // Reading again should fail as the fifo is empty.
262        assert_eq!(fifo2.read(&mut read_vec), Err(Status::SHOULD_WAIT));
263    }
264}