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}