rkyv_test/rel_ptr/
mod.rs

1//! Relative pointer implementations and options.
2
3#[cfg(feature = "validation")]
4mod validation;
5
6use crate::{ArchivePointee, ArchiveUnsized, Archived};
7use core::{
8    convert::TryFrom,
9    fmt,
10    marker::{PhantomData, PhantomPinned},
11    ptr,
12};
13
14/// An error where the distance between two positions cannot be represented by the offset type.
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum OffsetError {
17    /// The offset overflowed the range of `isize`
18    IsizeOverflow,
19    /// The offset is too far for the offset type of the relative pointer
20    ExceedsStorageRange,
21}
22
23impl fmt::Display for OffsetError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            OffsetError::IsizeOverflow => write!(f, "the offset overflowed the range of `isize`"),
27            OffsetError::ExceedsStorageRange => write!(
28                f,
29                "the offset is too far for the offset type of the relative pointer"
30            ),
31        }
32    }
33}
34
35#[cfg(feature = "std")]
36impl std::error::Error for OffsetError {}
37
38/// Calculates the offset between two positions as an `isize`.
39///
40/// This function exists solely to get the distance between two `usizes` as an `isize` with a full
41/// range of values.
42///
43/// # Examples
44///
45/// ```
46/// use rkyv::rel_ptr::{signed_offset, OffsetError};
47///
48/// assert_eq!(signed_offset(0, 1), Ok(1));
49/// assert_eq!(signed_offset(1, 0), Ok(-1));
50/// assert_eq!(signed_offset(0, isize::MAX as usize), Ok(isize::MAX));
51/// assert_eq!(signed_offset(isize::MAX as usize, 0), Ok(-isize::MAX));
52/// assert_eq!(signed_offset(0, isize::MAX as usize + 1), Err(OffsetError::IsizeOverflow));
53/// assert_eq!(signed_offset(isize::MAX as usize + 1, 0), Ok(isize::MIN));
54/// assert_eq!(signed_offset(0, isize::MAX as usize + 2), Err(OffsetError::IsizeOverflow));
55/// assert_eq!(signed_offset(isize::MAX as usize + 2, 0), Err(OffsetError::IsizeOverflow));
56/// ```
57#[inline]
58pub fn signed_offset(from: usize, to: usize) -> Result<isize, OffsetError> {
59    let (result, overflow) = to.overflowing_sub(from);
60    if (!overflow && result <= (isize::MAX as usize))
61        || (overflow && result >= (isize::MIN as usize))
62    {
63        Ok(result as isize)
64    } else {
65        Err(OffsetError::IsizeOverflow)
66    }
67}
68
69/// A offset that can be used with [`RawRelPtr`].
70pub trait Offset: Copy {
71    /// Creates a new offset between a `from` position and a `to` position.
72    fn between(from: usize, to: usize) -> Result<Self, OffsetError>;
73
74    /// Gets the offset as an `isize`.
75    fn to_isize(&self) -> isize;
76}
77
78macro_rules! impl_offset {
79    ($ty:ty) => {
80        impl Offset for $ty {
81            #[inline]
82            fn between(from: usize, to: usize) -> Result<Self, OffsetError> {
83                // pointer::add and pointer::offset require that the computed offsets cannot
84                // overflow an isize, which is why we're using signed_offset instead of checked_sub
85                // for unsized types
86                Self::try_from(signed_offset(from, to)?)
87                    .map_err(|_| OffsetError::ExceedsStorageRange)
88            }
89
90            #[inline]
91            fn to_isize(&self) -> isize {
92                // We're guaranteed that our offset will not exceed the the capacity of an `isize`
93                *self as isize
94            }
95        }
96    };
97    (@endian $ty:ty) => {
98        impl Offset for Archived<$ty> {
99            #[inline]
100            fn between(from: usize, to: usize) -> Result<Self, OffsetError> {
101                // pointer::add and pointer::offset require that the computed offsets cannot
102                // overflow an isize, which is why we're using signed_offset instead of checked_sub
103                // for unsized types
104                <$ty>::try_from(signed_offset(from, to)?)
105                    .map(|x| to_archived!(x))
106                    .map_err(|_| OffsetError::ExceedsStorageRange)
107            }
108
109            #[inline]
110            fn to_isize(&self) -> isize {
111                // We're guaranteed that our offset will not exceed the the capacity of an `isize`
112                from_archived!(*self) as isize
113            }
114        }
115    };
116}
117
118impl_offset!(i8);
119impl_offset!(@endian i16);
120#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
121impl_offset!(@endian i32);
122#[cfg(target_pointer_width = "64")]
123impl_offset!(@endian i64);
124impl_offset!(u8);
125impl_offset!(@endian u16);
126#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
127impl_offset!(@endian u32);
128#[cfg(target_pointer_width = "64")]
129impl_offset!(@endian u64);
130
131/// Errors that can occur while creating raw relative pointers.
132#[derive(Debug)]
133pub enum RelPtrError {
134    /// The given `from` and `to` positions for the relative pointer failed to form a valid offset.
135    ///
136    /// This is probably because the distance between them could not be represented by the offset
137    /// type.
138    OffsetError,
139}
140
141/// An untyped pointer which resolves relative to its position in memory.
142///
143/// This is the most fundamental building block in rkyv. It allows the construction and use of
144/// pointers that can be safely relocated as long as the source and target are moved together. This
145/// is what allows memory to be moved from disk into memory and accessed without decoding.
146///
147/// Regular pointers are *absolute*, meaning that the pointee can be moved without invalidating the
148/// pointer. However, the target cannot be moved or the pointer is invalidated.
149///
150/// Relative pointers are *relative*, meaning that the pointee can be moved with the target without
151/// invalidating the pointer. However, if either the pointee or the target move independently, the
152/// pointer will be invalidated.
153#[repr(transparent)]
154pub struct RawRelPtr<O> {
155    offset: O,
156    _phantom: PhantomPinned,
157}
158
159impl<O: Offset> RawRelPtr<O> {
160    /// Attempts to create a new `RawRelPtr` in-place between the given `from` and `to` positions.
161    ///
162    /// # Safety
163    ///
164    /// - `out` must be located at position `from`
165    /// - `to` must be a position within the archive
166    #[inline]
167    pub unsafe fn try_emplace(from: usize, to: usize, out: *mut Self) -> Result<(), OffsetError> {
168        let offset = O::between(from, to)?;
169        ptr::addr_of_mut!((*out).offset).write(offset);
170        Ok(())
171    }
172
173    /// Creates a new `RawRelPtr` in-place between the given `from` and `to` positions.
174    ///
175    /// # Safety
176    ///
177    /// - `out` must be located at position `from`
178    /// - `to` must be a position within the archive
179    /// - The offset between `from` and `to` must fit in an `isize` and not exceed the offset
180    ///   storage
181    #[inline]
182    pub unsafe fn emplace(from: usize, to: usize, out: *mut Self) {
183        Self::try_emplace(from, to, out).unwrap();
184    }
185
186    /// Gets the base pointer for the relative pointer.
187    #[inline]
188    pub fn base(&self) -> *const u8 {
189        (self as *const Self).cast::<u8>()
190    }
191
192    /// Gets the mutable base pointer for the relative pointer.
193    #[inline]
194    pub fn base_mut(&mut self) -> *mut u8 {
195        (self as *mut Self).cast::<u8>()
196    }
197
198    /// Gets the offset of the relative pointer from its base.
199    #[inline]
200    pub fn offset(&self) -> isize {
201        self.offset.to_isize()
202    }
203
204    /// Gets whether the offset of the relative pointer is 0.
205    #[inline]
206    pub fn is_null(&self) -> bool {
207        self.offset() == 0
208    }
209
210    /// Calculates the memory address being pointed to by this relative pointer.
211    #[inline]
212    pub fn as_ptr(&self) -> *const () {
213        unsafe { self.base().offset(self.offset()).cast() }
214    }
215
216    /// Returns an unsafe mutable pointer to the memory address being pointed to
217    /// by this relative pointer.
218    #[inline]
219    pub fn as_mut_ptr(&mut self) -> *mut () {
220        unsafe { self.base_mut().offset(self.offset()).cast() }
221    }
222}
223
224impl<O: fmt::Debug> fmt::Debug for RawRelPtr<O> {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        f.debug_struct("RawRelPtr")
227            .field("offset", &self.offset)
228            .finish()
229    }
230}
231
232impl<O: Offset> fmt::Pointer for RawRelPtr<O> {
233    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234        fmt::Pointer::fmt(&self.as_ptr(), f)
235    }
236}
237
238/// A raw relative pointer that uses an archived `i8` as the underlying offset.
239pub type RawRelPtrI8 = RawRelPtr<Archived<i8>>;
240/// A raw relative pointer that uses an archived `i16` as the underlying offset.
241pub type RawRelPtrI16 = RawRelPtr<Archived<i16>>;
242/// A raw relative pointer that uses an archived `i32` as the underlying offset.
243#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
244pub type RawRelPtrI32 = RawRelPtr<Archived<i32>>;
245/// A raw relative pointer that uses an archived `i64` as the underlying offset.
246#[cfg(target_pointer_width = "64")]
247pub type RawRelPtrI64 = RawRelPtr<Archived<i64>>;
248
249/// A raw relative pointer that uses an archived `u8` as the underlying offset.
250pub type RawRelPtrU8 = RawRelPtr<Archived<u8>>;
251/// A raw relative pointer that uses an archived `u16` as the underlying offset.
252pub type RawRelPtrU16 = RawRelPtr<Archived<u16>>;
253/// A raw relative pointer that uses an archived `u32` as the underlying offset.
254#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
255pub type RawRelPtrU32 = RawRelPtr<Archived<u32>>;
256/// A raw relative pointer that uses an archived `u64` as the underlying offset.
257#[cfg(target_pointer_width = "64")]
258pub type RawRelPtrU64 = RawRelPtr<Archived<u64>>;
259
260// TODO: implement for NonZero types
261
262/// A pointer which resolves to relative to its position in memory.
263///
264/// This is a strongly-typed version of [`RawRelPtr`].
265///
266/// See [`Archive`](crate::Archive) for an example of creating one.
267pub struct RelPtr<T: ArchivePointee + ?Sized, O> {
268    raw_ptr: RawRelPtr<O>,
269    metadata: T::ArchivedMetadata,
270    _phantom: PhantomData<T>,
271}
272
273impl<T, O: Offset> RelPtr<T, O> {
274    /// Attempts to create a relative pointer from one position to another.
275    ///
276    /// # Safety
277    ///
278    /// - `from` must be the position of `out` within the archive
279    /// - `to` must be the position of some valid `T`
280    #[inline]
281    pub unsafe fn try_emplace(from: usize, to: usize, out: *mut Self) -> Result<(), OffsetError> {
282        let (fp, fo) = out_field!(out.raw_ptr);
283        // Skip metadata since sized T is guaranteed to be ()
284        RawRelPtr::try_emplace(from + fp, to, fo)
285    }
286
287    /// Creates a relative pointer from one position to another.
288    ///
289    /// # Panics
290    ///
291    /// - The offset between `from` and `to` does not fit in an `isize`
292    /// - The offset between `from` and `to` exceeds the offset storage
293    ///
294    /// # Safety
295    ///
296    /// - `from` must be the position of `out` within the archive
297    /// - `to` must be the position of some valid `T`
298    #[inline]
299    pub unsafe fn emplace(from: usize, to: usize, out: *mut Self) {
300        Self::try_emplace(from, to, out).unwrap();
301    }
302}
303
304impl<T: ArchivePointee + ?Sized, O: Offset> RelPtr<T, O>
305where
306    T::ArchivedMetadata: Default,
307{
308    /// Attempts to create a null relative pointer with default metadata.
309    ///
310    /// # Safety
311    ///
312    /// `pos` must be the position of `out` within the archive.
313    #[inline]
314    pub unsafe fn try_emplace_null(pos: usize, out: *mut Self) -> Result<(), OffsetError> {
315        let (fp, fo) = out_field!(out.raw_ptr);
316        RawRelPtr::try_emplace(pos + fp, pos, fo)?;
317        let (_, fo) = out_field!(out.metadata);
318        fo.write(Default::default());
319        Ok(())
320    }
321
322    /// Creates a null relative pointer with default metadata.
323    ///
324    /// # Panics
325    ///
326    /// - An offset of `0` does not fit in an `isize`
327    /// - An offset of `0` exceeds the offset storage
328    ///
329    /// # Safety
330    ///
331    /// `pos` must be the position of `out` within the archive.
332    #[inline]
333    pub unsafe fn emplace_null(pos: usize, out: *mut Self) {
334        Self::try_emplace_null(pos, out).unwrap()
335    }
336}
337
338impl<T: ArchivePointee + ?Sized, O: Offset> RelPtr<T, O> {
339    /// Attempts to create a relative pointer from one position to another.
340    ///
341    /// # Safety
342    ///
343    /// - `from` must be the position of `out` within the archive
344    /// - `to` must be the position of some valid `T`
345    /// - `value` must be the value being serialized
346    /// - `metadata_resolver` must be the result of serializing the metadata of `value`
347    #[inline]
348    pub unsafe fn try_resolve_emplace<U: ArchiveUnsized<Archived = T> + ?Sized>(
349        from: usize,
350        to: usize,
351        value: &U,
352        metadata_resolver: U::MetadataResolver,
353        out: *mut Self,
354    ) -> Result<(), OffsetError> {
355        let (fp, fo) = out_field!(out.raw_ptr);
356        RawRelPtr::try_emplace(from + fp, to, fo)?;
357        let (fp, fo) = out_field!(out.metadata);
358        value.resolve_metadata(from + fp, metadata_resolver, fo);
359        Ok(())
360    }
361
362    /// Creates a relative pointer from one position to another.
363    ///
364    /// # Panics
365    ///
366    /// - The offset between `from` and `to` does not fit in an `isize`
367    /// - The offset between `from` and `to` exceeds the offset storage
368    ///
369    /// # Safety
370    ///
371    /// - `from` must be the position of `out` within the archive
372    /// - `to` must be the position of some valid `T`
373    /// - `value` must be the value being serialized
374    /// - `metadata_resolver` must be the result of serializing the metadata of `value`
375    #[inline]
376    pub unsafe fn resolve_emplace<U: ArchiveUnsized<Archived = T> + ?Sized>(
377        from: usize,
378        to: usize,
379        value: &U,
380        metadata_resolver: U::MetadataResolver,
381        out: *mut Self,
382    ) {
383        Self::try_resolve_emplace(from, to, value, metadata_resolver, out).unwrap();
384    }
385
386    /// Gets the base pointer for the relative pointer.
387    #[inline]
388    pub fn base(&self) -> *const u8 {
389        self.raw_ptr.base()
390    }
391
392    /// Gets the mutable base pointer for the relative pointer.
393    #[inline]
394    pub fn base_mut(&mut self) -> *mut u8 {
395        self.raw_ptr.base_mut()
396    }
397
398    /// Gets the offset of the relative pointer from its base.
399    #[inline]
400    pub fn offset(&self) -> isize {
401        self.raw_ptr.offset()
402    }
403
404    /// Gets whether the offset of the relative pointer is 0.
405    #[inline]
406    pub fn is_null(&self) -> bool {
407        self.raw_ptr.is_null()
408    }
409
410    /// Gets the metadata of the relative pointer.
411    #[inline]
412    pub fn metadata(&self) -> &T::ArchivedMetadata {
413        &self.metadata
414    }
415
416    /// Calculates the memory address being pointed to by this relative pointer.
417    #[inline]
418    pub fn as_ptr(&self) -> *const T {
419        ptr_meta::from_raw_parts(self.raw_ptr.as_ptr(), T::pointer_metadata(&self.metadata))
420    }
421
422    /// Returns an unsafe mutable pointer to the memory address being pointed to by this relative
423    /// pointer.
424    #[inline]
425    pub fn as_mut_ptr(&mut self) -> *mut T {
426        ptr_meta::from_raw_parts_mut(
427            self.raw_ptr.as_mut_ptr(),
428            T::pointer_metadata(&self.metadata),
429        )
430    }
431}
432
433impl<T: ArchivePointee + ?Sized, O: fmt::Debug> fmt::Debug for RelPtr<T, O>
434where
435    T::ArchivedMetadata: fmt::Debug,
436{
437    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438        f.debug_struct("RelPtr")
439            .field("raw_ptr", &self.raw_ptr)
440            .field("metadata", &self.metadata)
441            .finish()
442    }
443}
444
445impl<T: ArchivePointee + ?Sized, O: Offset> fmt::Pointer for RelPtr<T, O> {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        fmt::Pointer::fmt(&self.as_ptr(), f)
448    }
449}