Skip to main content

rkyv/
place.rs

1//! An initialized, writeable location in memory.
2
3use core::{mem::size_of, ptr::NonNull};
4
5use munge::{Borrow, Destructure, Restructure};
6
7use crate::traits::{LayoutRaw, NoUndef};
8
9/// A place to write a `T` paired with its position in the output buffer.
10pub struct Place<T: ?Sized> {
11    pos: usize,
12    ptr: NonNull<T>,
13}
14
15impl<T: ?Sized> Clone for Place<T> {
16    fn clone(&self) -> Self {
17        *self
18    }
19}
20
21impl<T: ?Sized> Copy for Place<T> {}
22
23impl<T: ?Sized> Place<T> {
24    /// Creates a new `Place` from an output pointer.
25    ///
26    /// # Safety
27    ///
28    /// `ptr` must be properly aligned, dereferenceable, and all of its bytes
29    /// must be initialized.
30    pub unsafe fn new_unchecked(pos: usize, ptr: *mut T) -> Self {
31        unsafe {
32            Self {
33                pos,
34                ptr: NonNull::new_unchecked(ptr),
35            }
36        }
37    }
38
39    /// Creates a new `Place` from a parent pointer and the field the place
40    /// points to.
41    ///
42    /// # Safety
43    ///
44    /// - `ptr` must point to a field of `parent`
45    /// - `ptr` must be properly aligned, dereferenceable, and all of its bytes
46    ///   must be initialized
47    pub unsafe fn from_field_unchecked<U: ?Sized>(
48        parent: Place<U>,
49        ptr: *mut T,
50    ) -> Self {
51        // SAFETY: We won't write anything to the parent pointer, so we
52        // definitely won't write any uninitialized bytes.
53        let parent_ptr = unsafe { parent.ptr() };
54        let offset = ptr as *mut () as usize - parent_ptr as *mut () as usize;
55        // SAFETY: The caller has guaranteed that `ptr` is properly aligned,
56        // dereferenceable, and all of its bytes are initialized.
57        unsafe { Self::new_unchecked(parent.pos() + offset, ptr) }
58    }
59
60    /// Returns the position of the place.
61    pub fn pos(&self) -> usize {
62        self.pos
63    }
64
65    /// Returns the pointer associated with this place.
66    ///
67    /// # Safety
68    ///
69    /// Uninitialized bytes must not be written to the returned pointer.
70    pub unsafe fn ptr(&self) -> *mut T {
71        self.ptr.as_ptr()
72    }
73
74    /// Writes the provided value to this place.
75    ///
76    /// # Safety
77    ///
78    /// `value` must not have any uninitialized bytes (e.g. padding).
79    pub unsafe fn write_unchecked(&self, value: T)
80    where
81        T: Sized,
82    {
83        unsafe {
84            self.ptr().write(value);
85        }
86    }
87
88    /// Writes the provided value to this place.
89    pub fn write(&self, value: T)
90    where
91        T: NoUndef + Sized,
92    {
93        unsafe {
94            self.write_unchecked(value);
95        }
96    }
97
98    /// Returns this place casted to the given type.
99    ///
100    /// # Safety
101    ///
102    /// This place must point to a valid `U`.
103    pub unsafe fn cast_unchecked<U>(&self) -> Place<U>
104    where
105        T: Sized,
106    {
107        Place {
108            pos: self.pos,
109            ptr: self.ptr.cast(),
110        }
111    }
112
113    /// Returns a slice of the bytes this place points to.
114    pub fn as_slice(&self) -> &[u8]
115    where
116        T: LayoutRaw,
117    {
118        let ptr = self.ptr.as_ptr();
119        let len = T::layout_raw(ptr_meta::metadata(ptr)).unwrap().size();
120        // SAFETY: The pointers of places are always properly aligned and
121        // dereferenceable. All of the bytes this place points to are guaranteed
122        // to be initialized at all times.
123        unsafe { core::slice::from_raw_parts(ptr.cast::<u8>(), len) }
124    }
125}
126
127impl<T> Place<[T]> {
128    /// Gets a `Place` to the `i`-th element of the slice.
129    ///
130    /// # Safety
131    ///
132    /// `i` must be in-bounds for the slice pointed to by this place.
133    pub unsafe fn index(&self, i: usize) -> Place<T> {
134        // SAFETY: The caller has guaranteed that `i` is in-bounds for the slice
135        // pointed to by this place.
136        let ptr = unsafe { self.ptr().cast::<T>().add(i) };
137        // SAFETY: `ptr` is an element of `self`, and so is also properly
138        // aligned, dereferenceable, and all of its bytes are initialized.
139        unsafe { Place::new_unchecked(self.pos() + i * size_of::<T>(), ptr) }
140    }
141}
142
143impl<T, const N: usize> Place<[T; N]> {
144    /// Gets a `Place` to the `i`-th element of the array.
145    ///
146    /// # Safety
147    ///
148    /// `i` must be in-bounds for the array pointed to by this place.
149    pub unsafe fn index(&self, i: usize) -> Place<T> {
150        // SAFETY: The caller has guaranteed that `i` is in-bounds for the array
151        // pointed to by this place.
152        let ptr = unsafe { self.ptr().cast::<T>().add(i) };
153        // SAFETY: `ptr` is an element of `self`, and so is also properly
154        // aligned, dereferenceable, and all of its bytes are initialized.
155        unsafe { Place::new_unchecked(self.pos() + i * size_of::<T>(), ptr) }
156    }
157}
158
159unsafe impl<T: ?Sized> Destructure for Place<T> {
160    type Underlying = T;
161    type Destructuring = Borrow;
162
163    fn underlying(&mut self) -> *mut Self::Underlying {
164        self.ptr.as_ptr()
165    }
166}
167
168unsafe impl<T: ?Sized, U: ?Sized> Restructure<U> for Place<T> {
169    type Restructured = Place<U>;
170
171    unsafe fn restructure(&self, ptr: *mut U) -> Self::Restructured {
172        // SAFETY: `ptr` is a pointer to a subfield of the underlying pointer,
173        // and so is also properly aligned, dereferenceable, and all of its
174        // bytes are initialized.
175        unsafe { Place::from_field_unchecked(*self, ptr) }
176    }
177}