From 8309e19a6866e0a0d1513c96e5679ca6592f82f1 Mon Sep 17 00:00:00 2001 From: pantonshire Date: Sun, 18 Sep 2022 21:21:45 +0100 Subject: [PATCH] strings: remove wide pointer cast in CappedString impl `CappedString::as_bytes` previously used a wide pointer cast to obtain a `&[u8]` from the `[MaybeUninit; N]` buffer; it cast a `*const [MaybeUninit]` to a `*const [u8]` as an intermediate step. Although this seems to be valid and did not cause any UB detected by MIRI, it seems to be generally accepted that `slice::from_raw_parts` is the preferred way to transmute slices since it makes explicit the metadata (length in this case) of the new wide pointer. This is in contrast to casting with `as`, which implicitly copies the metadata from the old wide pointer into the new one. Therefore, this patch replaces the `as *const [u8]` conversion with a call to `slice::from_raw_parts`. --- src/strings/capped.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/strings/capped.rs b/src/strings/capped.rs index 2625108..7832800 100644 --- a/src/strings/capped.rs +++ b/src/strings/capped.rs @@ -4,7 +4,7 @@ use core::{ fmt, hash::{Hash, Hasher}, mem::MaybeUninit, - ops, ptr, str, + ops, ptr, slice, str, }; #[cfg(not(feature = "std"))] @@ -443,17 +443,16 @@ impl CappedString { #[inline] #[must_use] pub fn as_bytes(&self) -> &[u8] { - // Get the slice of the buffer containing initialised string data. - // SAFETY: - // It is an invariant of `CappedString` that `self.len <= N`, so `..self.len` is a valid - // range over `self.buf`. - let data_slice = unsafe { self.buf.get_unchecked(..usize::from(self.len)) }; + // Get a pointer to the start of the buffer and convert it from a `*const MaybeUninit` + // to a `*const u8`. This conversion is valid because `MaybeUninit` has the same memory + // layout as `u8`. + let data_ptr = self.buf.as_ptr() as *const u8; - // Convert the `&[MaybeUninit]` to a `&[u8]`. // SAFETY: - // `MaybeUninit` has the same memory layout as `u8`, and the first `self.len` bytes of - // the buffer are initialised, so this conversion is valid. - unsafe { &*(data_slice as *const [MaybeUninit] as *const [u8]) } + // It is an invariant of `CappedString` that the first `self.len` bytes of the buffer are + // initialised, so `data_ptr` is valid for reads of `self.len` bytes. `data_ptr` is + // trivially properly aligned, since `u8` has an alignment of 1. + unsafe { slice::from_raw_parts(data_ptr, usize::from(self.len)) } } /// # Safety