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