Skip to main content

shared_buffer/
lib.rs

1// Copyright 2018 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//! Utilities for safely operating on memory shared between untrusting
6//! processes.
7//!
8//! `shared-buffer` provides support for safely operating on memory buffers
9//! which are shared with another process which is untrusted. The Rust memory
10//! model assumes that only code running in the current process - and thus
11//! either trusted or generated by Rust itself - operates on a given region of
12//! memory. As a result, simply treating a region of memory to which another,
13//! untrusted process has read or write access as equivalent to normal process
14//! memory is unsafe. This crate provides the `SharedBuffer` type, which has
15//! methods that allow safe access to such memory.
16//!
17//! Examples of issues that could arise if shared memory were treated as normal
18//! memory include:
19//! - Unintentionally leaking sensitive values to another process
20//! - Allowing other processes to cause an invalid sequence of memory to be
21//!   interpreted as a given type
22
23// NOTES(joshlf) on implementation: We need to worry about the following issues:
24// - If another process has write access to a given region of memory, then
25//   arbitrary writes may happen at any time. Thus, it is never safe to access
26//   this memory through any Rust type other than a raw pointer, or else the
27//   compiler might allow operations or make optimizations based on the
28//   assumption that the memory is either owned (in the case of a mutable
29//   reference) or immutable (in the case of an immutable reference). In either
30//   of these cases, any such allowance or optimization would be unsound. For
31//   example, the compiler might decide that, after having written a T to a
32//   particular memory location, it is safe to read that memory location and
33//   treat it as a T. This would cause undefined behavior if the other process
34//   modified that memory location in the meantime. Perhaps more fundamentally,
35//   both mutable and immutable references guarantee that nobody else is
36//   modifying this memory other than me (and not even me, in the case of an
37//   immutable reference). On this basis alone, it is clear that neither
38//   reference is compatible with foreign write access to the referent.
39// - If another process has read access to a given region of memory, then it
40//   cannot affect the correctness of a Rust program. However, it can do things
41//   that do not technically violate correctness, but are still undesirable. The
42//   canonical example is reading memory which contains sensitive information.
43//   Even if the programmer were to construct a mutable reference to such memory
44//   and write a value to it which the programmer intended to be shared with the
45//   other process, the compiler might use the fact that it had exclusive access
46//   to the memory (so says the mutable reference...) to store any arbitrary
47//   value in the memory temporarily. So long as it's not observable from the
48//   Rust program, it preserves the semantics of the program. Of course, it *is*
49//   observable from the other process, and there are no guarantees on what the
50//   compiler might decide to store there, including any value currently in your
51//   memory space, including particularly sensitive values. As a result, while
52//   read-only access doesn't violate the correctness of a Rust program, it's
53//   still worth handling carefully.
54//
55// In order to address both of these issues, our approach is simple: never treat
56// the memory as anything other than a raw pointer. Do not construct any
57// references, mutable or immutable, even temporarily, and even if they are
58// never used. This basically boils down to only accessing the memory using the
59// various functions from core::ptr which operate directly on raw pointers.
60
61// NOTE(joshlf):
62// - Since you must assume that the other process might be writing to the
63//   memory, there's no technical requirement to have exclusive access. E.g., we
64//   could safely implement Clone, have write and write_at take immutable
65//   references, etc. (see here for a discussion of the soundness of using
66//   copy_nonoverlapping simultaneously in multiple threads:
67//   https://users.rust-lang.org/t/copy-nonoverlapping-concurrently/18353).
68//   However, this would be confusing because it would depart from the Rust
69//   idiom. Instead, we provide SharedBuffer, which has ownership semantics
70//   analogous to Vec, and SharedBufferSlice and SharedBufferSliceMut, which
71//   have reference semantics analogous to immutable and mutable slice
72//   references. Similarly, write, write_at, and release_writes take mutable
73//   references.
74//   Clone and provide slicing methods. There's no point not to.
75// - Since all access to these buffers must go through the methods of
76//   SharedBuffer, correct code may not construct a reference to this memory.
77//   Thus, the references to dst and src passed to read, read_at, write, and
78//   write_at cannot overlap with the buffer itself, and so it's safe to use
79//   ptr::copy_nonoverlapping.
80// - Note on volatility and observability: The memory in a SharedBuffer is
81//   either allocated by this process and then sent to another process, or
82//   allocated by another process and sent to this process. However, on Fuchsia,
83//   what's actually shared is a VMO, which is then mapped into the address
84//   space. While LLVM is almost certainly guaranteed to treat this call as
85//   opaque, and thus to be unable to prove to itself that the returned memory
86//   is not shared, it is worth hedging against that reasoning being wrong. If
87//   LLVM were, for some reason, to decide that mapping a VMO resulted in
88//   uniquely owned memory, it would be able to reason that writes to that
89//   memory could never be observed by other threads, and so if the writes were
90//   not observed by the _current_ thread, they could be elided altogether since
91//   they could have no effect. In order to hedge against this possibility, and
92//   to ensure that LLVM definitely cannot take this line of reasoning, we
93//   volatile write the pointer when we first construct the SharedBuffer. LLVM
94//   must conclude that it doesn't know who else is using the memory once a
95//   pointer to it has been written in a volatile manner, and so must assume
96//   that all future writes must be observable. This single volatile write which
97//   happens at most once per message (although more likely once when the
98//   connection is first established) has minimal performance overhead.
99
100// TODO(joshlf):
101// - Create a variant for read-only memory
102// - Create a variant for write-only memory?
103
104#![no_std]
105
106use core::marker::PhantomData;
107use core::ops::{Bound, Range, RangeBounds};
108use core::ptr;
109use core::sync::atomic::{Ordering, fence};
110
111mod sealed {
112    // A buffer with no ownership or reference semantics. It is the caller's
113    // responsibility to wrap this type in a type which provides ownership or
114    // reference semantics, and to only call methods when appropriate.
115    #[derive(Debug)]
116    pub struct SharedBufferInner {
117        // invariant: '(buf as usize) + len' doesn't overflow usize
118        pub(super) buf: *mut u8,
119        pub(super) len: usize,
120    }
121
122    pub trait AsSharedBufferInner {
123        fn as_inner(&self) -> &SharedBufferInner;
124    }
125}
126use sealed::SharedBufferInner;
127
128impl SharedBufferInner {
129    fn read_at(&self, offset: usize, dst: &mut [u8]) -> usize {
130        if let Some(to_copy) = overlap(offset, dst.len(), self.len) {
131            // Since overlap returned Some, we're guaranteed that 'offset +
132            // to_copy <= self.len'. That in turn means that, so long as the
133            // invariant holds that '(self.buf as usize) + self.len' doesn't
134            // overflow usize, then this call to offset_from won't overflow, and
135            // neither will the call to copy_nonoverlapping.
136            let base = offset_from(self.buf, offset);
137            unsafe { ptr::copy_nonoverlapping(base, dst.as_mut_ptr(), to_copy) };
138            to_copy
139        } else {
140            panic!("byte offset {} out of range for SharedBuffer of length {}", offset, self.len);
141        }
142    }
143
144    fn write_at(&self, offset: usize, src: &[u8]) -> usize {
145        if let Some(to_copy) = overlap(offset, src.len(), self.len) {
146            // Since overlap returned Some, we're guaranteed that 'offset +
147            // to_copy <= self.len'. That in turn means that, so long as the
148            // invariant holds that '(self.buf as usize) + self.len' doesn't
149            // overflow usize, then this call to offset_from won't overflow, and
150            // neither will the call to copy_nonoverlapping.
151            let base = offset_from(self.buf, offset);
152            unsafe { ptr::copy_nonoverlapping(src.as_ptr(), base, to_copy) };
153            to_copy
154        } else {
155            panic!("byte offset {} out of range for SharedBuffer of length {}", offset, self.len);
156        }
157    }
158
159    fn copy_from_at(
160        &self,
161        dst_offset: usize,
162        src: &SharedBufferInner,
163        src_offset: usize,
164        len: usize,
165    ) -> usize {
166        let Some(dst_avail) = overlap(dst_offset, len, self.len) else {
167            panic!(
168                "destination byte offset {} out of range for SharedBuffer of length {}",
169                dst_offset, self.len
170            );
171        };
172        let Some(src_avail) = overlap(src_offset, len, src.len) else {
173            panic!(
174                "source byte offset {} out of range for SharedBuffer of length {}",
175                src_offset, src.len
176            );
177        };
178        let to_copy = dst_avail.min(src_avail);
179        if to_copy > 0 {
180            let dst_base = offset_from(self.buf, dst_offset);
181            let src_base = offset_from(src.buf, src_offset);
182            // SAFETY: Since `overlap` returned `Some` for both `self` and
183            // `src`, `dst_offset + to_copy <= self.len` and
184            // `src_offset + to_copy <= src.len`. Combined with the invariant
185            // that `(buf as usize) + len` does not overflow `usize`, neither
186            // `offset_from` nor the `to_copy`-byte copy can overflow, both
187            // regions are valid for `to_copy` bytes, and `ptr::copy` permits
188            // overlapping ranges.
189            unsafe { ptr::copy(src_base, dst_base, to_copy) };
190        }
191        to_copy
192    }
193
194    fn slice<R: RangeBounds<usize>>(&self, range: R) -> SharedBufferInner {
195        let range = canonicalize_range_infallible(self.len, range);
196        SharedBufferInner { buf: offset_from(self.buf, range.start), len: range.end - range.start }
197    }
198
199    fn split_at(&self, idx: usize) -> (SharedBufferInner, SharedBufferInner) {
200        assert!(idx <= self.len, "split index out of bounds");
201        let a = SharedBufferInner { buf: self.buf, len: idx };
202        let b = SharedBufferInner { buf: offset_from(self.buf, idx), len: self.len - idx };
203        (a, b)
204    }
205}
206
207// Verifies that 'offset' is in range of range_len (that 'offset <= range_len'),
208// and returns the amount of overlap between a copy of length 'copy_len'
209// starting at 'offset' and a buffer of length 'range_len'. The number it
210// returns is guaranteed to be less than or equal to 'range_len'.
211//
212// overlap is guaranteed to be correct for any three usize values.
213fn overlap(offset: usize, copy_len: usize, range_len: usize) -> Option<usize> {
214    if offset > range_len {
215        None
216    } else if offset.checked_add(copy_len).map(|sum| sum <= range_len).unwrap_or(false) {
217        // if 'offset + copy_len' overflows usize, then 'offset + copy_len >
218        // range_len', so we unwrap_or(false)
219        Some(copy_len)
220    } else {
221        Some(range_len - offset)
222    }
223}
224
225// Like the offset method on primitive pointers, but for unsigned offsets. Both
226// the 'offset' and 'add' methods on primitive pointers have the limitation that
227// the offset cannot overflow an isize or else it will cause UB. offset_from
228// function has no such restriction.
229//
230// The caller must guarantee that '(ptr as usize) + offset' doesn't overflow
231// usize.
232fn offset_from(ptr: *mut u8, offset: usize) -> *mut u8 {
233    // just in case our logic is wrong, better to catch it at runtime than
234    // invoke UB
235    (ptr as usize).checked_add(offset).unwrap() as *mut u8
236}
237
238// Return the inclusive equivalent of the bound.
239fn canonicalize_lower_bound(bound: Bound<&usize>) -> usize {
240    match bound {
241        Bound::Included(x) => *x,
242        Bound::Excluded(x) => *x + 1,
243        Bound::Unbounded => 0,
244    }
245}
246// Return the exclusive equivalent of the bound, verifying that it is in range
247// of len.
248fn canonicalize_upper_bound(len: usize, bound: Bound<&usize>) -> Option<usize> {
249    let bound = match bound {
250        Bound::Included(x) => *x + 1,
251        Bound::Excluded(x) => *x,
252        Bound::Unbounded => len,
253    };
254    if bound > len {
255        return None;
256    }
257    Some(bound)
258}
259// Return the inclusive-exclusive equivalent of the bound, verifying that it is
260// in range of len, and panicking if it is not or if the range is nonsensical.
261fn canonicalize_range_infallible<R: RangeBounds<usize>>(len: usize, range: R) -> Range<usize> {
262    let lower = canonicalize_lower_bound(range.start_bound());
263    let upper =
264        canonicalize_upper_bound(len, range.end_bound()).expect("slice range out of bounds");
265    assert!(lower <= upper, "invalid range");
266    lower..upper
267}
268
269/// A shared region of memory.
270///
271/// A `SharedBuffer` is a view into a region of memory to which another process
272/// has access. It provides methods to access this memory in a way that
273/// preserves memory safety. From the perspective of the current process, it
274/// owns its memory (analogous to a `Vec`).
275///
276/// Since the buffer is shared by an untrusted process, it is never valid to
277/// assume that a given region of the buffer will not change in between method
278/// calls. Even if no thread in this process wrote anything to the buffer, the
279/// other process might have.
280///
281/// # Unmapping
282///
283/// `SharedBuffer`s do nothing when dropped. In order to avoid leaking memory,
284/// use the `consume` method to consume the `SharedBuffer` and get back the
285/// underlying pointer and length, and unmap the memory manually.
286#[derive(Debug)]
287pub struct SharedBuffer {
288    inner: SharedBufferInner,
289}
290
291impl SharedBuffer {
292    /// Create a new `SharedBuffer` from a raw buffer.
293    ///
294    /// `new` creates a new `SharedBuffer` from the provided buffer and lenth,
295    /// taking ownership of the memory.
296    ///
297    /// # Safety
298    ///
299    /// Memory in a shared buffer must never be accessed except through the
300    /// methods of `SharedBuffer`. It must not be treated as normal memory, and
301    /// pointers to it must not be passed to unsafe code which is designed to
302    /// operate on normal memory. It must be guaranteed that, for the lifetime
303    /// of the `SharedBuffer`, the memory region is mapped, readable, and
304    /// writable.
305    ///
306    /// If any of these guarantees are violated, it may cause undefined
307    /// behavior.
308    #[inline]
309    pub unsafe fn new(buf: *mut u8, len: usize) -> SharedBuffer {
310        // Write the pointer and the length using a volatile write so that LLVM
311        // must assume that the memory has escaped, and that all future writes
312        // to it are observable. See the NOTE above for more details.
313        let mut scratch = (ptr::null_mut(), 0);
314        unsafe { ptr::write_volatile(&mut scratch, (buf, len)) };
315
316        // Acquire any writes to the buffer that happened in a different thread
317        // or process already so they are visible without having to call the
318        // acquire_writes method.
319        fence(Ordering::Acquire);
320        SharedBuffer { inner: SharedBufferInner { buf, len } }
321    }
322
323    /// Read bytes from the buffer.
324    ///
325    /// Read up to `dst.len()` bytes from the buffer, returning how many bytes
326    /// were read. The only thing that can cause fewer bytes to be read than
327    /// requested is if `dst` is larger than the buffer itself.
328    ///
329    /// A call to `read` is only guaranteed to happen after an operation in
330    /// another thread or process if the mechanism used to signal the other
331    /// process has well-defined memory ordering semantics. Otherwise, the
332    /// `acquire_writes` method must be called before `read` and after receiving
333    /// a signal from the other process in order to provide such ordering
334    /// guarantees. In practice, this means that `acquire_writes` should be the
335    /// first read operation that happens after receiving a signal from another
336    /// process that the memory may be read. See the `acquire_writes`
337    /// documentation for more details.
338    #[inline]
339    pub fn read(&self, dst: &mut [u8]) -> usize {
340        self.inner.read_at(0, dst)
341    }
342
343    /// Read bytes from the buffer at an offset.
344    ///
345    /// Read up to `dst.len()` bytes starting at `offset` into the buffer,
346    /// returning how many bytes were read. The only thing that can cause fewer
347    /// bytes to be read than requested is if there are fewer than `dst.len()`
348    /// bytes available starting at `offset` within the buffer.
349    ///
350    /// A call to `read_at` is only guaranteed to happen after an operation in
351    /// another thread or process if the mechanism used to signal the other
352    /// process has well-defined memory ordering semantics. Otherwise, the
353    /// `acquire_writes` method must be called before `read_at` and after
354    /// receiving a signal from the other process in order to provide such
355    /// ordering guarantees. In practice, this means that `acquire_writes`
356    /// should be the first read operation that happens after receiving a signal
357    /// from another process that the memory may be read. See the
358    /// `acquire_writes` documentation for more details.
359    ///
360    /// # Panics
361    ///
362    /// `read_at` panics if `offset` is greater than the length of the buffer.
363    #[inline]
364    pub fn read_at(&self, offset: usize, dst: &mut [u8]) -> usize {
365        self.inner.read_at(offset, dst)
366    }
367
368    /// Write bytes to the buffer.
369    ///
370    /// Write up to `src.len()` bytes into the buffer, returning how many bytes
371    /// were written. The only thing that can cause fewer bytes to be written
372    /// than requested is if `src` is larger than the buffer itself.
373    ///
374    /// A call to `write` is only guaranteed to happen before an operation in
375    /// another thread or process if the mechanism used to signal the other
376    /// process has well-defined memory ordering semantics. Otherwise, the
377    /// `release_writes` method must be called after `write` and before
378    /// signalling the other process in order to provide such ordering
379    /// guarantees. In practice, this means that `release_writes` should be the
380    /// last write operation that happens before signalling another process that
381    /// the memory may be read. See the `release_writes` documentation for more
382    /// details.
383    #[inline]
384    pub fn write(&self, src: &[u8]) -> usize {
385        self.inner.write_at(0, src)
386    }
387
388    /// Write bytes to the buffer at an offset.
389    ///
390    /// Write up to `src.len()` bytes starting at `offset` into the buffer,
391    /// returning how many bytes were written. The only thing that can cause
392    /// fewer bytes to be written than requested is if there are fewer than
393    /// `src.len()` bytes available starting at `offset` within the buffer.
394    ///
395    /// A call to `write_at` is only guaranteed to happen before an operation in
396    /// another thread or process if the mechanism used to signal the other
397    /// process has well-defined memory ordering semantics. Otherwise, the
398    /// `release_writes` method must be called after `write_at` and before
399    /// signalling the other process in order to provide such ordering
400    /// guarantees. In practice, this means that `release_writes` should be the
401    /// last write operation that happens before signalling another process that
402    /// the memory may be read. See the `release_writes` documentation for more
403    /// details.
404    ///
405    /// # Panics
406    ///
407    /// `write_at` panics if `offset` is greater than the length of the buffer.
408    #[inline]
409    pub fn write_at(&self, offset: usize, src: &[u8]) -> usize {
410        self.inner.write_at(offset, src)
411    }
412
413    /// Copy bytes from a `SharedBuffer` or slice into this buffer at an offset.
414    ///
415    /// Copy up to `len` bytes starting at `src_offset` in `src` into `self`
416    /// starting at `dst_offset`, returning how many bytes were copied. `src`
417    /// may refer to the same underlying memory as `self` (overlapping regions
418    /// are handled safely). The only thing that can cause fewer bytes to be
419    /// copied than requested is if there are fewer than `len` bytes available
420    /// starting at `dst_offset` in `self` or starting at `src_offset` in `src`.
421    ///
422    /// Since `copy_from_at` reads from `src` and writes to `self`, a call to
423    /// `copy_from_at` is only guaranteed to happen after an operation on `src`
424    /// or before an operation on `self` in another thread or process if the
425    /// mechanism used to signal the other process has well-defined memory
426    /// ordering semantics. Otherwise, `src.acquire_writes()` must be called
427    /// before `copy_from_at` (after receiving a signal from the other process
428    /// that `src` may be read) and `self.release_writes()` must be called after
429    /// `copy_from_at` (before signalling the other process that `self` may be
430    /// read). See the `acquire_writes` and `release_writes` documentation for
431    /// more details.
432    ///
433    /// # Panics
434    ///
435    /// `copy_from_at` panics if `dst_offset` is greater than the length of
436    /// `self` or `src_offset` is greater than the length of `src`.
437    #[inline]
438    pub fn copy_from_at<B: AsSharedBuffer + ?Sized>(
439        &self,
440        dst_offset: usize,
441        src: &B,
442        src_offset: usize,
443        len: usize,
444    ) -> usize {
445        self.inner.copy_from_at(dst_offset, src.as_inner(), src_offset, len)
446    }
447
448    /// Acquire all writes performed by the other process.
449    ///
450    /// On some systems (such as Fuchsia, currently), the communication
451    /// mechanism used for signalling a process that memory is readable does not
452    /// have well-defined synchronization semantics. On those systems, this
453    /// method MUST be called after receiving such a signal, or else writes
454    /// performed before that signal are not guaranteed to be observed by this
455    /// process.
456    ///
457    /// `acquire_writes` acquires any writes performed on this buffer or any
458    /// slice within the buffer.
459    ///
460    /// # Note on Fuchsia
461    ///
462    /// Zircon, the Fuchsia kernel, will likely eventually have well-defined
463    /// semantics around the synchronization behavior of various syscalls. Once
464    /// that happens, calling this method in Fuchsia programs may become
465    /// optional. This work is tracked in [https://fxbug.dev/42107145].
466    ///
467    /// [https://fxbug.dev/42107145]: #
468    // TODO(joshlf): Replace with link once issues are public.
469    #[inline]
470    pub fn acquire_writes(&self) {
471        fence(Ordering::Acquire);
472    }
473
474    /// Release all writes performed so far.
475    ///
476    /// On some systems (such as Fuchsia, currently), the communication
477    /// mechanism used for signalling the other process that memory is readable
478    /// does not have well-defined synchronization semantics. On those systems,
479    /// this method MUST be called before such signalling, or else writes
480    /// performed before that signal are not guaranteed to be observed by the
481    /// other process.
482    ///
483    /// `release_writes` releases any writes performed on this buffer or any
484    /// slice within the buffer.
485    ///
486    /// # Note on Fuchsia
487    ///
488    /// Zircon, the Fuchsia kernel, will likely eventually have well-defined
489    /// semantics around the synchronization behavior of various syscalls. Once
490    /// that happens, calling this method in Fuchsia programs may become
491    /// optional. This work is tracked in [https://fxbug.dev/42107145].
492    ///
493    /// [https://fxbug.dev/42107145]: #
494    // TODO(joshlf): Replace with link once issues are public.
495    #[inline]
496    pub fn release_writes(&mut self) {
497        fence(Ordering::Release);
498    }
499
500    /// The number of bytes in this `SharedBuffer`.
501    #[inline]
502    pub fn len(&self) -> usize {
503        self.inner.len
504    }
505
506    /// Create a slice of the original `SharedBuffer`.
507    ///
508    /// Just like the slicing operation on array and slice references, `slice`
509    /// constructs a `SharedBufferSlice` which points to the same memory as the
510    /// original `SharedBuffer`, but starting and index `from` (inclusive) and
511    /// ending at index `to` (exclusive).
512    ///
513    /// # Panics
514    ///
515    /// `slice` panics if `range` is out of bounds of `self` or if `range` is
516    /// nonsensical (its lower bound is larger than its upper bound).
517    #[inline]
518    pub fn slice<'a, R: RangeBounds<usize>>(&'a self, range: R) -> SharedBufferSlice<'a> {
519        SharedBufferSlice { inner: self.inner.slice(range), _marker: PhantomData }
520    }
521
522    /// Create a mutable slice of the original `SharedBuffer`.
523    ///
524    /// Just like the mutable slicing operation on array and slice references,
525    /// `slice_mut` constructs a `SharedBufferSliceMut` which points to the same
526    /// memory as the original `SharedBuffer`, but starting and index `from`
527    /// (inclusive) and ending at index `to` (exclusive).
528    ///
529    /// # Panics
530    ///
531    /// `slice_mut` panics if `range` is out of bounds of `self` or if `range`
532    /// is nonsensical (its lower bound is larger than its upper bound).
533    #[inline]
534    pub fn slice_mut<'a, R: RangeBounds<usize>>(
535        &'a mut self,
536        range: R,
537    ) -> SharedBufferSliceMut<'a> {
538        SharedBufferSliceMut { inner: self.inner.slice(range), _marker: PhantomData }
539    }
540
541    /// Create two non-overlapping slices of the original `SharedBuffer`.
542    ///
543    /// Just like the `split_at` method on array and slice references,
544    /// `split_at` constructs one `SharedBufferSlice` which represents bytes
545    /// `[0, idx)`, and one which represents bytes `[idx, len)`, where `len` is
546    /// the length of the buffer.
547    ///
548    /// # Panics
549    ///
550    /// `split_at` panics if `idx > self.len()`.
551    #[inline]
552    pub fn split_at<'a>(&'a self, idx: usize) -> (SharedBufferSlice<'a>, SharedBufferSlice<'a>) {
553        let (a, b) = self.inner.split_at(idx);
554        let a = SharedBufferSlice { inner: a, _marker: PhantomData };
555        let b = SharedBufferSlice { inner: b, _marker: PhantomData };
556        (a, b)
557    }
558
559    /// Create two non-overlapping mutable slices of the original `SharedBuffer`.
560    ///
561    /// Just like the `split_at_mut` method on array and slice references,
562    /// `split_at_miut` constructs one `SharedBufferSliceMut` which represents
563    /// bytes `[0, idx)`, and one which represents bytes `[idx, len)`, where
564    /// `len` is the length of the buffer.
565    ///
566    /// # Panics
567    ///
568    /// `split_at_mut` panics if `idx > self.len()`.
569    #[inline]
570    pub fn split_at_mut<'a>(
571        &'a mut self,
572        idx: usize,
573    ) -> (SharedBufferSliceMut<'a>, SharedBufferSliceMut<'a>) {
574        let (a, b) = self.inner.split_at(idx);
575        let a = SharedBufferSliceMut { inner: a, _marker: PhantomData };
576        let b = SharedBufferSliceMut { inner: b, _marker: PhantomData };
577        (a, b)
578    }
579
580    /// Get the buffer pointer and length so that the memory can be freed.
581    ///
582    /// This method is an alternative to calling `consume` if relinquishing
583    /// ownership of the object is infeasible (for example, when the object is a
584    /// struct field and thus can't be moved out of the struct). Since it allows
585    /// the object to continue existing, it must be used with care (see the
586    /// "Safety" section below).
587    ///
588    /// # Safety
589    ///
590    /// The returned pointer must *only* be used to free the memory. Since the
591    /// memory is shared by another process, using it as a normal raw pointer to
592    /// normal memory owned by this process is unsound.
593    ///
594    /// If the pointer is used for this purpose, then the caller must ensure
595    /// that no methods will be called on the object after the call to
596    /// `as_ptr_len`. The only scenario in which the object may be used again is
597    /// if the caller does nothing at all with the return value of this method
598    /// (although that would be kind of pointless...).
599    pub fn as_ptr_len(&mut self) -> (*mut u8, usize) {
600        (self.inner.buf, self.inner.len)
601    }
602
603    /// Consume the `SharedBuffer`, returning the underlying buffer pointer and
604    /// length.
605    ///
606    /// Since `SharedBuffer`s do nothing on drop, the only way to ensure that
607    /// resources are not leaked is to `consume` a `SharedBuffer` and then unmap
608    /// the memory manually.
609    #[inline]
610    pub fn consume(self) -> (*mut u8, usize) {
611        (self.inner.buf, self.inner.len)
612    }
613}
614
615impl Drop for SharedBuffer {
616    fn drop(&mut self) {
617        // Release any writes performed after the last call to
618        // self.release_writes().
619        fence(Ordering::Release);
620    }
621}
622
623/// An immutable slice into a `SharedBuffer`.
624///
625/// A `SharedBufferSlice` is created with `SharedBuffer::slice`,
626/// `SharedBufferSlice::slice`, or `SharedBufferSliceMut::slice`.
627#[derive(Debug)]
628pub struct SharedBufferSlice<'a> {
629    inner: SharedBufferInner,
630    _marker: PhantomData<&'a ()>,
631}
632
633impl<'a> SharedBufferSlice<'a> {
634    /// Read bytes from the buffer.
635    ///
636    /// Read up to `dst.len()` bytes from the buffer, returning how many bytes
637    /// were read. The only thing that can cause fewer bytes to be read than
638    /// requested is if `dst` is larger than the buffer itself.
639    ///
640    /// A call to `read` is only guaranteed to happen after an operation in
641    /// another thread or process if the mechanism used to signal the other
642    /// process has well-defined memory ordering semantics. Otherwise, the
643    /// `acquire_writes` method must be called before `read` and after receiving
644    /// a signal from the other process in order to provide such ordering
645    /// guarantees. In practice, this means that `acquire_writes` should be the
646    /// first read operation that happens after receiving a signal from another
647    /// process that the memory may be read. See the `acquire_writes`
648    /// documentation for more details.
649    #[inline]
650    pub fn read(&self, dst: &mut [u8]) -> usize {
651        self.inner.read_at(0, dst)
652    }
653
654    /// Read bytes from the buffer at an offset.
655    ///
656    /// Read up to `dst.len()` bytes starting at `offset` into the buffer,
657    /// returning how many bytes were read. The only thing that can cause fewer
658    /// bytes to be read than requested is if there are fewer than `dst.len()`
659    /// bytes available starting at `offset` within the buffer.
660    ///
661    /// A call to `read_at` is only guaranteed to happen after an operation in
662    /// another thread or process if the mechanism used to signal the other
663    /// process has well-defined memory ordering semantics. Otherwise, the
664    /// `acquire_writes` method must be called before `read_at` and after
665    /// receiving a signal from the other process in order to provide such
666    /// ordering guarantees. In practice, this means that `acquire_writes`
667    /// should be the first read operation that happens after receiving a signal
668    /// from another process that the memory may be read. See the
669    /// `acquire_writes` documentation for more details.
670    ///
671    /// # Panics
672    ///
673    /// `read_at` panics if `offset` is greater than the length of the buffer.
674    #[inline]
675    pub fn read_at(&self, offset: usize, dst: &mut [u8]) -> usize {
676        self.inner.read_at(offset, dst)
677    }
678
679    /// Acquire all writes performed by the other process.
680    ///
681    /// On some systems (such as Fuchsia, currently), the communication
682    /// mechanism used for signalling a process that memory is readable does not
683    /// have well-defined synchronization semantics. On those systems, this
684    /// method MUST be called after receiving such a signal, or else writes
685    /// performed before that signal are not guaranteed to be observed by this
686    /// process.
687    ///
688    /// `acquire_writes` acquires any writes performed on this buffer or any
689    /// slice within the buffer.
690    ///
691    /// # Note on Fuchsia
692    ///
693    /// Zircon, the Fuchsia kernel, will likely eventually have well-defined
694    /// semantics around the synchronization behavior of various syscalls. Once
695    /// that happens, calling this method in Fuchsia programs may become
696    /// optional. This work is tracked in [https://fxbug.dev/42107145].
697    ///
698    /// [https://fxbug.dev/42107145]: #
699    // TODO(joshlf): Replace with link once issues are public.
700    #[inline]
701    pub fn acquire_writes(&self) {
702        fence(Ordering::Acquire);
703    }
704
705    /// Create a sub-slice of this `SharedBufferSlice`.
706    ///
707    /// Just like the slicing operation on array and slice references, `slice`
708    /// constructs a new `SharedBufferSlice` which points to the same memory as
709    /// the original, but starting and index `from` (inclusive) and ending at
710    /// index `to` (exclusive).
711    ///
712    /// # Panics
713    ///
714    /// `slice` panics if `range` is out of bounds of `self` or if `range` is
715    /// nonsensical (its lower bound is larger than its upper bound).
716    #[inline]
717    pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> SharedBufferSlice<'a> {
718        SharedBufferSlice { inner: self.inner.slice(range), _marker: PhantomData }
719    }
720
721    /// Split this `SharedBufferSlice` in two.
722    ///
723    /// Just like the `split_at` method on array and slice references,
724    /// `split_at` constructs one `SharedBufferSlice` which represents bytes
725    /// `[0, idx)`, and one which represents bytes `[idx, len)`, where `len` is
726    /// the length of the buffer slice.
727    ///
728    /// # Panics
729    ///
730    /// `split_at` panics if `idx > self.len()`.
731    #[inline]
732    pub fn split_at(&self, idx: usize) -> (SharedBufferSlice<'a>, SharedBufferSlice<'a>) {
733        let (a, b) = self.inner.split_at(idx);
734        let a = SharedBufferSlice { inner: a, _marker: PhantomData };
735        let b = SharedBufferSlice { inner: b, _marker: PhantomData };
736        (a, b)
737    }
738
739    /// The number of bytes in this `SharedBufferSlice`.
740    #[inline]
741    pub fn len(&self) -> usize {
742        self.inner.len
743    }
744}
745
746/// A mutable slice into a `SharedBuffer`.
747///
748/// A `SharedBufferSliceMut` is created with `SharedBuffer::slice_mut` or
749/// `SharedBufferSliceMut::slice_mut`.
750#[derive(Debug)]
751pub struct SharedBufferSliceMut<'a> {
752    inner: SharedBufferInner,
753    _marker: PhantomData<&'a ()>,
754}
755
756impl<'a> SharedBufferSliceMut<'a> {
757    /// Read bytes from the buffer.
758    ///
759    /// Read up to `dst.len()` bytes from the buffer, returning how many bytes
760    /// were read. The only thing that can cause fewer bytes to be read than
761    /// requested is if `dst` is larger than the buffer itself.
762    ///
763    /// A call to `read` is only guaranteed to happen after an operation in
764    /// another thread or process if the mechanism used to signal the other
765    /// process has well-defined memory ordering semantics. Otherwise, the
766    /// `acquire_writes` method must be called before `read` and after receiving
767    /// a signal from the other process in order to provide such ordering
768    /// guarantees. In practice, this means that `acquire_writes` should be the
769    /// first read operation that happens after receiving a signal from another
770    /// process that the memory may be read. See the `acquire_writes`
771    /// documentation for more details.
772    #[inline]
773    pub fn read(&self, dst: &mut [u8]) -> usize {
774        self.inner.read_at(0, dst)
775    }
776
777    /// Read bytes from the buffer at an offset.
778    ///
779    /// Read up to `dst.len()` bytes starting at `offset` into the buffer,
780    /// returning how many bytes were read. The only thing that can cause fewer
781    /// bytes to be read than requested is if there are fewer than `dst.len()`
782    /// bytes available starting at `offset` within the buffer.
783    ///
784    /// A call to `read_at` is only guaranteed to happen after an operation in
785    /// another thread or process if the mechanism used to signal the other
786    /// process has well-defined memory ordering semantics. Otherwise, the
787    /// `acquire_writes` method must be called before `read_at` and after
788    /// receiving a signal from the other process in order to provide such
789    /// ordering guarantees. In practice, this means that `acquire_writes`
790    /// should be the first read operation that happens after receiving a signal
791    /// from another process that the memory may be read. See the
792    /// `acquire_writes` documentation for more details.
793    ///
794    /// # Panics
795    ///
796    /// `read_at` panics if `offset` is greater than the length of the buffer.
797    #[inline]
798    pub fn read_at(&self, offset: usize, dst: &mut [u8]) -> usize {
799        self.inner.read_at(offset, dst)
800    }
801
802    /// Write bytes to the buffer.
803    ///
804    /// Write up to `src.len()` bytes into the buffer, returning how many bytes
805    /// were written. The only thing that can cause fewer bytes to be written
806    /// than requested is if `src` is larger than the buffer itself.
807    ///
808    /// A call to `write` is only guaranteed to happen before an operation in
809    /// another thread or process if the mechanism used to signal the other
810    /// process has well-defined memory ordering semantics. Otherwise, the
811    /// `release_writes` method must be called after `write` and before
812    /// signalling the other process in order to provide such ordering
813    /// guarantees. In practice, this means that `release_writes` should be the
814    /// last write operation that happens before signalling another process that
815    /// the memory may be read. See the `release_writes` documentation for more
816    /// details.
817    #[inline]
818    pub fn write(&self, src: &[u8]) -> usize {
819        self.inner.write_at(0, src)
820    }
821
822    /// Write bytes to the buffer at an offset.
823    ///
824    /// Write up to `src.len()` bytes starting at `offset` into the buffer,
825    /// returning how many bytes were written. The only thing that can cause
826    /// fewer bytes to be written than requested is if there are fewer than
827    /// `src.len()` bytes available starting at `offset` within the buffer.
828    ///
829    /// A call to `write_at` is only guaranteed to happen before an operation in
830    /// another thread or process if the mechanism used to signal the other
831    /// process has well-defined memory ordering semantics. Otherwise, the
832    /// `release_writes` method must be called after `write_at` and before
833    /// signalling the other process in order to provide such ordering
834    /// guarantees. In practice, this means that `release_writes` should be the
835    /// last write operation that happens before signalling another process that
836    /// the memory may be read. See the `release_writes` documentation for more
837    /// details.
838    ///
839    /// # Panics
840    ///
841    /// `write_at` panics if `offset` is greater than the length of the buffer.
842    #[inline]
843    pub fn write_at(&self, offset: usize, src: &[u8]) -> usize {
844        self.inner.write_at(offset, src)
845    }
846
847    /// Copy bytes from a `SharedBuffer` or slice into this buffer at an offset.
848    ///
849    /// Copy up to `len` bytes starting at `src_offset` in `src` into `self`
850    /// starting at `dst_offset`, returning how many bytes were copied. `src`
851    /// may refer to the same underlying memory as `self` (overlapping regions
852    /// are handled safely). The only thing that can cause fewer bytes to be
853    /// copied than requested is if there are fewer than `len` bytes available
854    /// starting at `dst_offset` in `self` or starting at `src_offset` in `src`.
855    ///
856    /// Since `copy_from_at` reads from `src` and writes to `self`, a call to
857    /// `copy_from_at` is only guaranteed to happen after an operation on `src`
858    /// or before an operation on `self` in another thread or process if the
859    /// mechanism used to signal the other process has well-defined memory
860    /// ordering semantics. Otherwise, `src.acquire_writes()` must be called
861    /// before `copy_from_at` (after receiving a signal from the other process
862    /// that `src` may be read) and `self.release_writes()` must be called after
863    /// `copy_from_at` (before signalling the other process that `self` may be
864    /// read). See the `acquire_writes` and `release_writes` documentation for
865    /// more details.
866    ///
867    /// # Panics
868    ///
869    /// `copy_from_at` panics if `dst_offset` is greater than the length of
870    /// `self` or `src_offset` is greater than the length of `src`.
871    #[inline]
872    pub fn copy_from_at<B: AsSharedBuffer + ?Sized>(
873        &self,
874        dst_offset: usize,
875        src: &B,
876        src_offset: usize,
877        len: usize,
878    ) -> usize {
879        self.inner.copy_from_at(dst_offset, src.as_inner(), src_offset, len)
880    }
881
882    /// Acquire all writes performed by the other process.
883    ///
884    /// On some systems (such as Fuchsia, currently), the communication
885    /// mechanism used for signalling a process that memory is readable does not
886    /// have well-defined synchronization semantics. On those systems, this
887    /// method MUST be called after receiving such a signal, or else writes
888    /// performed before that signal are not guaranteed to be observed by this
889    /// process.
890    ///
891    /// `acquire_writes` acquires any writes performed on this buffer or any
892    /// slice within the buffer.
893    ///
894    /// # Note on Fuchsia
895    ///
896    /// Zircon, the Fuchsia kernel, will likely eventually have well-defined
897    /// semantics around the synchronization behavior of various syscalls. Once
898    /// that happens, calling this method in Fuchsia programs may become
899    /// optional. This work is tracked in [https://fxbug.dev/42107145].
900    ///
901    /// [https://fxbug.dev/42107145]: #
902    // TODO(joshlf): Replace with link once issues are public.
903    #[inline]
904    pub fn acquire_writes(&self) {
905        fence(Ordering::Acquire);
906    }
907
908    /// Atomically release all writes performed so far.
909    ///
910    /// On some systems (such as Fuchsia, currently), the communication
911    /// mechanism used for signalling the other process that memory is readable
912    /// does not have well-defined synchronization semantics. On those systems,
913    /// this method MUST be called before such signalling, or else writes
914    /// performed before that signal are not guaranteed to be observed by the
915    /// other process.
916    ///
917    /// `release_writes` releases any writes performed on this slice or any
918    /// sub-slice of this slice.
919    ///
920    /// # Note on Fuchsia
921    ///
922    /// Zircon, the Fuchsia kernel, will likely eventually have well-defined
923    /// semantics around the synchronization behavior of various syscalls. Once
924    /// that happens, calling this method in Fuchsia programs may become
925    /// optional. This work is tracked in [https://fxbug.dev/42107145].
926    ///
927    /// [https://fxbug.dev/42107145]: #
928    // TODO(joshlf): Replace with link once issues are public.
929    #[inline]
930    pub fn release_writes(&mut self) {
931        fence(Ordering::Release);
932    }
933
934    /// Create a sub-slice of this `SharedBufferSliceMut`.
935    ///
936    /// Just like the slicing operation on array and slice references, `slice`
937    /// constructs a new `SharedBufferSlice` which points to the same memory as
938    /// the original, but starting and index `from` (inclusive) and ending at
939    /// index `to` (exclusive).
940    ///
941    /// # Panics
942    ///
943    /// `slice` panics if `range` is out of bounds of `self` or if `range` is
944    /// nonsensical (its lower bound is larger than its upper bound).
945    #[inline]
946    pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> SharedBufferSlice<'a> {
947        SharedBufferSlice { inner: self.inner.slice(range), _marker: PhantomData }
948    }
949
950    /// Create a mutable slice of the original `SharedBufferSliceMut`.
951    ///
952    /// Just like the mutable slicing operation on array and slice references,
953    /// `slice_mut` constructs a new `SharedBufferSliceMut` which points to the
954    /// same memory as the original, but starting and index `from` (inclusive)
955    /// and ending at index `to` (exclusive).
956    ///
957    /// # Panics
958    ///
959    /// `slice_mut` panics if `range` is out of bounds of `self` or if `range`
960    /// is nonsensical (its lower bound is larger than its upper bound).
961    #[inline]
962    pub fn slice_mut<R: RangeBounds<usize>>(&mut self, range: R) -> SharedBufferSliceMut<'a> {
963        SharedBufferSliceMut { inner: self.inner.slice(range), _marker: PhantomData }
964    }
965
966    /// Split this `SharedBufferSliceMut` into two immutable slices.
967    ///
968    /// Just like the `split_at` method on array and slice references,
969    /// `split_at` constructs one `SharedBufferSlice` which represents bytes
970    /// `[0, idx)`, and one which represents bytes `[idx, len)`, where `len` is
971    /// the length of the buffer slice.
972    ///
973    /// # Panics
974    ///
975    /// `split_at` panics if `idx > self.len()`.
976    #[inline]
977    pub fn split_at(&self, idx: usize) -> (SharedBufferSlice<'a>, SharedBufferSlice<'a>) {
978        let (a, b) = self.inner.split_at(idx);
979        let a = SharedBufferSlice { inner: a, _marker: PhantomData };
980        let b = SharedBufferSlice { inner: b, _marker: PhantomData };
981        (a, b)
982    }
983
984    /// Split this `SharedBufferSliceMut` in two.
985    ///
986    /// Just like the `split_at_mut` method on array and slice references,
987    /// `split_at` constructs one `SharedBufferSliceMut` which represents bytes
988    /// `[0, idx)`, and one which represents bytes `[idx, len)`, where `len` is
989    /// the length of the buffer slice.
990    ///
991    /// # Panics
992    ///
993    /// `split_at_mut` panics if `idx > self.len()`.
994    #[inline]
995    pub fn split_at_mut(
996        &mut self,
997        idx: usize,
998    ) -> (SharedBufferSliceMut<'a>, SharedBufferSliceMut<'a>) {
999        let (a, b) = self.inner.split_at(idx);
1000        let a = SharedBufferSliceMut { inner: a, _marker: PhantomData };
1001        let b = SharedBufferSliceMut { inner: b, _marker: PhantomData };
1002        (a, b)
1003    }
1004
1005    /// The number of bytes in this `SharedBufferSlice`.
1006    #[inline]
1007    pub fn len(&self) -> usize {
1008        self.inner.len
1009    }
1010}
1011
1012// Send and Sync implementations. Send and Sync are definitely safe since
1013// SharedBufferXXX are all written under the assumption that a remote process is
1014// concurrently modifying the memory. However, we aim to provide a Rust-like API
1015// with lifetimes and an immutable/mutable distinction, so the real question is
1016// whether Send and Sync make sense by analogy to normal Rust types. Insofar as
1017// SharedBuffer is analogous to [u8], SharedBufferSlice is analogous to &[u8],
1018// and SharedBufferSliceMut is analogous to &mut [u8], the answer is yes - all
1019// of those types implement both Send and Sync.
1020
1021unsafe impl Send for SharedBuffer {}
1022unsafe impl Sync for SharedBuffer {}
1023unsafe impl<'a> Send for SharedBufferSlice<'a> {}
1024unsafe impl<'a> Sync for SharedBufferSlice<'a> {}
1025unsafe impl<'a> Send for SharedBufferSliceMut<'a> {}
1026unsafe impl<'a> Sync for SharedBufferSliceMut<'a> {}
1027
1028/// A shared buffer or slice that can be used as the source of a copy operation.
1029pub trait AsSharedBuffer: sealed::AsSharedBufferInner {}
1030
1031impl sealed::AsSharedBufferInner for SharedBuffer {
1032    #[inline]
1033    fn as_inner(&self) -> &SharedBufferInner {
1034        &self.inner
1035    }
1036}
1037impl AsSharedBuffer for SharedBuffer {}
1038
1039impl sealed::AsSharedBufferInner for SharedBufferSlice<'_> {
1040    #[inline]
1041    fn as_inner(&self) -> &SharedBufferInner {
1042        &self.inner
1043    }
1044}
1045impl AsSharedBuffer for SharedBufferSlice<'_> {}
1046
1047impl sealed::AsSharedBufferInner for SharedBufferSliceMut<'_> {
1048    #[inline]
1049    fn as_inner(&self) -> &SharedBufferInner {
1050        &self.inner
1051    }
1052}
1053impl AsSharedBuffer for SharedBufferSliceMut<'_> {}
1054
1055impl<T: core::ops::Deref<Target: AsSharedBuffer> + ?Sized> sealed::AsSharedBufferInner for T {
1056    #[inline]
1057    fn as_inner(&self) -> &SharedBufferInner {
1058        (**self).as_inner()
1059    }
1060}
1061impl<T: core::ops::Deref<Target: AsSharedBuffer> + ?Sized> AsSharedBuffer for T {}
1062
1063#[cfg(test)]
1064mod tests {
1065    use core::{mem, ptr};
1066
1067    use super::{SharedBuffer, overlap};
1068
1069    // use the referent as the backing memory for a SharedBuffer
1070    unsafe fn buf_from_ref<T>(x: &mut T) -> SharedBuffer {
1071        let size = mem::size_of::<T>();
1072        unsafe { SharedBuffer::new(x as *mut _ as *mut u8, size) }
1073    }
1074
1075    #[test]
1076    fn test_buf() {
1077        // initialize some memory and turn it into a SharedBuffer
1078        const ONE: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
1079        let mut buf_memory = ONE;
1080        let buf = unsafe { buf_from_ref(&mut buf_memory) };
1081
1082        // we read the same initial contents back
1083        let mut bytes = [0u8; 8];
1084        assert_eq!(buf.read(&mut bytes[..]), 8);
1085        assert_eq!(bytes, ONE);
1086
1087        // when we write new contents, we read those back
1088        const TWO: [u8; 8] = [7, 6, 5, 4, 3, 2, 1, 0];
1089        assert_eq!(buf.write(&TWO[..]), 8);
1090        assert_eq!(buf.read(&mut bytes[..]), 8);
1091        assert_eq!(bytes, TWO);
1092
1093        // even with a bigger buffer, we still only read 8 bytes
1094        let mut bytes = [0u8; 16];
1095        assert_eq!(buf.read(&mut bytes[..]), 8);
1096        // starting at offset 4, there are only 4 bytes left, so we only read 4
1097        // bytes
1098        assert_eq!(buf.read_at(4, &mut bytes[..]), 4);
1099
1100        // copy directly between two SharedBuffers, slices, and overlapping regions
1101        let mut dst_memory = [0u8; 8];
1102        let mut dst_buf = unsafe { buf_from_ref(&mut dst_memory) };
1103        assert_eq!(dst_buf.copy_from_at(2, &buf, 1, 5), 5);
1104        assert_eq!(dst_memory, [0, 0, 6, 5, 4, 3, 2, 0]);
1105
1106        let src_slice = buf.slice(2..6);
1107        let dst_slice_mut = dst_buf.slice_mut(1..7);
1108        assert_eq!(dst_slice_mut.copy_from_at(0, &src_slice, 1, 3), 3);
1109        assert_eq!(dst_memory, [0, 4, 3, 2, 4, 3, 2, 0]);
1110
1111        // overlapping copy within the same buffer
1112        assert_eq!(dst_buf.copy_from_at(2, &dst_buf, 1, 4), 4);
1113        assert_eq!(dst_memory, [0, 4, 4, 3, 2, 4, 2, 0]);
1114
1115        // zero-length copy on an empty buffer does not dereference null
1116        let empty_buf = unsafe { SharedBuffer::new(ptr::null_mut(), 0) };
1117        assert_eq!(empty_buf.copy_from_at(0, &empty_buf, 0, 0), 0);
1118    }
1119
1120    #[test]
1121    fn test_slice() {
1122        // various slices give us the lengths we expect
1123        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1124        let tmp = buf.slice(..);
1125        assert_eq!(tmp.len(), 10);
1126        let tmp = buf.slice(..10);
1127        assert_eq!(tmp.len(), 10);
1128        let tmp = buf.slice(5..10);
1129        assert_eq!(tmp.len(), 5);
1130        let tmp = buf.slice(0..0);
1131        assert_eq!(tmp.len(), 0);
1132        let tmp = buf.slice(10..10);
1133        assert_eq!(tmp.len(), 0);
1134
1135        // initialize some memory and turn it into a SharedBuffer
1136        const INIT: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
1137        let mut buf_memory = INIT;
1138        let buf = unsafe { buf_from_ref(&mut buf_memory) };
1139
1140        // we read the same initial contents back
1141        let mut bytes = [0u8; 8];
1142        assert_eq!(buf.read_at(0, &mut bytes[..]), 8);
1143        assert_eq!(bytes, INIT);
1144
1145        // create a slice to the second half of the SharedBuffer
1146        let buf2 = buf.slice(4..8);
1147
1148        // now we read back only the second half of the original SharedBuffer
1149        bytes = [0; 8];
1150        assert_eq!(buf2.read(&mut bytes[..]), 4);
1151        assert_eq!(bytes, [4, 5, 6, 7, 0, 0, 0, 0]);
1152    }
1153
1154    #[test]
1155    fn test_split() {
1156        // various splits give us the lengths we expect
1157        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1158        let (tmp1, tmp2) = buf.split_at(10);
1159        assert_eq!(tmp1.len(), 10);
1160        assert_eq!(tmp2.len(), 0);
1161        let (tmp1, tmp2) = buf.split_at(5);
1162        assert_eq!(tmp1.len(), 5);
1163        assert_eq!(tmp2.len(), 5);
1164        let (tmp1, tmp2) = buf.split_at(0);
1165        assert_eq!(tmp1.len(), 0);
1166        assert_eq!(tmp2.len(), 10);
1167
1168        // initialize some memory and turn it into a SharedBuffer
1169        const INIT: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
1170        let mut buf_memory = INIT;
1171        let mut buf = unsafe { buf_from_ref(&mut buf_memory) };
1172
1173        // we read the same initial contents back
1174        let mut bytes = [0u8; 8];
1175        assert_eq!(buf.read_at(0, &mut bytes[..]), 8);
1176        assert_eq!(bytes, INIT);
1177
1178        // split in two equal-sized halves
1179        let (buf1, buf2) = buf.split_at_mut(4);
1180
1181        // now we read back the halves separately
1182        bytes = [0; 8];
1183        assert_eq!(buf1.read(&mut bytes[..4]), 4);
1184        assert_eq!(buf2.read(&mut bytes[4..]), 4);
1185        assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]);
1186
1187        // use the mutable slices to write to the buffer
1188        assert_eq!(buf1.write(&[7, 6, 5, 4]), 4);
1189        assert_eq!(buf2.write(&[3, 2, 1, 0]), 4);
1190
1191        // split again into equal-sized quarters
1192        let ((buf1, buf2), (buf3, buf4)) = (buf1.split_at(2), buf2.split_at(2));
1193
1194        // now we read back the quarters separately
1195        bytes = [0; 8];
1196        assert_eq!(buf1.read(&mut bytes[..2]), 2);
1197        assert_eq!(buf2.read(&mut bytes[2..4]), 2);
1198        assert_eq!(buf3.read(&mut bytes[4..6]), 2);
1199        assert_eq!(buf4.read(&mut bytes[6..]), 2);
1200        assert_eq!(bytes, [7, 6, 5, 4, 3, 2, 1, 0]);
1201    }
1202
1203    #[test]
1204    fn test_overlap() {
1205        // overlap(offset, copy_len, range_len)
1206
1207        // first branch: offset > range_len
1208        assert_eq!(overlap(10, 4, 8), None);
1209
1210        // middle branch: offset + copy_len <= range_len
1211        assert_eq!(overlap(0, 4, 8), Some(4));
1212        assert_eq!(overlap(4, 4, 8), Some(4));
1213
1214        // middle branch: 'offset + copy_len' overflows usize
1215        assert_eq!(overlap(4, usize::MAX, 8), Some(4));
1216
1217        // last branch: else
1218        assert_eq!(overlap(6, 4, 8), Some(2));
1219        assert_eq!(overlap(8, 4, 8), Some(0));
1220    }
1221
1222    #[test]
1223    #[should_panic]
1224    fn test_panic_read_at() {
1225        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1226        // "byte offset 11 out of range for SharedBuffer of length 10"
1227        buf.read_at(11, &mut [][..]);
1228    }
1229
1230    #[test]
1231    #[should_panic]
1232    fn test_panic_write_at() {
1233        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1234        // "byte offset 11 out of range for SharedBuffer of length 10"
1235        buf.write_at(11, &[][..]);
1236    }
1237
1238    #[test]
1239    #[should_panic]
1240    fn test_panic_slice_1() {
1241        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1242        // "byte index 11 out of range for SharedBuffer of length 10"
1243        buf.slice(0..11);
1244    }
1245
1246    #[test]
1247    #[should_panic(
1248        expected = "destination byte offset 11 out of range for SharedBuffer of length 10"
1249    )]
1250    fn test_panic_copy_from_at_dst() {
1251        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1252        buf.copy_from_at(11, &buf, 0, 0);
1253    }
1254
1255    #[test]
1256    #[should_panic(expected = "source byte offset 11 out of range for SharedBuffer of length 10")]
1257    fn test_panic_copy_from_at_src() {
1258        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1259        buf.copy_from_at(0, &buf, 11, 0);
1260    }
1261
1262    #[test]
1263    #[should_panic]
1264    fn test_panic_slice_2() {
1265        let buf = unsafe { SharedBuffer::new(ptr::null_mut(), 10) };
1266        // "slice starts at byte 6 but ends at byte 5"
1267        #[allow(clippy::reversed_empty_ranges)]
1268        buf.slice(6..5);
1269    }
1270}