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}