Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 156 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,94 @@ pub unsafe trait FromBytes: FromZeroes {
where
Self: Sized;

/// Interprets the given `bytes` as a `&Self` without copying.
///
/// If `bytes.len() != size_of::<T>()` or `bytes` is not aligned to
/// `align_of::<T>()`, this returns `None`.
#[inline]
fn ref_from(bytes: &[u8]) -> Option<&Self>
where
Self: Sized,
{
Ref::<&[u8], Self>::new(bytes).map(Ref::into_ref)
}

/// Interprets the prefix of the given `bytes` as a `&Self` without copying.
///
/// `ref_from_prefix` returns a reference to the first `size_of::<Self>()`
/// bytes of `bytes`. If `bytes.len() != size_of::<T>()` or `bytes` is not
/// aligned to `align_of::<T>()`, this returns `None`.
///
/// To also access the prefix bytes, use [`Ref::new_from_prefix`]. Then,
/// use [`Ref::into_ref`] to get a `&Self` with the same lifetime.
#[inline]
fn ref_from_prefix(bytes: &[u8]) -> Option<&Self>
where
Self: Sized,
{
Ref::<&[u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_ref())
}

/// Interprets the suffix of the given `bytes` as a `&Self` without copying.
///
/// `ref_from_suffix` returns a reference to the last `size_of::<Self>()`
/// bytes of `bytes`. If `bytes.len() != size_of::<T>()` or the suffix of
/// `bytes` is not aligned to `align_of::<T>()`, this returns `None`.
///
/// To also access the suffix bytes, use [`Ref::new_from_suffix`]. Then,
/// use [`Ref::into_ref`] to get a `&Self` with the same lifetime.
#[inline]
fn ref_from_suffix(bytes: &[u8]) -> Option<&Self>
where
Self: Sized,
{
Ref::<&[u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_ref())
}

/// Interprets the given `bytes` as a `&mut Self` without copying.
///
/// If `bytes.len() != size_of::<T>()` or `bytes` is not aligned to
/// `align_of::<T>()`, this returns `None`.
#[inline]
fn mut_from(bytes: &mut [u8]) -> Option<&mut Self>
where
Self: Sized + AsBytes,
{
Ref::<&mut [u8], Self>::new(bytes).map(Ref::into_mut)
}

/// Interprets the prefix of the given `bytes` as a `&mut Self` without copying.
///
/// `mut_from_prefix` returns a reference to the first `size_of::<Self>()`
/// bytes of `bytes`. If `bytes.len() != size_of::<T>()` or `bytes` is not
/// aligned to `align_of::<T>()`, this returns `None`.
///
/// To also access the prefix bytes, use [`Ref::new_from_prefix`]. Then,
/// use [`Ref::into_mut`] to get a `&mut Self` with the same lifetime.
#[inline]
fn mut_from_prefix(bytes: &mut [u8]) -> Option<&mut Self>
where
Self: Sized + AsBytes,
{
Ref::<&mut [u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_mut())
}

/// Interprets the suffix of the given `bytes` as a `&mut Self` without copying.
///
/// `mut_from_suffix` returns a reference to the last `size_of::<Self>()`
/// bytes of `bytes`. If `bytes.len() != size_of::<T>()` or the suffix of
/// `bytes` is not aligned to `align_of::<T>()`, this returns `None`.
///
/// To also access the suffix bytes, use [`Ref::new_from_suffix`]. Then,
/// use [`Ref::into_mut`] to get a `&mut Self` with the same lifetime.
#[inline]
fn mut_from_suffix(bytes: &mut [u8]) -> Option<&mut Self>
where
Self: Sized + AsBytes,
{
Ref::<&mut [u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_mut())
}

/// Reads a copy of `Self` from `bytes`.
///
/// If `bytes.len() != size_of::<Self>()`, `read_from` returns `None`.
Expand All @@ -1017,8 +1105,7 @@ pub unsafe trait FromBytes: FromZeroes {
where
Self: Sized,
{
let r = Ref::<_, Unalign<Self>>::new_unaligned(bytes)?;
Some(r.read().into_inner())
Ref::<_, Unalign<Self>>::new_unaligned(bytes).map(|r| r.read().into_inner())
}

/// Reads a copy of `Self` from the prefix of `bytes`.
Expand All @@ -1031,8 +1118,8 @@ pub unsafe trait FromBytes: FromZeroes {
where
Self: Sized,
{
let (r, _suffix) = Ref::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes)?;
Some(r.read().into_inner())
Ref::<_, Unalign<Self>>::new_unaligned_from_prefix(bytes)
.map(|(r, _)| r.read().into_inner())
}

/// Reads a copy of `Self` from the suffix of `bytes`.
Expand All @@ -1045,8 +1132,8 @@ pub unsafe trait FromBytes: FromZeroes {
where
Self: Sized,
{
let (_prefix, r) = Ref::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes)?;
Some(r.read().into_inner())
Ref::<_, Unalign<Self>>::new_unaligned_from_suffix(bytes)
.map(|(_, r)| r.read().into_inner())
}
}

Expand Down Expand Up @@ -4453,6 +4540,69 @@ mod tests {
}
}

#[test]
fn test_ref_from_mut_from() {
// Test `FromBytes::{ref_from, mut_from}{,_prefix,_suffix}` success cases
// Exhaustive coverage for these methods is covered by the `Ref` tests above,
// which these helper methods defer to.

let mut buf =
Align::<[u8; 16], AU64>::new([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
Comment thread
kupiakos marked this conversation as resolved.

assert_eq!(
AU64::ref_from(&buf.t[8..]).unwrap().0.to_ne_bytes(),
[8, 9, 10, 11, 12, 13, 14, 15]
);
let suffix = AU64::mut_from(&mut buf.t[8..]).unwrap();
suffix.0 = 0x0101010101010101;
assert_eq!(<[u8; 9]>::ref_from_suffix(&buf.t[..]).unwrap(), &[7u8, 1, 1, 1, 1, 1, 1, 1, 1]);
let suffix = AU64::mut_from_suffix(&mut buf.t[1..]).unwrap();
suffix.0 = 0x0202020202020202;
<[u8; 10]>::mut_from_suffix(&mut buf.t[..]).unwrap()[0] = 42;
assert_eq!(<[u8; 9]>::ref_from_prefix(&buf.t[..]).unwrap(), &[0, 1, 2, 3, 4, 5, 42, 7, 2]);
<[u8; 2]>::mut_from_prefix(&mut buf.t[..]).unwrap()[1] = 30;
assert_eq!(buf.t, [0, 30, 2, 3, 4, 5, 42, 7, 2, 2, 2, 2, 2, 2, 2, 2]);
}

#[test]
fn test_ref_from_mut_from_error() {
// Test `FromBytes::{ref_from, mut_from}{,_prefix,_suffix}` error cases.

// Fail because the buffer is too large.
let mut buf = Align::<[u8; 16], AU64>::default();
// `buf.t` should be aligned to 8, so only the length check should fail.
assert!(AU64::ref_from(&buf.t[..]).is_none());
assert!(AU64::mut_from(&mut buf.t[..]).is_none());
assert!(<[u8; 8]>::ref_from(&buf.t[..]).is_none());
assert!(<[u8; 8]>::mut_from(&mut buf.t[..]).is_none());

// Fail because the buffer is too small.
let mut buf = Align::<[u8; 4], AU64>::default();
assert!(AU64::ref_from(&buf.t[..]).is_none());
assert!(AU64::mut_from(&mut buf.t[..]).is_none());
assert!(<[u8; 8]>::ref_from(&buf.t[..]).is_none());
assert!(<[u8; 8]>::mut_from(&mut buf.t[..]).is_none());
assert!(AU64::ref_from_prefix(&buf.t[..]).is_none());
assert!(AU64::mut_from_prefix(&mut buf.t[..]).is_none());
assert!(AU64::ref_from_suffix(&buf.t[..]).is_none());
assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_none());
assert!(<[u8; 8]>::ref_from_prefix(&buf.t[..]).is_none());
assert!(<[u8; 8]>::mut_from_prefix(&mut buf.t[..]).is_none());
assert!(<[u8; 8]>::ref_from_suffix(&buf.t[..]).is_none());
assert!(<[u8; 8]>::mut_from_suffix(&mut buf.t[..]).is_none());

// Fail because the alignment is insufficient.
let mut buf = Align::<[u8; 13], AU64>::default();
assert!(AU64::ref_from(&buf.t[1..]).is_none());
assert!(AU64::mut_from(&mut buf.t[1..]).is_none());
assert!(AU64::ref_from(&buf.t[1..]).is_none());
assert!(AU64::mut_from(&mut buf.t[1..]).is_none());
assert!(AU64::ref_from_prefix(&buf.t[1..]).is_none());
assert!(AU64::mut_from_prefix(&mut buf.t[1..]).is_none());
assert!(AU64::ref_from_suffix(&buf.t[..]).is_none());
assert!(AU64::mut_from_suffix(&mut buf.t[..]).is_none());
}

#[test]
#[allow(clippy::cognitive_complexity)]
fn test_new_error() {
Expand Down