presser/lib.rs
1//! # `🗜 presser`
2//!
3//! **Utilities to help make copying data around into raw, possibly-uninitialized buffers
4//! easier and safer.**
5//!
6//! ## Motivation
7//!
8//! `presser` can help you when copying data into raw buffers. One primary use-case is copying data into
9//! graphics-api-allocated buffers which will then be accessed by the GPU. Common methods for doing this
10//! right now in Rust can often invoke UB in subtle and hard-to-see ways. For example, viewing an allocated
11//! but uninitialized buffer as an `&mut [u8]` **is instantly undefined behavior**\*, and `transmute`ing even a
12//! `T: Copy` type which has *any padding bytes in its layout* as a `&[u8]` to be the source of a copy is
13//! **also instantly undefined behavior**, in both cases because it is *invalid* to create a reference to an invalid
14//! value (and uninitialized memory is an invalid `u8`), *even if* your code never actually accesses that memory.
15//! This immediately makes what seems like the most straightforward way to copy data into buffers unsound 😬.
16//!
17//! `presser` helps with this by allowing you to view raw allocated memory of some size as a "[`Slab`]" of memory and then
18//! provides *safe, valid* ways to copy data into that memory. For example, you could implement [`Slab`] for your
19//! GPU-allocated buffer type, or use the built-in [`RawAllocation`] workflow described below, then use
20//! [`copy_to_offset_with_align`] to copy any `T: Copy` data into that buffer safely for use on the GPU.
21//! Of course, if your `T` doesn't have the correct layout the GPU expects, accessing it on the GPU side may still be
22//! unsound or at least give an error.
23//!
24//! \* *If you're currently thinking to yourself "bah! what's the issue? surely an uninit u8 is just any random bit pattern
25//! and that's fine we don't care," [check out this blog post](https://www.ralfj.de/blog/2019/07/14/uninit.html) by
26//! @RalfJung, one of the people leading the effort to better define Rust's memory and execution model. As is explored
27//! in that blog post, an *uninit* piece of memory is not simply *an arbitrary bit pattern*, it is a wholly separate
28//! state about a piece of memory, outside of its value, which lets the compiler perform optimizations that reorder,
29//! delete, and otherwise change the actual execution flow of your program in ways that cannot be described simply
30//! by "the value could have *some* possible bit pattern". LLVM and Clang are changing themselves to require special
31//! `noundef` attribute to perform many important optimizations that are otherwise unsound. For a concrete example
32//! of the sorts of problems this can cause,
33//! [see this issue @scottmcm hit](https://github.com/rust-lang/rust/pull/98919#issuecomment-1186106387).*
34//!
35//! ## Introduction
36//!
37//! The main idea is to implement [`Slab`] on raw-buffer-esque-types (see [the `Slab` safety docs][Slab#Safety]),
38//! which then enables the use of the other functions within the crate.
39//!
40//! Depending on your use case, you may be able to implement [`Slab`] directly for your buffer type, or it may
41//! be more convenient or necessary to create a wrapping struct that borrows your raw buffer type and in turn
42//! implements [`Slab`]. For an example of this, see [`RawAllocation`] and [`BorrowedRawAllocation`], which you
43//! may also use directly. The idea is to create a [`RawAllocation`] to your buffer, which you then borrow into
44//! a [`BorrowedRawAllocation`] (which implements [`Slab`]) by calling the unsafe function
45//! [`RawAllocation::borrow_as_slab`]
46//!
47//! Once you have a slab, you can use the copy helper functions provided at the crate root, for example,
48//! [`copy_to_offset`] and [`copy_to_offset_with_align`].
49//!
50//! ### Example
51//!
52//! ```rust,ignore
53//! #[derive(Clone, Copy)]
54//! #[repr(C)]
55//! struct MyDataStruct {
56//! a: u8,
57//! b: u32,
58//! }
59//!
60//! let my_data = MyDataStruct { a: 0, b: 42 };
61//!
62//! // allocate an uninit buffer of some size
63//! let my_buffer: MyBufferType = some_api.alloc_buffer_size(2048);
64//!
65//! // use `RawAllocation` helper to allow access to a presser `Slab`.
66//! // alternatively, you could implement the `Slab` on `MyByfferType` directly if that
67//! // type is owned by your code!
68//! let raw_allocation = presser::RawAllocation::from_raw_parts(my_buffer.ptr(), my_buffer.size());
69//!
70//! // here we assert that we have exclusive access to the data in the buffer, and get the actual
71//! // `Slab` to use to copy into.
72//! let slab = unsafe { raw_allocation.borrow_as_slab(); }
73//!
74//! // now we may safely copy `my_data` into `my_buffer`, starting at a minimum offset of 0 into the buffer
75//! let copy_record = presser::copy_to_offset(&my_data, &mut slab, 0)?;
76//!
77//! // note that due to alignment requirements of `my_data`, the *actual* start of the bytes of
78//! // `my_data` may be placed at a different offset than requested. so, we check the returned
79//! // `CopyRecord` to check the actual start offset of the copied data.
80//! let actual_start_offset = copy_record.copy_start_offset;
81//! ```
82//!
83//! ### `#[no_std]`
84//!
85//! This crate supports `no_std` environments by building without the '`std`' feature. This will limit some
86//! of the fuctions the crate can perform.
87//!
88//! # Safety
89//!
90//! An important note is that obeying the safety rules specified in the [`Slab`] safety documentation
91//! *only* guarantees safety for the *direct results* of the copy operations performed by the
92//! helper functions exported at the crate root (and the safe functions on [`Slab`]). **However**,
93//! it is ***not*** guaranteed that operations which would previously have been safe to perform
94//! using same backing memory that the [`Slab`] you copied into used are still safe.
95//!
96//! For example, say you have a fully-initialized
97//! chunk of bytes (like a `Vec<u8>`), which you view as a [`Slab`], and then (safely) perform a copy
98//! operation into using [`copy_to_offset`]. If the `T` you copied into it has any padding bytes in
99//! its memory layout, then the memory locations where those padding bytes now exist in the underlying `Vec`'s
100//! memory must now be treated as uninitialized. As such, taking any view into that byte vector which
101//! relies on those newly-uninit bytes being initialized to be valid (for example, taking a `&[u8]` slice of the `Vec`
102//! which includes those bytes, ***even if your code never actually reads from that slice***)
103//! is now instant **undefined behavior**.
104#![cfg_attr(not(feature = "std"), no_std)]
105#![deny(unsafe_op_in_unsafe_fn)]
106#![deny(missing_docs)]
107// only enables the `doc_auto_cfg` feature when
108// the `docs_build` configuration attribute is defined
109// this cfg is defined when building on docs.rs (defined thru the project
110// Cargo.toml) and when building the docs for publishing on github pages (thru the
111// .github/workflows/rustdoc-pages.yml workflow)
112#![cfg_attr(docs_build, feature(doc_auto_cfg))]
113
114use core::alloc::Layout;
115use core::alloc::LayoutError;
116use core::marker::PhantomData;
117use core::mem::MaybeUninit;
118use core::ptr::NonNull;
119
120/// Represents a contiguous piece of a single allocation with some layout that is used as a
121/// data copying destination. May be wholly or partially uninitialized.
122///
123/// This trait is *basically* equivalent to implementing `Deref`/`DerefMut` with
124/// `Target = [MaybeUninit<u8>]` in terms of safety requirements. It is a separate
125/// trait for the extra flexibility having a trait we own provides: namely, the ability
126/// to implement it on foreign types.
127///
128/// # Safety
129///
130/// Implementors of this trait must ensure these guarantees:
131///
132/// - The memory range represented by `base_ptr` and `size` **may** be wholly or partially uninitialized
133/// - `base_ptr` **must** point to a valid, single allocation of at least `size` bytes.
134/// - `size` **must not** be greater than `isize::MAX`
135///
136/// Assume the lifetime of a shared borrow of self is named `'a`:
137///
138/// - `base_ptr` **must** be [valid][`core::ptr#safety`] for `'a`
139/// - `base_ptr` **must *not*** be mutably aliased for `'a`
140/// - It is necessary but not sufficient for this requirement that
141/// **no outside *mutable* references** may exist to its data, even if they are unused by user code.
142///
143/// Assume the lifetime of a mutable borrow of self is named `'a`:
144///
145/// - `base_ptr_mut` **must** be [valid][`core::ptr#safety`] for `'a`
146/// - `base_ptr_mut` **must *not*** be aliased at all for `'a`
147/// - It is necessary but not sufficient for this requirement that
148/// **no outside references** may exist to its data, even if they are unused by user code.
149///
150/// Also see the [crate-level safety documentation][`crate#safety`].
151pub unsafe trait Slab {
152 /// Get a pointer to the beginning of the allocation represented by `self`.
153 fn base_ptr(&self) -> *const u8;
154
155 /// Get a pointer to the beginning of the allocation represented by `self`.
156 fn base_ptr_mut(&mut self) -> *mut u8;
157
158 /// Get the size of the allocation represented by `self`.
159 fn size(&self) -> usize;
160
161 /// Interpret a portion of `self` as a slice of [`MaybeUninit<u8>`]. This is likely not
162 /// incredibly useful, you probably want to use [`Slab::as_maybe_uninit_bytes_mut`]
163 fn as_maybe_uninit_bytes(&self) -> &[MaybeUninit<u8>] {
164 // SAFETY: Safe so long as top level safety guarantees are held, since
165 // `MaybeUninit` has same layout as bare type.
166 unsafe { core::slice::from_raw_parts(self.base_ptr().cast(), self.size()) }
167 }
168
169 /// Interpret a portion of `self` as a mutable slice of [`MaybeUninit<u8>`].
170 fn as_maybe_uninit_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
171 // SAFETY: Safe so long as top level safety guarantees are held, since
172 // `MaybeUninit` has same layout as bare type.
173 unsafe { core::slice::from_raw_parts_mut(self.base_ptr_mut().cast(), self.size()) }
174 }
175
176 /// Interpret `self` as a byte slice. This assumes that **all bytes**
177 /// in `self` are initialized.
178 ///
179 /// # Safety
180 ///
181 /// Assuming that the safety guarantees for creating `self` were followed,
182 /// the only extra requirement for this to be safe is that **all memory**
183 /// within the range of `self` must be **initialized**. If *any bytes* within
184 /// this range are not initialized, using this function is *instantly **undefined
185 /// behavior***, even if you *do noting* with the result.
186 ///
187 /// Also see the [crate-level Safety documentation][`crate#safety`] for more.
188 unsafe fn assume_initialized_as_bytes(&self) -> &[u8] {
189 // SAFETY: same requirements as function-level safety assuming the requirements
190 // for creating `self` are met
191 unsafe { core::slice::from_raw_parts(self.base_ptr().cast(), self.size()) }
192 }
193
194 /// Interpret `self` as a mutable byte slice. This assumes that **all bytes**
195 /// in `self` are initialized.
196 ///
197 /// # Safety
198 ///
199 /// Assuming that the safety guarantees for creating `self` were followed,
200 /// the only extra requirement for this to be safe is that **all memory**
201 /// within the range of `self` must be **initialized**. If *any bytes* within
202 /// this range are not initialized, using this function is *instantly **undefined
203 /// behavior***, even if you *do noting* with the result.
204 ///
205 /// Also see the [crate-level Safety documentation][`crate#safety`] for more.
206 unsafe fn assume_initialized_as_bytes_mut(&mut self) -> &mut [u8] {
207 // SAFETY: same requirements as function-level safety assuming the requirements
208 // for creating `self` are met
209 unsafe { core::slice::from_raw_parts_mut(self.base_ptr_mut().cast(), self.size()) }
210 }
211
212 /// Interpret a range of `self` as a byte slice. This assumes that **all bytes**
213 /// within `range` are initialized.
214 ///
215 /// In the future, this will hopefully not be needed as this operation will be equivalent to
216 /// something like `self.as_maybe_uninit_bytes_mut()[range].assume_init()`, but the `core`/`std`
217 /// implementation for this is still being scaffolded.
218 ///
219 /// # Safety
220 ///
221 /// Assuming that the safety guarantees for creating `self` were followed,
222 /// the only extra requirement for this to be safe is that **all memory**
223 /// within `range` must be **initialized**. If *any bytes* within
224 /// this range are not initialized, using this function is *instantly **undefined
225 /// behavior***, even if you *do noting* with the result.
226 ///
227 /// Also see the [crate-level Safety documentation][`crate#safety`] for more.
228 unsafe fn assume_range_initialized_as_bytes<R>(&self, range: R) -> &[u8]
229 where
230 R: core::slice::SliceIndex<[MaybeUninit<u8>], Output = [MaybeUninit<u8>]>,
231 {
232 let maybe_uninit_slice = &self.as_maybe_uninit_bytes()[range];
233 // SAFETY: same requirements as function-level safety assuming the requirements
234 // for creating `self` are met since `MaybeUnint<T>` has same layout as `T`
235 unsafe {
236 core::slice::from_raw_parts(
237 maybe_uninit_slice.as_ptr().cast(),
238 maybe_uninit_slice.len(),
239 )
240 }
241 }
242
243 /// Interpret a range of `self` as a mutable byte slice. This assumes that **all bytes**
244 /// within `range` are initialized.
245 ///
246 /// In the future, this will hopefully not be needed as this operation will be equivalent to
247 /// something like `self.as_maybe_uninit_bytes_mut()[range].assume_init()`, but the `core`/`std`
248 /// implementation for this is still being scaffolded.
249 ///
250 /// # Safety
251 ///
252 /// Assuming that the safety guarantees for creating `self` were followed,
253 /// the only extra requirement for this to be safe is that **all memory**
254 /// within `range` must be **initialized**. If *any bytes* within
255 /// this range are not initialized, using this function is *instantly **undefined
256 /// behavior***, even if you *do noting* with the result.
257 ///
258 /// Also see the [crate-level Safety documentation][`crate#safety`] for more.
259 unsafe fn assume_range_initialized_as_bytes_mut<R>(&mut self, range: R) -> &mut [u8]
260 where
261 R: core::slice::SliceIndex<[MaybeUninit<u8>], Output = [MaybeUninit<u8>]>,
262 {
263 let maybe_uninit_slice = &mut self.as_maybe_uninit_bytes_mut()[range];
264 // SAFETY: same requirements as function-level safety assuming the requirements
265 // for creating `self` are met since `MaybeUnint<T>` has same layout as `T`
266 unsafe {
267 core::slice::from_raw_parts_mut(
268 maybe_uninit_slice.as_mut_ptr().cast(),
269 maybe_uninit_slice.len(),
270 )
271 }
272 }
273}
274
275// SAFETY: The captured `[MaybeUninit<u8>]` will all be part of the same allocation object, and borrowck
276// will assure that the borrows that occur on `self` on the relevant methods live long enough since they are
277// native borrows anyway.
278unsafe impl Slab for [MaybeUninit<u8>] {
279 fn base_ptr(&self) -> *const u8 {
280 self.as_ptr().cast()
281 }
282
283 fn base_ptr_mut(&mut self) -> *mut u8 {
284 self.as_mut_ptr().cast()
285 }
286
287 fn size(&self) -> usize {
288 core::mem::size_of_val(self)
289 }
290}
291
292/// Takes a `Vec` and unsafely resizes it to the given length, returning a mutable slice to `MaybeUninit<T>` for each
293/// item in the newly-resized `Vec`.
294///
295/// # Safety
296///
297/// You promise that the given `Vec` already has at least `length` capacity. You also promise to either fill all items before dropping
298/// the returned slice, or to continue to not violate validity rules for any items that you do not initialize.
299#[cfg(feature = "std")]
300pub unsafe fn maybe_uninit_slice_from_vec<T>(
301 vec: &mut Vec<T>,
302 length: usize,
303) -> &mut [MaybeUninit<T>] {
304 // SAFETY: As long as the function-level safety rules are met, this is valid
305 unsafe {
306 #[allow(clippy::uninit_vec)]
307 vec.set_len(length);
308 }
309
310 // SAFETY: If function-level safety is met, then we are constructing a slice within a single allocation. `MaybeUninit<T>` is valid
311 // even for uninit memory, and has the same memory layout as `T`.
312 unsafe { core::slice::from_raw_parts_mut(vec.as_mut_ptr().cast::<MaybeUninit<T>>(), length) }
313}
314
315/// Copies the elements from `src` to `dst`, returning a mutable reference to the now initialized contents of `dst`.
316///
317/// If `T` does not implement `Copy`, use [`clone_into_maybe_uninit_slice`]
318///
319/// This is similar to [`slice::copy_from_slice`]. This is identical to the implementation of the method
320/// `write_to_slice` on [`MaybeUninit`], but that API is as yet unstable.
321///
322/// # Panics
323///
324/// This function will panic if the two slices have different lengths.
325pub fn copy_into_maybe_uninit_slice<'a, T>(src: &[T], dst: &'a mut [MaybeUninit<T>]) -> &'a mut [T]
326where
327 T: Copy,
328{
329 let uninit_src: &[MaybeUninit<T>] =
330 // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout
331 unsafe { &*(src as *const [T] as *const [MaybeUninit<T>]) };
332
333 dst.copy_from_slice(uninit_src);
334
335 // SAFETY: Valid elements have just been copied into `this` so it is initialized
336 unsafe { &mut *(dst as *mut [MaybeUninit<T>] as *mut [T]) }
337}
338
339/// Clones the elements from `src` to `dst`, returning a mutable reference to the now initialized contents of `dst`.
340/// Any already initialized elements will not be dropped.
341///
342/// If `T` implements `Copy`, use [`copy_into_maybe_uninit_slice`]
343///
344/// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. This is identical to the implementation of
345/// the method `write_to_slice_cloned` on [`MaybeUninit`], but that API is as yet unstable.
346///
347/// # Panics
348///
349/// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics.
350///
351/// If there is a panic, the already cloned elements will be dropped.
352pub fn clone_into_maybe_uninit_slice<'a, T>(src: &[T], dst: &'a mut [MaybeUninit<T>]) -> &'a mut [T]
353where
354 T: Clone,
355{
356 // unlike copy_from_slice this does not call clone_from_slice on the slice
357 // this is because `MaybeUninit<T: Clone>` does not implement Clone.
358
359 struct Guard<'a, T> {
360 slice: &'a mut [MaybeUninit<T>],
361 initialized: usize,
362 }
363
364 impl<'a, T> Drop for Guard<'a, T> {
365 fn drop(&mut self) {
366 let initialized_part = &mut self.slice[..self.initialized];
367 // SAFETY: this raw slice will contain only initialized objects
368 // that's why, it is allowed to drop it.
369 unsafe {
370 core::ptr::drop_in_place(
371 &mut *(initialized_part as *mut [MaybeUninit<T>] as *mut [T]),
372 );
373 }
374 }
375 }
376
377 assert_eq!(
378 dst.len(),
379 src.len(),
380 "destination and source slices have different lengths"
381 );
382 // NOTE: We need to explicitly slice them to the same length
383 // for bounds checking to be elided, and the optimizer will
384 // generate memcpy for simple cases (for example T = u8).
385 let len = dst.len();
386 let src = &src[..len];
387
388 // guard is needed b/c panic might happen during a clone
389 let mut guard = Guard {
390 slice: dst,
391 initialized: 0,
392 };
393
394 #[allow(clippy::needless_range_loop)]
395 for i in 0..len {
396 guard.slice[i].write(src[i].clone());
397 guard.initialized += 1;
398 }
399
400 #[allow(clippy::mem_forget)]
401 core::mem::forget(guard);
402
403 // SAFETY: Valid elements have just been written into `this` so it is initialized
404 unsafe { &mut *(dst as *mut [MaybeUninit<T>] as *mut [T]) }
405}
406
407/// Represents a contiguous piece of a single allocation with some layout.
408/// May be wholly or partially uninitialized.
409///
410/// This exists as a convenient way to get access to a type implementing [`Slab`]
411/// when dealing with your own raw allocations/buffers if you don't want to or
412/// cannot implement [`Slab`] for another native type.
413pub struct RawAllocation {
414 /// A pointer to the base address of the allocation
415 pub base_ptr: NonNull<u8>,
416
417 /// The size of the allocation in bytes
418 pub size: usize,
419}
420
421impl RawAllocation {
422 /// Create a new [`RawAllocation`] from a pointer and size.
423 ///
424 /// # Safety
425 ///
426 /// This function is safe in and of itself, as nothing will be done
427 /// with the pointer and size upon creation.
428 pub fn from_raw_parts(base_ptr: NonNull<u8>, size: usize) -> Self {
429 Self { base_ptr, size }
430 }
431
432 /// Asserts that we are uniquely borrowing the memory range represented by `self` for
433 /// the duration of the borrow, giving us a [`BorrowedRawAllocation`] which implements [`Slab`].
434 ///
435 /// # Safety
436 ///
437 /// Using this method makes some strong guarantees about the contained `base_ptr` and `size`
438 /// for the duration of the borrow. See the [safety][`Slab#safety`] documentation for the
439 /// [`Slab`] trait for a list of the guarantees you must make to use this method.
440 ///
441 /// Also see the [top-level safety documentation][`crate#safety`]
442 #[allow(clippy::needless_lifetimes)] // Important to be explicit in this case because of unsafety
443 pub unsafe fn borrow_as_slab<'a>(&'a mut self) -> BorrowedRawAllocation<'a> {
444 BorrowedRawAllocation {
445 base_ptr: self.base_ptr,
446 size: self.size,
447 phantom: PhantomData,
448 }
449 }
450}
451
452/// Represents the unique borrow of a contiguous piece of a single allocation with some layout that is used as a
453/// data copying destination. May be wholly or partially uninitialized.
454///
455/// This type can only be obtained through the [`borrow_as_slab`][`RawAllocation::borrow_as_slab`] method on [`RawAllocation`].
456pub struct BorrowedRawAllocation<'a> {
457 base_ptr: NonNull<u8>,
458 size: usize,
459 phantom: PhantomData<&'a ()>,
460}
461
462// SAFETY: So long as the safety requirements of `borrow_as_slab` are met, this is also safe
463// since it's just a basic pass-thru of info.
464unsafe impl<'a> Slab for BorrowedRawAllocation<'a> {
465 fn base_ptr(&self) -> *const u8 {
466 self.base_ptr.as_ptr() as *const u8
467 }
468
469 fn base_ptr_mut(&mut self) -> *mut u8 {
470 self.base_ptr.as_ptr()
471 }
472
473 fn size(&self) -> usize {
474 self.size
475 }
476}
477
478/// Given pointer and offset, returns a new offset aligned to `align`.
479///
480/// `align` *must* be a power of two and >= 1 or else the result is meaningless.
481fn align_offset_up_to(ptr: usize, offset: usize, align: usize) -> Option<usize> {
482 let offsetted_ptr = ptr.checked_add(offset)?;
483 let aligned_ptr = offsetted_ptr.checked_add(align - 1)? & !(align - 1);
484 // don't need to check since we know aligned_ptr is >= ptr at this point
485 Some(aligned_ptr - ptr)
486}
487
488/// Compute and validate offsets for a copy operation with the given parameters.
489fn compute_offsets<S: Slab>(
490 dst: &S,
491 start_offset: usize,
492 t_layout: Layout,
493 min_alignment: usize,
494) -> Result<CopyRecord, CopyError> {
495 let copy_layout = t_layout.align_to(min_alignment.next_power_of_two())?;
496
497 let copy_start_offset =
498 align_offset_up_to(dst.base_ptr() as usize, start_offset, copy_layout.align())
499 .ok_or(CopyError::InvalidLayout)?;
500 let copy_end_offset = copy_start_offset
501 .checked_add(copy_layout.size())
502 .ok_or(CopyError::InvalidLayout)?;
503 let copy_end_offset_padded = copy_start_offset
504 .checked_add(copy_layout.pad_to_align().size())
505 .ok_or(CopyError::InvalidLayout)?;
506
507 // check start is inside slab
508 // if within slab, we also know that copy_start_offset is <= isize::MAX since slab.size() must be <= isize::MAX
509 if copy_start_offset > dst.size() {
510 return Err(CopyError::OffsetOutOfBounds);
511 }
512
513 // check end is inside slab
514 if copy_end_offset_padded > dst.size() {
515 return Err(CopyError::OutOfMemory);
516 }
517
518 Ok(CopyRecord {
519 copy_start_offset,
520 copy_end_offset,
521 copy_end_offset_padded,
522 })
523}
524
525/// An error that may occur during a copy operation.
526#[derive(Debug)]
527pub enum CopyError {
528 /// Copy would exceed the end of the allocation
529 OutOfMemory,
530 /// Requested to copy to an offset outside the bounds of the allocation
531 OffsetOutOfBounds,
532 /// Computed invalid layout for copy operation, probably caused by incredibly large size, offset, or min-alignment parameters
533 InvalidLayout,
534}
535
536impl core::fmt::Display for CopyError {
537 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
538 write!(f, "{}", match self {
539 Self::OutOfMemory => "Copy would exceed the end of the allocation",
540 Self::OffsetOutOfBounds => "Requested copy to a location starting outside the allocation",
541 Self::InvalidLayout => "Invalid layout, probably caused by incredibly large size, offset, or alignment parameters",
542 })
543 }
544}
545
546#[cfg(feature = "std")]
547impl std::error::Error for CopyError {}
548
549impl From<LayoutError> for CopyError {
550 fn from(_err: LayoutError) -> Self {
551 Self::InvalidLayout
552 }
553}
554
555/// Record of the results of a copy operation
556#[derive(Debug, Copy, Clone)]
557pub struct CopyRecord {
558 /// The offset from the start of the allocation, in bytes, at which the
559 /// copy operation began to write data.
560 ///
561 /// Not necessarily equal to the `start_offset`, since this offset
562 /// includes necessary padding to assure alignment.
563 pub copy_start_offset: usize,
564
565 /// The offset from the start of the allocation, in bytes, at which the
566 /// copy operation no longer wrote data.
567 ///
568 /// This does not include any padding at the end necessary to maintain
569 /// alignment requirements.
570 pub copy_end_offset: usize,
571
572 /// The offset from the start of the allocation, in bytes, at which the
573 /// copy operation no longer wrote data, plus any padding necessary to
574 /// maintain derived alignment requirements.
575 pub copy_end_offset_padded: usize,
576}
577
578/// Copies `src` into the memory represented by `dst` starting at a minimum location
579/// of `start_offset` bytes past the start of `dst`.
580///
581/// - `start_offset` is the offset into the allocation represented by `dst`,
582/// in bytes, before which any copied data will *certainly not* be placed. However,
583/// the actual beginning of the copied data may not be exactly at `start_offset` if
584/// padding bytes are needed to satisfy alignment requirements. The actual beginning
585/// of the copied bytes is contained in the returned [`CopyRecord`].
586///
587/// # Safety
588///
589/// This function is safe on its own, however it is very possible to do unsafe
590/// things if you read the copied data in the wrong way. See the
591/// [crate-level Safety documentation][`crate#safety`] for more.
592#[inline]
593pub fn copy_to_offset<T: Copy, S: Slab>(
594 src: &T,
595 dst: &mut S,
596 start_offset: usize,
597) -> Result<CopyRecord, CopyError> {
598 copy_to_offset_with_align(src, dst, start_offset, 1)
599}
600
601/// Copies `src` into the memory represented by `dst` starting at a minimum location
602/// of `start_offset` bytes past the start of `dst` and with minimum alignment
603/// `min_alignment`.
604///
605/// - `start_offset` is the offset into the allocation represented by `dst`,
606/// in bytes, before which any copied data will *certainly not* be placed. However,
607/// the actual beginning of the copied data may not be exactly at `start_offset` if
608/// padding bytes are needed to satisfy alignment requirements. The actual beginning
609/// of the copied bytes is contained in the returned [`CopyRecord`].
610/// - `min_alignment` is the minimum alignment to which the copy will be aligned. The
611/// copy may not actually be aligned to `min_alignment` depending on the alignment requirements
612/// of `T`.
613///
614/// # Safety
615///
616/// This function is safe on its own, however it is very possible to do unsafe
617/// things if you read the copied data in the wrong way. See the
618/// [crate-level Safety documentation][`crate#safety`] for more.
619pub fn copy_to_offset_with_align<T: Copy, S: Slab>(
620 src: &T,
621 dst: &mut S,
622 start_offset: usize,
623 min_alignment: usize,
624) -> Result<CopyRecord, CopyError> {
625 let t_layout = Layout::new::<T>();
626 let record = compute_offsets(&*dst, start_offset, t_layout, min_alignment)?;
627
628 // SAFETY: if compute_offsets succeeded, this has already been checked to be safe.
629 let dst_ptr = unsafe { dst.base_ptr_mut().add(record.copy_start_offset) }.cast::<T>();
630
631 // SAFETY:
632 // - src is valid as we have a reference to it
633 // - dst is valid so long as requirements for `slab` were met, i.e.
634 // we have unique access to the region described and that it is valid for the duration
635 // of 'a.
636 // - areas not overlapping as long as safety requirements of creation of `self` were met,
637 // i.e. that we have exclusive access to the region of memory described.
638 // - dst aligned at least to align_of::<T>()
639 // - checked that copy stays within bounds of our allocation
640 unsafe {
641 core::ptr::copy_nonoverlapping(src as *const T, dst_ptr, 1);
642 }
643
644 Ok(record)
645}
646
647/// Copies from `slice` into the memory represented by `dst` starting at a minimum location
648/// of `start_offset` bytes past the start of `self`.
649///
650/// - `start_offset` is the offset into the allocation represented by `dst`,
651/// in bytes, before which any copied data will *certainly not* be placed. However,
652/// the actual beginning of the copied data may not be exactly at `start_offset` if
653/// padding bytes are needed to satisfy alignment requirements. The actual beginning
654/// of the copied bytes is contained in the returned [`CopyRecord`].
655///
656/// # Safety
657///
658/// This function is safe on its own, however it is very possible to do unsafe
659/// things if you read the copied data in the wrong way. See the
660/// [crate-level Safety documentation][`crate#safety`] for more.
661#[inline]
662pub fn copy_from_slice_to_offset<T: Copy, S: Slab>(
663 src: &[T],
664 dst: &mut S,
665 start_offset: usize,
666) -> Result<CopyRecord, CopyError> {
667 copy_from_slice_to_offset_with_align(src, dst, start_offset, 1)
668}
669
670/// Copies from `slice` into the memory represented by `dst` starting at a minimum location
671/// of `start_offset` bytes past the start of `dst`.
672///
673/// - `start_offset` is the offset into the allocation represented by `dst`,
674/// in bytes, before which any copied data will *certainly not* be placed. However,
675/// the actual beginning of the copied data may not be exactly at `start_offset` if
676/// padding bytes are needed to satisfy alignment requirements. The actual beginning
677/// of the copied bytes is contained in the returned [`CopyRecord`].
678/// - `min_alignment` is the minimum alignment to which the copy will be aligned. The
679/// copy may not actually be aligned to `min_alignment` depending on the alignment requirements
680/// of `T` and the underlying allocation.
681/// - The whole data of the slice will be copied directly, so, alignment between elements
682/// ignores `min_alignment`.
683///
684/// # Safety
685///
686/// This function is safe on its own, however it is very possible to do unsafe
687/// things if you read the copied data in the wrong way. See the
688/// [crate-level Safety documentation][`crate#safety`] for more.
689pub fn copy_from_slice_to_offset_with_align<T: Copy, S: Slab>(
690 src: &[T],
691 dst: &mut S,
692 start_offset: usize,
693 min_alignment: usize,
694) -> Result<CopyRecord, CopyError> {
695 let t_layout = Layout::for_value(src);
696 let record = compute_offsets(&*dst, start_offset, t_layout, min_alignment)?;
697
698 // SAFETY: if compute_offsets succeeded, this has already been checked to be safe.
699 let dst_ptr = unsafe { dst.base_ptr_mut().add(record.copy_start_offset) }.cast::<T>();
700
701 // SAFETY:
702 // - src is valid as we have a reference to it
703 // - dst is valid so long as requirements for `slab` were met, i.e.
704 // we have unique access to the region described and that it is valid for the duration
705 // of 'a.
706 // - areas not overlapping as long as safety requirements of creation of `self` were met,
707 // i.e. that we have exclusive access to the region of memory described.
708 // - dst aligned at least to align_of::<T>()
709 // - checked that copy stays within bounds of our allocation
710 unsafe {
711 core::ptr::copy_nonoverlapping(src.as_ptr(), dst_ptr, src.len());
712 }
713
714 Ok(record)
715}
716
717/// Copies from `src` iterator into the memory represented by `dst` starting at a minimum location
718/// of `start_offset` bytes past the start of `dst`.
719///
720/// Returns a vector of [`CopyRecord`]s, one for each item in the `src` iterator.
721///
722/// - `start_offset` is the offset into the allocation represented by `dst`,
723/// in bytes, before which any copied data will *certainly not* be placed. However,
724/// the actual beginning of the copied data may not be exactly at `start_offset` if
725/// padding bytes are needed to satisfy alignment requirements. The actual beginning
726/// of the copied bytes is contained in the returned [`CopyRecord`]s.
727/// - `min_alignment` is the minimum alignment to which the copy will be aligned. The
728/// copy may not actually be aligned to `min_alignment` depending on the alignment requirements
729/// of `T`.
730/// - For this variation, `min_alignment` will also be respected *between* elements yielded by
731/// the iterator. To copy inner elements aligned only to `align_of::<T>()`, see
732/// [`copy_from_iter_to_offset_with_align_packed`]
733///
734/// # Safety
735///
736/// This function is safe on its own, however it is very possible to do unsafe
737/// things if you read the copied data in the wrong way. See the
738/// [crate-level Safety documentation][`crate#safety`] for more.
739#[cfg(feature = "std")]
740pub fn copy_from_iter_to_offset_with_align<T: Copy, Iter: Iterator<Item = T>, S: Slab>(
741 src: Iter,
742 dst: &mut S,
743 start_offset: usize,
744 min_alignment: usize,
745) -> Result<Vec<CopyRecord>, CopyError> {
746 let mut offset = start_offset;
747
748 src.map(|item| {
749 let copy_record = copy_to_offset_with_align(&item, dst, offset, min_alignment)?;
750 offset = copy_record.copy_end_offset;
751 Ok(copy_record)
752 })
753 .collect::<Result<Vec<_>, _>>()
754}
755
756/// Like [`copy_from_iter_to_offset_with_align`] except that
757/// alignment between elements yielded by the iterator will ignore `min_alignment`
758/// and rather only be aligned to the alignment of `T`.
759///
760/// Because of this, only one [`CopyRecord`] is returned specifying the record of the
761/// entire block of copied data. If the `src` iterator is empty, returns `None`.
762pub fn copy_from_iter_to_offset_with_align_packed<T: Copy, Iter: Iterator<Item = T>, S: Slab>(
763 mut src: Iter,
764 dst: &mut S,
765 start_offset: usize,
766 min_alignment: usize,
767) -> Result<Option<CopyRecord>, CopyError> {
768 let first_record = if let Some(first_item) = src.next() {
769 copy_to_offset_with_align(&first_item, dst, start_offset, min_alignment)?
770 } else {
771 return Ok(None);
772 };
773
774 let mut prev_record = first_record;
775
776 for item in src {
777 let copy_record = copy_to_offset_with_align(&item, dst, prev_record.copy_end_offset, 1)?;
778 prev_record = copy_record;
779 }
780
781 Ok(Some(CopyRecord {
782 copy_start_offset: first_record.copy_start_offset,
783 copy_end_offset: prev_record.copy_end_offset,
784 copy_end_offset_padded: prev_record.copy_end_offset_padded,
785 }))
786}