Skip to main content

musli_zerocopy/pointer/
ref.rs

1use core::any;
2use core::cmp::Ordering;
3use core::fmt;
4use core::hash::Hash;
5use core::marker::PhantomData;
6use core::mem::size_of;
7
8use crate::ZeroCopy;
9use crate::buf::{Padder, Validator};
10use crate::endian::{Big, ByteOrder, DefaultEndian, Little, Native};
11use crate::error::{CoerceError, CoerceErrorKind, Error};
12use crate::mem::PackedMaybeUninit;
13use crate::pointer::Coerce;
14use crate::pointer::{DefaultSize, Pointee, Size};
15
16/// A stored reference to a type `T`.
17///
18/// A reference is made up of two components:
19/// * An [`offset()`] indicating the absolute offset into a [`Buf`] where the
20///   pointed-to (pointee) data is located.
21/// * An optional [`metadata()`] components, which if set indicates that this
22///   reference is a wide pointer. This is used when encoding types such as
23///   `[T]` or `str` to include additional data necessary to handle the type.
24///
25/// [`Buf`]: crate::buf::Buf
26/// [`offset()`]: Ref::offset
27/// [`metadata()`]: Ref::metadata
28///
29/// # Examples
30///
31/// ```
32/// use std::mem::align_of;
33///
34/// use musli_zerocopy::{Ref, OwnedBuf};
35///
36/// let mut buf = OwnedBuf::with_alignment::<u32>();
37/// buf.extend_from_slice(&[1, 2, 3, 4])?;
38///
39/// let buf = buf.as_ref();
40///
41/// let number = Ref::<u32>::new(0u32);
42/// assert_eq!(*buf.load(number)?, u32::from_ne_bytes([1, 2, 3, 4]));
43/// # Ok::<_, musli_zerocopy::Error>(())
44/// ```
45#[repr(C)]
46pub struct Ref<T, E = DefaultEndian, O = DefaultSize>
47where
48    T: ?Sized + Pointee,
49    E: ByteOrder,
50    O: Size,
51{
52    offset: O,
53    metadata: T::Stored<O>,
54    _marker: PhantomData<E>,
55}
56
57unsafe impl<T, E, O> ZeroCopy for Ref<T, E, O>
58where
59    T: ?Sized + Pointee,
60    E: ByteOrder,
61    O: Size,
62{
63    // A `Ref` type cannot inhabit any bit pattern since it must represent a
64    // validly sized reference.
65    const ANY_BITS: bool = false;
66
67    /// The `Ref` type is never padded.
68    const PADDED: bool = const {
69        debug_assert!(
70            size_of::<Self>() == (size_of::<O>() + size_of::<T::Stored<O>>()),
71            "Size of Ref should equal its fields"
72        );
73        debug_assert!(!O::PADDED, "Offset should not be padded");
74        debug_assert!(!T::Stored::<O>::PADDED, "Metadata should not be padded");
75        false
76    };
77
78    // Since the ref type statically encodes the byte order, it cannot be byte
79    // swapped with retained meaning.
80    const CAN_SWAP_BYTES: bool = false;
81
82    #[inline]
83    unsafe fn pad(padder: &mut Padder<'_, Self>) {
84        unsafe {
85            padder.pad::<O>();
86            padder.pad::<T::Stored<O>>();
87        }
88    }
89
90    #[inline]
91    unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
92        unsafe {
93            // NB: The `Ref` might be stored unaligned, such as inside of a
94            // packed struct, so its fields have to be read unaligned.
95            let offset = validator.read_field::<O>()?;
96            let metadata = validator.read_field::<T::Stored<O>>()?;
97            Self::try_from_parts(offset, metadata)?;
98            Ok(())
99        }
100    }
101
102    #[inline]
103    fn swap_bytes<B: ByteOrder>(self) -> Self {
104        self
105    }
106}
107
108impl<T, E, O> Ref<T, E, O>
109where
110    T: ?Sized + Pointee,
111    E: ByteOrder,
112    O: Size,
113{
114    /// Construct a new reference from raw representation.
115    ///
116    /// # Safety
117    ///
118    /// The caller must ensure that the reference follows all layout
119    /// requirements documented under the Errors section in
120    /// [`Ref::with_metadata`].
121    #[inline]
122    pub(crate) const unsafe fn new_unchecked(offset: O, metadata: T::Stored<O>) -> Self {
123        Self {
124            offset,
125            metadata,
126            _marker: PhantomData,
127        }
128    }
129
130    /// Convert this reference into a [`Big`]-endian [`ByteOrder`].
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use musli_zerocopy::{endian, Ref};
136    ///
137    /// let r: Ref<u32> = Ref::new(10u32);
138    /// assert_eq!(r.offset(), 10);
139    ///
140    /// let r: Ref<u32, endian::Little> = Ref::new(10u32);
141    /// assert_eq!(r.offset(), 10);
142    ///
143    /// let r: Ref<u32, endian::Big> = r.to_be();
144    /// assert_eq!(r.offset(), 10);
145    /// ```
146    #[inline]
147    pub fn to_be(self) -> Ref<T, Big, O> {
148        self.to_endian()
149    }
150
151    /// Convert this reference into a [`Little`]-endian [`ByteOrder`].
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use musli_zerocopy::{endian, Ref};
157    ///
158    /// let r: Ref<u32> = Ref::new(10u32);
159    /// assert_eq!(r.offset(), 10);
160    ///
161    /// let r: Ref<u32, endian::Big> = Ref::new(10u32);
162    /// assert_eq!(r.offset(), 10);
163    ///
164    /// let r: Ref<u32, endian::Little> = r.to_le();
165    /// assert_eq!(r.offset(), 10);
166    /// ```
167    #[inline]
168    pub fn to_le(self) -> Ref<T, Little, O> {
169        self.to_endian()
170    }
171
172    /// Convert this reference into a [`Native`]-endian [`ByteOrder`].
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use musli_zerocopy::{endian, Ref};
178    ///
179    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Big>::new(10u32).to_ne();
180    /// assert_eq!(r.offset(), 10);
181    ///
182    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Little>::new(10u32).to_ne();
183    /// assert_eq!(r.offset(), 10);
184    ///
185    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Native>::new(10u32).to_ne();
186    /// assert_eq!(r.offset(), 10);
187    /// ```
188    #[inline]
189    pub fn to_ne(self) -> Ref<T, Native, O> {
190        self.to_endian()
191    }
192
193    /// Convert this reference into a `U`-endian [`ByteOrder`].
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// use musli_zerocopy::{endian, Ref};
199    ///
200    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Big>::new(10u32).to_endian();
201    /// assert_eq!(r.offset(), 10);
202    ///
203    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Little>::new(10u32).to_endian();
204    /// assert_eq!(r.offset(), 10);
205    ///
206    /// let r: Ref<u32, endian::Native> = Ref::<u32, endian::Native>::new(10u32).to_endian();
207    /// assert_eq!(r.offset(), 10);
208    /// ```
209    #[inline]
210    pub fn to_endian<U: ByteOrder>(self) -> Ref<T, U, O> {
211        Ref {
212            offset: self.offset.swap_bytes::<E>().swap_bytes::<U>(),
213            metadata: self.metadata.swap_bytes::<E>().swap_bytes::<U>(),
214            _marker: PhantomData,
215        }
216    }
217}
218
219impl<T, E, O> Ref<T, E, O>
220where
221    T: ?Sized + Pointee,
222    E: ByteOrder,
223    O: Size,
224{
225    #[inline]
226    fn from_parts(offset: O, metadata: T::Stored<O>) -> Self {
227        match Self::try_from_parts(offset, metadata) {
228            Ok(ok) => ok,
229            Err(error) => panic!("{error}"),
230        }
231    }
232
233    #[inline]
234    fn try_from_parts(offset: O, metadata: T::Stored<O>) -> Result<Self, CoerceError> {
235        T::check_layout(
236            offset.swap_bytes::<E>().as_usize(),
237            T::to_metadata(metadata.swap_bytes::<E>()),
238        )?;
239
240        Ok(Self {
241            offset,
242            metadata,
243            _marker: PhantomData,
244        })
245    }
246
247    /// Construct a reference with custom metadata.
248    ///
249    /// # Panics
250    ///
251    /// This will panic if:
252    /// * The `offset` or `metadata` can't be byte swapped as per
253    ///   [`ZeroCopy::CAN_SWAP_BYTES`].
254    /// * Packed [`offset()`] cannot be constructed from `U` (out of range).
255    /// * Packed [`metadata()`] cannot be constructed from `T::Metadata` (reason
256    ///   depends on the exact metadata).
257    /// * The metadata does not describe a valid [`Layout`].
258    /// * The `offset` plus this layout's size overflow `usize::MAX`.
259    ///
260    /// To guarantee that this constructor will never panic, [`Ref<T, E,
261    /// usize>`] can be used. This also ensures that construction is a no-op.
262    ///
263    /// [`Layout`]: core::alloc::Layout
264    /// [`offset()`]: Ref::offset
265    /// [`metadata()`]: Ref::metadata
266    ///
267    /// # Examples
268    ///
269    /// ```
270    /// use musli_zerocopy::Ref;
271    ///
272    /// let reference = Ref::<[u64]>::with_metadata(42u32, 10);
273    /// assert_eq!(reference.offset(), 42);
274    /// assert_eq!(reference.len(), 10);
275    /// ```
276    ///
277    /// Using maximally sized metadata with different byte orderings:
278    ///
279    /// ```
280    /// use musli_zerocopy::Ref;
281    /// use musli_zerocopy::endian::{Big, Little};
282    ///
283    /// let o = usize::MAX - isize::MAX as usize;
284    /// let l = isize::MAX as usize;
285    ///
286    /// let a = Ref::<[u8], Big, usize>::with_metadata(o, l);
287    /// let b = Ref::<[u8], Little, usize>::with_metadata(o, l);
288    ///
289    /// assert_eq!(a.len(), l);
290    /// assert_eq!(a.len(), b.len());
291    /// ```
292    #[inline]
293    pub fn with_metadata<U>(offset: U, metadata: T::Metadata) -> Self
294    where
295        U: Size,
296    {
297        match Ref::try_with_metadata(offset, metadata) {
298            Ok(ok) => ok,
299            Err(error) => panic!("{error}"),
300        }
301    }
302
303    /// Fallibly try to construct a reference with metadata.
304    ///
305    /// # Errors
306    ///
307    /// This will not compile through a constant assertion if the `offset` or
308    ///   `metadata` can't be byte swapped as per [`ZeroCopy::CAN_SWAP_BYTES`].
309    ///
310    /// This will error if:
311    /// * Packed [`offset()`] cannot be constructed from `U` (out of range).
312    /// * Packed [`metadata()`] cannot be constructed from `T::Metadata` (reason
313    ///   depends on the exact metadata).
314    /// * The metadata does not describe a valid [`Layout`].
315    /// * The `offset` plus this layout's overflows `usize::MAX`.
316    ///
317    /// To guarantee that this constructor will never error, [`Ref<T, Native,
318    /// usize>`] can be used. This also ensures that construction is a no-op.
319    ///
320    /// [`Layout`]: core::alloc::Layout
321    /// [`offset()`]: Ref::offset
322    /// [`metadata()`]: Ref::metadata
323    ///
324    /// # Examples
325    ///
326    /// ```
327    /// use musli_zerocopy::Ref;
328    ///
329    /// let reference = Ref::<[u64]>::try_with_metadata(42u32, 10)?;
330    /// assert_eq!(reference.offset(), 42);
331    /// assert_eq!(reference.len(), 10);
332    /// # Ok::<_, musli_zerocopy::Error>(())
333    /// ```
334    ///
335    /// Using maximally sized metadata with different byte orderings:
336    ///
337    /// ```
338    /// use musli_zerocopy::Ref;
339    /// use musli_zerocopy::endian::{Big, Little};
340    ///
341    /// let o = usize::MAX - isize::MAX as usize;
342    /// let l = isize::MAX as usize;
343    ///
344    /// let a = Ref::<[u8], Big, usize>::try_with_metadata(o, l)?;
345    /// let b = Ref::<[u8], Little, usize>::try_with_metadata(o, l)?;
346    ///
347    /// assert_eq!(a.len(), l);
348    /// assert_eq!(a.len(), b.len());
349    ///
350    /// assert!(Ref::<[u8], Big, usize>::try_with_metadata(o + 1, l).is_err());
351    /// assert!(Ref::<[u8], Little, usize>::try_with_metadata(o + 1, l).is_err());
352    /// # Ok::<_, musli_zerocopy::CoerceError>(())
353    /// ```
354    pub fn try_with_metadata<U>(offset: U, metadata: T::Metadata) -> Result<Self, CoerceError>
355    where
356        U: Size,
357    {
358        T::check_layout(offset.as_usize(), metadata)?;
359
360        // SAFETY: We just checked that the layout of the type is valid.
361        unsafe { Self::try_with_metadata_unchecked(offset, metadata) }
362    }
363
364    /// Construct a reference doing only the layout conversion and checks,
365    /// trusting that the layout is valid.
366    #[inline(always)]
367    pub(crate) unsafe fn try_with_metadata_unchecked<U>(
368        offset: U,
369        metadata: T::Metadata,
370    ) -> Result<Self, CoerceError>
371    where
372        U: Size,
373    {
374        Ok(Ref {
375            offset: O::try_from(offset)?.swap_bytes::<E>(),
376            metadata: T::try_from_metadata(metadata)?.swap_bytes::<E>(),
377            _marker: PhantomData,
378        })
379    }
380}
381
382impl<T, E, O> Ref<[T], E, O>
383where
384    T: ZeroCopy,
385    E: ByteOrder,
386    O: Size,
387{
388    /// Return the number of elements in the slice `[T]`.
389    ///
390    /// # Examples
391    ///
392    /// ```
393    /// use musli_zerocopy::pointer::Ref;
394    ///
395    /// let slice = Ref::<[u32]>::with_metadata(0u32, 2);
396    /// assert_eq!(slice.len(), 2);
397    /// ```
398    #[inline]
399    pub fn len(self) -> usize {
400        self.metadata.swap_bytes::<E>().as_usize()
401    }
402
403    /// Test if the slice `[T]` is empty.
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use musli_zerocopy::pointer::Ref;
409    ///
410    /// let slice = Ref::<[u32]>::with_metadata(0u32, 0);
411    /// assert!(slice.is_empty());
412    ///
413    /// let slice = Ref::<[u32]>::with_metadata(0u32, 2);
414    /// assert!(!slice.is_empty());
415    /// ```
416    #[inline]
417    pub fn is_empty(self) -> bool {
418        self.metadata.is_zero()
419    }
420
421    /// Try to get a reference directly out of the slice without validation.
422    ///
423    /// This avoids having to validate every element in a slice in order to
424    /// address them.
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// use musli_zerocopy::OwnedBuf;
430    ///
431    /// let mut buf = OwnedBuf::new();
432    /// let slice = buf.store_slice(&[1, 2, 3, 4])?;
433    ///
434    /// let two = slice.get(2).expect("Missing element 2");
435    /// assert_eq!(buf.load(two)?, &3);
436    ///
437    /// assert!(slice.get(4).is_none());
438    /// # Ok::<_, musli_zerocopy::Error>(())
439    /// ```
440    #[inline]
441    pub fn get(self, index: usize) -> Option<Ref<T, E, O>> {
442        if index >= self.len() {
443            return None;
444        }
445
446        let offset = self.offset.swap_bytes::<E>().as_usize() + size_of::<T>() * index;
447        Some(Ref::new(offset))
448    }
449
450    /// Get an unchecked reference directly out of the slice without validation.
451    ///
452    /// This avoids having to validate every element in a slice in order to
453    /// address them.
454    ///
455    /// In contrast to [`get()`], this does not check that the index is within
456    /// the bounds of the current slice, all though it's not unsafe since it
457    /// cannot lead to anything inherently unsafe. Only garbled data.
458    ///
459    /// [`get()`]: Ref::get
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// use musli_zerocopy::OwnedBuf;
465    ///
466    /// let mut buf = OwnedBuf::new();
467    /// let slice = buf.store_slice(&[1, 2, 3, 4])?;
468    ///
469    /// let two = slice.get_unchecked(2);
470    /// assert_eq!(buf.load(two)?, &3);
471    ///
472    /// let oob = slice.get_unchecked(4);
473    /// assert!(buf.load(oob).is_err());
474    /// # Ok::<_, musli_zerocopy::Error>(())
475    /// ```
476    pub fn get_unchecked(self, index: usize) -> Ref<T, E, O> {
477        let offset = self.offset.swap_bytes::<E>().as_usize() + size_of::<T>() * index;
478        Ref::new(offset)
479    }
480
481    /// Split the slice reference at the given position `at`.
482    ///
483    /// # Panics
484    ///
485    /// This panics if the given range is out of bounds.
486    ///
487    /// # Examples
488    ///
489    /// ```
490    /// use musli_zerocopy::OwnedBuf;
491    ///
492    /// let mut buf = OwnedBuf::new();
493    /// let slice = buf.store_slice(&[1, 2, 3, 4])?;
494    ///
495    /// buf.align_in_place()?;
496    ///
497    /// let (a, b) = slice.split_at(3)?;
498    /// let (c, d) = slice.split_at(4)?;
499    ///
500    /// assert_eq!(buf.load(a)?, &[1, 2, 3]);
501    /// assert_eq!(buf.load(b)?, &[4]);
502    /// assert_eq!(buf.load(c)?, &[1, 2, 3, 4]);
503    /// assert_eq!(buf.load(d)?, &[]);
504    /// # Ok::<_, musli_zerocopy::Error>(())
505    /// ```
506    #[inline]
507    pub fn split_at(self, at: usize) -> Result<(Self, Self), CoerceError> {
508        let offset = self.offset();
509        let len = self.len();
510
511        if at > len {
512            return Err(CoerceError::new(CoerceErrorKind::SplitAt { at, len }));
513        }
514
515        // SAFETY: Since we are splitting from a valid slice we know that they are valid.
516        unsafe {
517            let a = Self::try_with_metadata_unchecked(offset, at)?;
518            let b = Self::try_with_metadata_unchecked(offset + at * size_of::<T>(), len - at)?;
519            Ok((a, b))
520        }
521    }
522
523    /// Perform an fetch like `get` which panics with diagnostics in case the
524    /// index is out-of-bounds.
525    #[inline]
526    #[cfg(feature = "alloc")]
527    pub(crate) fn at(self, index: usize) -> Ref<T, E, O> {
528        let Some(r) = self.get(index) else {
529            panic!("Index {index} out of bounds 0-{}", self.len());
530        };
531
532        r
533    }
534
535    /// Construct an iterator over this reference.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// use musli_zerocopy::OwnedBuf;
541    ///
542    /// let mut buf = OwnedBuf::new();
543    /// buf.extend_from_slice(&[1, 2, 3, 4])?;
544    ///
545    /// let slice = buf.store_slice(&[1, 2, 3, 4])?;
546    ///
547    /// buf.align_in_place()?;
548    ///
549    /// let mut out = Vec::new();
550    ///
551    /// for r in slice.iter() {
552    ///     out.push(*buf.load(r)?);
553    /// }
554    ///
555    /// for r in slice.iter().rev() {
556    ///     out.push(*buf.load(r)?);
557    /// }
558    ///
559    /// assert_eq!(out, [1, 2, 3, 4, 4, 3, 2, 1]);
560    /// # Ok::<_, musli_zerocopy::Error>(())
561    /// ```
562    #[inline]
563    pub fn iter(self) -> Iter<T, E, O> {
564        let start = self.offset.swap_bytes::<E>();
565        let end = self
566            .metadata
567            .swap_bytes::<E>()
568            .checked_mul(O::from_usize(size_of::<T>()))
569            .and_then(|v| v.checked_add(start))
570            .unwrap_or(start);
571
572        Iter {
573            start,
574            end,
575            _marker: PhantomData,
576        }
577    }
578}
579
580impl<E, O> Ref<str, E, O>
581where
582    E: ByteOrder,
583    O: Size,
584{
585    /// Return the length of the string.
586    ///
587    /// # Examples
588    ///
589    /// ```
590    /// use musli_zerocopy::pointer::Ref;
591    ///
592    /// let slice = Ref::<str>::with_metadata(0u32, 2);
593    /// assert_eq!(slice.len(), 2);
594    /// ```
595    #[inline]
596    pub fn len(self) -> usize {
597        self.metadata.swap_bytes::<E>().as_usize()
598    }
599
600    /// Test if the slice `[T]` is empty.
601    ///
602    /// # Examples
603    ///
604    /// ```
605    /// use musli_zerocopy::pointer::Ref;
606    ///
607    /// let slice = Ref::<str>::with_metadata(0u32, 0);
608    /// assert!(slice.is_empty());
609    ///
610    /// let slice = Ref::<str>::with_metadata(0u32, 2);
611    /// assert!(!slice.is_empty());
612    /// ```
613    #[inline]
614    pub fn is_empty(self) -> bool {
615        self.metadata.is_zero()
616    }
617}
618
619/// An iterator over a `Ref<[T]>` which produces `Ref<T>` values.
620///
621/// See [`Ref::iter`].
622pub struct Iter<T, E, O> {
623    start: O,
624    end: O,
625    _marker: PhantomData<(T, E)>,
626}
627
628impl<T, E, O> Iterator for Iter<T, E, O>
629where
630    T: ZeroCopy,
631    E: ByteOrder,
632    O: Size,
633{
634    type Item = Ref<T, E, O>;
635
636    #[inline]
637    fn next(&mut self) -> Option<Self::Item> {
638        if self.start.eq(self.end) {
639            return None;
640        }
641
642        let start = self.start;
643        self.start = self.start.checked_add(O::from_usize(size_of::<T>()))?;
644        Some(Ref::from_parts(start.swap_bytes::<E>(), ()))
645    }
646}
647
648impl<T, E, O> DoubleEndedIterator for Iter<T, E, O>
649where
650    T: ZeroCopy,
651    E: ByteOrder,
652    O: Size,
653{
654    #[inline]
655    fn next_back(&mut self) -> Option<Self::Item> {
656        if self.start.eq(self.end) {
657            return None;
658        }
659
660        self.end = self.end.checked_sub(O::from_usize(size_of::<T>()))?;
661        Some(Ref::from_parts(self.end.swap_bytes::<E>(), ()))
662    }
663}
664
665impl<T, E, O> Ref<T, E, O>
666where
667    T: ?Sized + Pointee,
668    E: ByteOrder,
669    O: Size,
670{
671    /// The number of elements in the slice.
672    ///
673    /// # Examples
674    ///
675    /// ```
676    /// use musli_zerocopy::pointer::Ref;
677    ///
678    /// let slice = Ref::<str>::with_metadata(0u32, 10);
679    /// assert_eq!(slice.metadata(), 10);
680    /// ```
681    #[inline]
682    pub fn metadata(self) -> T::Stored<O> {
683        self.metadata
684    }
685}
686
687impl<T, E, O> Ref<T, E, O>
688where
689    T: Pointee<Metadata = (), Stored<O> = ()>,
690    E: ByteOrder,
691    O: Size,
692{
693    /// Construct a reference at the given offset.
694    ///
695    /// # Errors
696    ///
697    /// This will not compile through a constant assertion if the `offset` or
698    /// can't be byte swapped as per [`ZeroCopy::CAN_SWAP_BYTES`].
699    ///
700    /// # Panics
701    ///
702    /// This will panic if:
703    /// * Packed [`offset()`] cannot be constructed from `U` (out of range).
704    ///
705    /// [`offset()`]: Self::offset
706    ///
707    /// # Examples
708    ///
709    /// ```
710    /// use musli_zerocopy::Ref;
711    ///
712    /// let reference = Ref::<u64>::new(42u32);
713    /// assert_eq!(reference.offset(), 42);
714    /// ```
715    ///
716    /// Characters cannot be used as offsets:
717    ///
718    /// ```compile_fail
719    /// use musli_zerocopy::Ref;
720    ///
721    /// let reference = Ref::<_, _, char>::new('a');
722    /// ```
723    #[inline]
724    pub fn new<U>(offset: U) -> Self
725    where
726        U: Size,
727    {
728        const {
729            assert!(
730                O::CAN_SWAP_BYTES,
731                "Offset cannot be byte-ordered since it would not inhabit valid types",
732            );
733        }
734
735        let Ok(offset) = O::try_from(offset) else {
736            panic!(
737                "Offset {} not in the valid range 0-{}",
738                offset.as_usize(),
739                O::MAX
740            );
741        };
742
743        Ref::from_parts(O::swap_bytes::<E>(offset), ())
744    }
745
746    /// Construct a typed reference to the zeroeth offset in a buffer.
747    ///
748    /// # Examples
749    ///
750    /// ```
751    /// use musli_zerocopy::Ref;
752    ///
753    /// let reference = Ref::<u64>::zero();
754    /// assert_eq!(reference.offset(), 0);
755    /// ```
756    #[inline]
757    pub const fn zero() -> Self {
758        Self {
759            offset: O::ZERO,
760            metadata: (),
761            _marker: PhantomData,
762        }
763    }
764}
765
766impl<T, E, O> Ref<T, E, O>
767where
768    T: ?Sized + Pointee,
769    E: ByteOrder,
770    O: Size,
771{
772    /// Get the offset the reference points to.
773    ///
774    /// # Examples
775    ///
776    /// ```
777    /// use musli_zerocopy::Ref;
778    /// use musli_zerocopy::endian::{Big, Little, Native};
779    ///
780    /// let reference = Ref::<u64, Native>::new(42u32);
781    /// assert_eq!(reference.offset(), 42);
782    ///
783    /// let reference = Ref::<u64, Little>::new(42u32);
784    /// assert_eq!(reference.offset(), 42);
785    ///
786    /// let reference = Ref::<u64, Big>::new(42u32);
787    /// assert_eq!(reference.offset(), 42);
788    /// ```
789    #[inline]
790    pub fn offset(self) -> usize {
791        self.offset.swap_bytes::<E>().as_usize()
792    }
793
794    /// Coerce from one kind of reference to another ensuring that the
795    /// destination type `U` is size-compatible.
796    ///
797    /// This performs metadata conversion if the destination metadata for `U`
798    /// differs from `T`, such as for `[u32]` to `[u8]` it would multiply the
799    /// length by 4 to ensure that the slice points to an appropriately sized
800    /// region.
801    ///
802    /// If the metadata conversion would overflow, this will wrap around the
803    /// numerical bounds or panic for debug builds.
804    ///
805    /// See [`try_coerce()`] for more documentation, which is also a checked
806    /// variant of this method.
807    ///
808    /// [`try_coerce()`]: Self::try_coerce
809    pub fn coerce<U>(self) -> Ref<U, E, O>
810    where
811        T: Coerce<U>,
812        U: ?Sized + Pointee,
813    {
814        // NB: Since the metadata representation is byte-swapped, we need to
815        // swap it back and forth to convert to native representation during
816        // coercion.
817        let metadata = T::coerce_metadata(self.metadata.swap_bytes::<E>()).swap_bytes::<E>();
818        Ref::from_parts(self.offset, metadata)
819    }
820
821    /// Try to coerce from one kind of reference to another ensuring that the
822    /// destination type `U` is size-compatible.
823    ///
824    /// This performs metadata conversion if the destination metadata for `U`
825    /// differs from `T`, such as for `[u32]` to `[u8]` it would multiply the
826    /// length by 4 to ensure that the slice points to an appropriately sized
827    /// region.
828    ///
829    /// This returns `None` in case metadata would overflow due to the
830    /// conversion.
831    ///
832    /// ```
833    /// use musli_zerocopy::Ref;
834    ///
835    /// let reference: Ref<u64> = Ref::zero();
836    /// let reference2 = reference.coerce::<[u32]>();
837    /// assert_eq!(reference2.len(), 2);
838    /// ```
839    ///
840    /// This method ensures that coercions across inappropriate types are
841    /// prohibited, such as coercing from a reference to a slice which is too
842    /// large.
843    ///
844    /// ```compile_fail
845    /// use musli_zerocopy::Ref;
846    ///
847    /// let reference: Ref<u32> = Ref::zero();
848    /// let reference2 = reference.coerce::<[u64]>();
849    /// ```
850    ///
851    /// If metadata needs to be adjusted for the destination type such as for
852    /// slices, it will be:
853    ///
854    /// ```
855    /// use musli_zerocopy::Ref;
856    ///
857    /// let reference: Ref<[u32]> = Ref::with_metadata(0u32, 1);
858    /// let reference2 = reference.try_coerce::<[u8]>()?;
859    /// assert_eq!(reference2.len(), 4);
860    ///
861    /// let reference: Ref<str> = Ref::with_metadata(0u32, 12);
862    /// let reference2 = reference.try_coerce::<[u8]>()?;
863    /// assert_eq!(reference2.len(), 12);
864    /// # Ok::<_, musli_zerocopy::CoerceError>(())
865    /// ```
866    ///
867    /// This does mean that numerical overflow might occur if the packed
868    /// metadata is too small:
869    ///
870    /// ```
871    /// use musli_zerocopy::Ref;
872    /// use musli_zerocopy::endian::Native;
873    ///
874    /// let reference = Ref::<[u32], Native, u8>::with_metadata(0u32, 64);
875    /// let reference2 = reference.try_coerce::<[u8]>();
876    /// assert!(reference2.is_err()); // 64 * 4 would overflow u8 packed metadata.
877    /// ```
878    ///
879    /// Coercion of non-zero types are supported, but do not guarantee that the
880    /// destination data is valid.
881    pub fn try_coerce<U>(self) -> Result<Ref<U, E, O>, CoerceError>
882    where
883        T: Coerce<U>,
884        U: ?Sized + Pointee,
885    {
886        // NB: Since the metadata representation is byte-swapped, we need to
887        // swap it back and forth to convert to native representation during
888        // coercion.
889        let metadata = T::try_coerce_metadata(self.metadata.swap_bytes::<E>())?.swap_bytes::<E>();
890        Ref::try_from_parts(self.offset, metadata)
891    }
892
893    #[cfg(test)]
894    pub(crate) fn cast<U>(self) -> Ref<U, E, O>
895    where
896        U: ?Sized + Pointee<Stored<O> = T::Stored<O>>,
897    {
898        Ref::from_parts(self.offset, self.metadata)
899    }
900}
901
902impl<T, const N: usize, E, O> Ref<[T; N], E, O>
903where
904    T: ZeroCopy,
905    E: ByteOrder,
906    O: Size,
907{
908    /// Coerce a reference to an array into a slice.
909    ///
910    /// # Examples
911    ///
912    /// ```
913    /// use musli_zerocopy::OwnedBuf;
914    ///
915    /// let mut buf = OwnedBuf::new();
916    ///
917    /// let values = buf.store(&[1, 2, 3, 4])?;
918    /// let slice = values.array_into_slice()?;
919    ///
920    /// assert_eq!(buf.load(slice)?, &[1, 2, 3, 4]);
921    /// # Ok::<_, musli_zerocopy::Error>(())
922    /// ```
923    #[inline]
924    pub fn array_into_slice(self) -> Result<Ref<[T], E, O>, CoerceError> {
925        Ref::try_with_metadata(self.offset, N)
926    }
927}
928
929impl<T, E, O> Ref<PackedMaybeUninit<T>, E, O>
930where
931    T: Pointee,
932    E: ByteOrder,
933    O: Size,
934{
935    /// Assume that the reference is initialized.
936    ///
937    /// Unlike the counterpart in Rust, this isn't actually unsafe. Because in
938    /// order to load the reference again we'd have to validate it anyways.
939    #[inline]
940    pub const fn assume_init(self) -> Ref<T, E, O> {
941        Ref {
942            offset: self.offset,
943            metadata: self.metadata,
944            _marker: PhantomData,
945        }
946    }
947}
948
949impl<T, E, O> fmt::Debug for Ref<T, E, O>
950where
951    T: ?Sized + Pointee<Stored<O>: fmt::Debug>,
952    E: ByteOrder,
953    O: Size + fmt::Debug,
954{
955    #[inline]
956    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957        write!(
958            f,
959            "Ref<{}, {}, {}> {{ offset: {:?}, metadata: {:?} }}",
960            any::type_name::<T>(),
961            E::NAME,
962            any::type_name::<O>(),
963            self.offset,
964            self.metadata,
965        )
966    }
967}
968
969impl<T, E, O> Clone for Ref<T, E, O>
970where
971    T: ?Sized + Pointee,
972    E: ByteOrder,
973    O: Size,
974{
975    #[inline]
976    fn clone(&self) -> Self {
977        *self
978    }
979}
980
981impl<T, E, O> Copy for Ref<T, E, O>
982where
983    T: ?Sized + Pointee,
984    E: ByteOrder,
985    O: Size,
986{
987}
988
989impl<T, E, O> PartialEq for Ref<T, E, O>
990where
991    T: ?Sized + Pointee<Stored<O>: PartialEq>,
992    E: ByteOrder,
993    O: PartialEq + Size,
994{
995    #[inline]
996    fn eq(&self, other: &Self) -> bool {
997        self.offset == other.offset && self.metadata == other.metadata
998    }
999}
1000
1001impl<T, E, O> Eq for Ref<T, E, O>
1002where
1003    T: ?Sized + Pointee<Stored<O>: Eq>,
1004    E: ByteOrder,
1005    O: Eq + Size,
1006{
1007}
1008
1009impl<T, E, O> PartialOrd for Ref<T, E, O>
1010where
1011    T: ?Sized + Pointee<Stored<O>: PartialOrd>,
1012    E: ByteOrder,
1013    O: Ord + Size,
1014{
1015    #[inline]
1016    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1017        match self.offset.partial_cmp(&other.offset) {
1018            Some(Ordering::Equal) => {}
1019            ord => return ord,
1020        }
1021
1022        self.metadata.partial_cmp(&other.metadata)
1023    }
1024}
1025
1026impl<T, E, O> Ord for Ref<T, E, O>
1027where
1028    T: ?Sized + Pointee<Stored<O>: Ord>,
1029    E: ByteOrder,
1030    O: Ord + Size,
1031{
1032    #[inline]
1033    fn cmp(&self, other: &Self) -> Ordering {
1034        match self.offset.cmp(&other.offset) {
1035            Ordering::Equal => {}
1036            ord => return ord,
1037        }
1038
1039        self.metadata.cmp(&other.metadata)
1040    }
1041}
1042
1043impl<T, E, O> Hash for Ref<T, E, O>
1044where
1045    T: ?Sized + Pointee<Stored<O>: Hash>,
1046    E: ByteOrder,
1047    O: Hash + Size,
1048{
1049    #[inline]
1050    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
1051        self.offset.hash(state);
1052        self.metadata.hash(state);
1053    }
1054}