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}