Skip to content

Commit b528e37

Browse files
committed
Implement Ref::{try_as_ref,try_into_ref,try_into_mut}
Only `Ref::try_as_mut` remains missing, probably pending polonius landing in rustc. Partially fixes #1865 Supersedes #1184
1 parent 0bee231 commit b528e37

File tree

2 files changed

+314
-4
lines changed

2 files changed

+314
-4
lines changed

src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,13 @@ impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
569569
self.src
570570
}
571571

572+
pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> ValidityError<NewSrc, Dst> {
573+
// INVARIANT: `with_src` doesn't change the type of `Dst`, so the
574+
// invariant that `Dst`'s alignment requirement is greater than one is
575+
// preserved.
576+
ValidityError { src: new_src, dst: SendSyncPhantomData::default() }
577+
}
578+
572579
/// Maps the source value associated with the conversion error.
573580
///
574581
/// This can help mitigate [issues with `Send`, `Sync` and `'static`

src/ref.rs

Lines changed: 307 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,85 @@ where
595595
}
596596
}
597597

598+
impl<B, T> Ref<B, T>
599+
where
600+
B: ByteSlice,
601+
T: KnownLayout + ?Sized,
602+
{
603+
/// Attempts to dereference this `Ref<_, T>` into a `&T` without copying.
604+
///
605+
/// If the bytes of `self` are a valid instance of `T`, this method returns
606+
/// a reference to those bytes interpreted as `T`. If those bytes are not a
607+
/// valid instance of `T`, this returns `Err`.
608+
///
609+
/// # Examples
610+
///
611+
/// ```
612+
/// use zerocopy::Ref;
613+
/// # use zerocopy_derive::*;
614+
///
615+
/// // The only valid value of this type is the byte `0xC0`
616+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
617+
/// #[repr(u8)]
618+
/// enum C0 { xC0 = 0xC0 }
619+
///
620+
/// // The only valid value of this type is the bytes `0xC0C0`.
621+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
622+
/// #[repr(C, packed)]
623+
/// struct C0C0(C0, C0);
624+
///
625+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
626+
/// #[repr(C)]
627+
/// struct Packet {
628+
/// magic_number: C0C0,
629+
/// mug_size: u8,
630+
/// temperature: u8,
631+
/// marshmallows: [[u8; 2]],
632+
/// }
633+
///
634+
/// let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
635+
///
636+
/// let r = Ref::<_, Packet>::new(bytes).unwrap();
637+
/// let packet = Ref::try_as_ref(r).unwrap();
638+
///
639+
/// assert_eq!(packet.mug_size, 240);
640+
/// assert_eq!(packet.temperature, 77);
641+
/// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
642+
/// ```
643+
#[must_use = "has no side effects"]
644+
#[inline(always)]
645+
pub fn try_as_ref(r: &Self) -> Result<&T, ValidityError<&Self, T>>
646+
where
647+
T: TryFromBytes + Immutable,
648+
{
649+
// Presumably unreachable, since we've guarded each constructor of `Ref`.
650+
static_assert_dst_is_not_zst!(T);
651+
652+
// SAFETY: We don't call any methods on `r` other than those provided by
653+
// `ByteSlice`.
654+
let b = unsafe { r.as_byte_slice() };
655+
656+
match Ptr::from_ref(b.deref()).try_cast_into_no_leftover::<T, BecauseImmutable>(None) {
657+
Ok(candidate) => match candidate.try_into_valid() {
658+
Ok(valid) => Ok(valid.as_ref()),
659+
Err(e) => Err(e.map_src(|_| r)),
660+
},
661+
Err(CastError::Validity(i)) => match i {},
662+
Err(CastError::Alignment(_) | CastError::Size(_)) => {
663+
// SAFETY: By invariant on `Ref::0`, the referenced byte slice
664+
// is aligned to `T`'s alignment and its size corresponds to a
665+
// valid size for `T`. Since properties are checked upon
666+
// constructing `Ref`, these failures are unreachable.
667+
unsafe { core::hint::unreachable_unchecked() }
668+
}
669+
}
670+
}
671+
}
672+
598673
impl<'a, B, T> Ref<B, T>
599674
where
600675
B: 'a + IntoByteSlice<'a>,
601-
T: FromBytes + KnownLayout + Immutable + ?Sized,
676+
T: TryFromBytes + KnownLayout + Immutable + ?Sized,
602677
{
603678
/// Converts this `Ref` into a reference.
604679
///
@@ -609,7 +684,10 @@ where
609684
/// there is no conflict with a method on the inner type.
610685
#[must_use = "has no side effects"]
611686
#[inline(always)]
612-
pub fn into_ref(r: Self) -> &'a T {
687+
pub fn into_ref(r: Self) -> &'a T
688+
where
689+
T: FromBytes,
690+
{
613691
// Presumably unreachable, since we've guarded each constructor of `Ref`.
614692
static_assert_dst_is_not_zst!(T);
615693

@@ -627,12 +705,91 @@ where
627705
let ptr = ptr.bikeshed_recall_valid();
628706
ptr.as_ref()
629707
}
708+
709+
/// Attempts to convert this `Ref<_, T>` into a `&T` without copying.
710+
///
711+
/// If the bytes of `self` are a valid instance of `T`, this method returns
712+
/// a reference to those bytes interpreted as `T`. If those bytes are not a
713+
/// valid instance of `T`, this returns `Err`.
714+
///
715+
/// # Examples
716+
///
717+
/// ```
718+
/// use zerocopy::Ref;
719+
/// # use zerocopy_derive::*;
720+
///
721+
/// // The only valid value of this type is the byte `0xC0`
722+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
723+
/// #[repr(u8)]
724+
/// enum C0 { xC0 = 0xC0 }
725+
///
726+
/// // The only valid value of this type is the bytes `0xC0C0`.
727+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
728+
/// #[repr(C, packed)]
729+
/// struct C0C0(C0, C0);
730+
///
731+
/// #[derive(TryFromBytes, KnownLayout, Immutable)]
732+
/// #[repr(C)]
733+
/// struct Packet {
734+
/// magic_number: C0C0,
735+
/// mug_size: u8,
736+
/// temperature: u8,
737+
/// marshmallows: [[u8; 2]],
738+
/// }
739+
///
740+
/// let bytes = &[0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
741+
///
742+
/// let r = Ref::<_, Packet>::new(bytes).unwrap();
743+
/// let packet = Ref::try_into_ref(r).unwrap();
744+
///
745+
/// assert_eq!(packet.mug_size, 240);
746+
/// assert_eq!(packet.temperature, 77);
747+
/// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
748+
/// ```
749+
#[must_use = "has no side effects"]
750+
#[inline(always)]
751+
pub fn try_into_ref(r: Self) -> Result<&'a T, ValidityError<Self, T>> {
752+
// Presumably unreachable, since we've guarded each constructor of `Ref`.
753+
static_assert_dst_is_not_zst!(T);
754+
755+
// SAFETY: We don't call any methods on `b` other than those provided by
756+
// `ByteSlice`.
757+
let bytes = unsafe { r.as_byte_slice() };
758+
759+
let bytes: &'_ [u8] = bytes.deref();
760+
761+
// Extend the lifetime of `bytes` to `'a`. This gives us a reference
762+
// `bytes` with the same lifetime as if we had called
763+
// `r.into_byte_slice()`, but without consuming `r`. This is valuable,
764+
// since we will need to return `r` if validation fails.
765+
//
766+
// SAFETY: This is sound because `bytes` lives for `'a`. `Self` is
767+
// `IntoByteSlice`, whose `.into_byte_slice()` method is guaranteed to
768+
// produce a `&'a [u8]` with the same address and length as the slice
769+
// obtained by `.deref()` (which is how `bytes` is obtained).
770+
let bytes = unsafe { mem::transmute::<&[u8], &'a [u8]>(bytes) };
771+
772+
match Ptr::from_ref(bytes).try_cast_into_no_leftover::<T, BecauseImmutable>(None) {
773+
Ok(candidate) => match candidate.try_into_valid() {
774+
Ok(candidate) => Ok(candidate.as_ref()),
775+
Err(e) => Err(e.with_src(r)),
776+
},
777+
Err(CastError::Validity(i)) => match i {},
778+
Err(CastError::Alignment(_) | CastError::Size(_)) => {
779+
// SAFETY: By invariant on `Ref::0`, the referenced byte slice
780+
// is aligned to `T`'s alignment and its size corresponds to a
781+
// valid size for `T`. Since properties are checked upon
782+
// constructing `Ref`, these failures are unreachable.
783+
unsafe { core::hint::unreachable_unchecked() }
784+
}
785+
}
786+
}
630787
}
631788

632789
impl<'a, B, T> Ref<B, T>
633790
where
634791
B: 'a + IntoByteSliceMut<'a>,
635-
T: FromBytes + IntoBytes + KnownLayout + ?Sized,
792+
T: TryFromBytes + IntoBytes + KnownLayout + ?Sized,
636793
{
637794
/// Converts this `Ref` into a mutable reference.
638795
///
@@ -643,7 +800,10 @@ where
643800
/// there is no conflict with a method on the inner type.
644801
#[must_use = "has no side effects"]
645802
#[inline(always)]
646-
pub fn into_mut(r: Self) -> &'a mut T {
803+
pub fn into_mut(r: Self) -> &'a mut T
804+
where
805+
T: FromBytes,
806+
{
647807
// Presumably unreachable, since we've guarded each constructor of `Ref`.
648808
static_assert_dst_is_not_zst!(T);
649809

@@ -661,6 +821,86 @@ where
661821
let ptr = ptr.bikeshed_recall_valid();
662822
ptr.as_mut()
663823
}
824+
825+
/// Attempts to convert this `Ref<_, T>` into a `&mut T` without copying.
826+
///
827+
/// If the bytes of `self` are a valid instance of `T`, this method returns
828+
/// a reference to those bytes interpreted as `T`. If those bytes are not a
829+
/// valid instance of `T`, this returns `Err`.
830+
///
831+
/// # Examples
832+
///
833+
/// ```
834+
/// use zerocopy::Ref;
835+
/// # use zerocopy_derive::*;
836+
///
837+
/// // The only valid value of this type is the byte `0xC0`
838+
/// #[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
839+
/// #[repr(u8)]
840+
/// enum C0 { xC0 = 0xC0 }
841+
///
842+
/// // The only valid value of this type is the bytes `0xC0C0`.
843+
/// #[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
844+
/// #[repr(C, packed)]
845+
/// struct C0C0(C0, C0);
846+
///
847+
/// #[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
848+
/// #[repr(C, packed)]
849+
/// struct Packet {
850+
/// magic_number: C0C0,
851+
/// mug_size: u8,
852+
/// temperature: u8,
853+
/// marshmallows: [[u8; 2]],
854+
/// }
855+
///
856+
/// let bytes = &mut [0xC0, 0xC0, 240, 77, 0, 1, 2, 3, 4, 5][..];
857+
///
858+
/// let r = Ref::<_, Packet>::new(bytes).unwrap();
859+
/// let packet = Ref::try_into_mut(r).unwrap();
860+
///
861+
/// assert_eq!(packet.mug_size, 240);
862+
/// assert_eq!(packet.temperature, 77);
863+
/// assert_eq!(packet.marshmallows, [[0, 1], [2, 3], [4, 5]]);
864+
/// ```
865+
#[must_use = "has no side effects"]
866+
#[inline(always)]
867+
pub fn try_into_mut(mut r: Self) -> Result<&'a mut T, ValidityError<Self, T>> {
868+
// Presumably unreachable, since we've guarded each constructor of `Ref`.
869+
static_assert_dst_is_not_zst!(T);
870+
871+
// SAFETY: We don't call any methods on `b` other than those provided by
872+
// `ByteSliceMut`.
873+
let bytes = unsafe { r.as_byte_slice_mut() };
874+
875+
let bytes: &'_ mut [u8] = bytes.deref_mut();
876+
877+
// Extend the lifetime of `bytes` to `'a`. This gives us a reference
878+
// `bytes` with the same lifetime as if we had called
879+
// `r.into_byte_slice_mut()`, but without consuming `r`. This is
880+
// valuable, since we will need to return `r` if validation fails.
881+
//
882+
// SAFETY: This is sound because `bytes` lives for `'a`. `Self` is
883+
// `IntoByteSliceMut`, whose `.into_byte_slice_mut()` method is
884+
// guaranteed to produce a `&'a [u8]` with the same address and length
885+
// as the slice obtained by `.deref()` (which is how `bytes` is
886+
// obtained).
887+
let bytes = unsafe { mem::transmute::<&mut [u8], &'a mut [u8]>(bytes) };
888+
889+
match Ptr::from_mut(bytes).try_cast_into_no_leftover::<T, BecauseExclusive>(None) {
890+
Ok(candidate) => match candidate.try_into_valid() {
891+
Ok(candidate) => Ok(candidate.as_mut()),
892+
Err(e) => Err(e.with_src(r)),
893+
},
894+
Err(CastError::Validity(i)) => match i {},
895+
Err(CastError::Alignment(_) | CastError::Size(_)) => {
896+
// SAFETY: By invariant on `Ref::0`, the referenced byte slice
897+
// is aligned to `T`'s alignment and its size corresponds to a
898+
// valid size for `T`. Since properties are checked upon
899+
// constructing `Ref`, these failures are unreachable.
900+
unsafe { core::hint::unreachable_unchecked() }
901+
}
902+
}
903+
}
664904
}
665905

666906
impl<B, T> Ref<B, T>
@@ -1109,6 +1349,33 @@ mod tests {
11091349
assert!(Ref::<_, [AU64]>::from_suffix_with_elems(&buf.t[..], unreasonable_len).is_err());
11101350
}
11111351

1352+
#[test]
1353+
#[allow(unstable_name_collisions)]
1354+
#[allow(clippy::as_conversions)]
1355+
fn test_try_as_ref() {
1356+
#[allow(unused)]
1357+
use crate::util::AsAddress as _;
1358+
1359+
// valid source
1360+
1361+
let buf = Align::<[u8; 8], u64>::default();
1362+
let buf_addr = (&buf.t as *const [u8; 8]).addr();
1363+
1364+
let r = Ref::<_, u64>::from_bytes(&buf.t[..]).unwrap();
1365+
let rf = Ref::try_as_ref(&r).unwrap();
1366+
assert_eq!(rf, &0u64);
1367+
assert_eq!((rf as *const u64).addr(), buf_addr);
1368+
1369+
// invalid source
1370+
1371+
let buf = Align::<[u8; 1], u64>::new([42]);
1372+
let buf_addr = (&buf.t as *const [u8; 1]).addr();
1373+
1374+
let r = Ref::<_, bool>::from_bytes(&buf.t[..]).unwrap();
1375+
let re = Ref::try_as_ref(&r).unwrap_err();
1376+
assert_eq!(Ref::bytes(re.into_src()).addr(), buf_addr);
1377+
}
1378+
11121379
#[test]
11131380
#[allow(unstable_name_collisions)]
11141381
#[allow(clippy::as_conversions)]
@@ -1132,6 +1399,42 @@ mod tests {
11321399
assert_eq!(buf.t, [0xFF; 8]);
11331400
}
11341401

1402+
#[test]
1403+
#[allow(unstable_name_collisions)]
1404+
#[allow(clippy::as_conversions)]
1405+
fn test_try_into_ref_mut() {
1406+
#[allow(unused)]
1407+
use crate::util::AsAddress as _;
1408+
1409+
// valid source
1410+
1411+
let mut buf = Align::<[u8; 8], u64>::default();
1412+
let buf_addr = (&buf.t as *const [u8; 8]).addr();
1413+
1414+
let r = Ref::<_, u64>::from_bytes(&buf.t[..]).unwrap();
1415+
let rf = Ref::try_into_ref(r).unwrap();
1416+
assert_eq!(rf, &0u64);
1417+
assert_eq!((rf as *const u64).addr(), buf_addr);
1418+
1419+
let r = Ref::<_, u64>::from_bytes(&mut buf.t[..]).unwrap();
1420+
let rf = Ref::try_into_mut(r).unwrap();
1421+
assert_eq!(rf, &mut 0u64);
1422+
assert_eq!((rf as *mut u64).addr(), buf_addr);
1423+
1424+
// invalid source
1425+
1426+
let mut buf = Align::<[u8; 1], u64>::new([42]);
1427+
let buf_addr = (&buf.t as *const [u8; 1]).addr();
1428+
1429+
let r = Ref::<_, bool>::from_bytes(&buf.t[..]).unwrap();
1430+
let re = Ref::try_into_ref(r).unwrap_err();
1431+
assert_eq!(Ref::bytes(&re.into_src()).addr(), buf_addr);
1432+
1433+
let r = Ref::<_, bool>::from_bytes(&mut buf.t[..]).unwrap();
1434+
let re = Ref::try_into_mut(r).unwrap_err();
1435+
assert_eq!(Ref::bytes(&re.into_src()).addr(), buf_addr);
1436+
}
1437+
11351438
#[test]
11361439
fn test_display_debug() {
11371440
let buf = Align::<[u8; 8], u64>::default();

0 commit comments

Comments
 (0)