From b9330db6fad0dc30f25a47527816e3633f078e42 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 1 Aug 2026 16:43:00 +0200 Subject: [PATCH] prepare for 2024 edition compat --- src/read.rs | 126 ++++++++++++++++++++++------------------------ src/readbuffer.rs | 39 +++++++------- src/readstream.rs | 20 ++++---- 3 files changed, 90 insertions(+), 95 deletions(-) diff --git a/src/read.rs b/src/read.rs index 3c51b85..223da53 100644 --- a/src/read.rs +++ b/src/read.rs @@ -96,6 +96,8 @@ pub trait BitRead<'a, E: Endianness>: Sized { /// Note: only the bounds are unchecked /// /// any other validations (e.g. checking for valid utf8) still needs to be done + /// + /// SAFETY: the caller is required to check the bounds of the read #[doc(hidden)] #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, _end: bool) -> Result { @@ -131,7 +133,7 @@ macro_rules! impl_read_int { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream, end: bool) -> Result<$type> { - Ok(stream.read_int_unchecked::<$type>(<$type>::BITS as usize, end)) + Ok(unsafe { stream.read_int_unchecked::<$type>(<$type>::BITS as usize, end) }) } #[inline] @@ -155,9 +157,9 @@ macro_rules! impl_read_int_nonzero { stream: &mut BitReadStream, end: bool, ) -> Result { - Ok(<$type>::new( - stream.read_int_unchecked(size_of::<$type>() * 8, end), - )) + Ok(<$type>::new(unsafe { + stream.read_int_unchecked(size_of::<$type>() * 8, end) + })) } #[inline] @@ -177,9 +179,9 @@ macro_rules! impl_read_int_nonzero { stream: &mut BitReadStream, end: bool, ) -> Result { - Ok(<$type>::new( - stream.read_int_unchecked(size_of::<$type>() * 8, end), - )) + Ok(<$type>::new(unsafe { + stream.read_int_unchecked(size_of::<$type>() * 8, end) + })) } #[inline] @@ -215,7 +217,7 @@ impl BitRead<'_, E> for f32 { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream, end: bool) -> Result { - Ok(stream.read_float_unchecked::(end)) + Ok(unsafe { stream.read_float_unchecked::(end) }) } #[inline] @@ -232,7 +234,7 @@ impl BitRead<'_, E> for f64 { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream, end: bool) -> Result { - Ok(stream.read_float_unchecked::(end)) + Ok(unsafe { stream.read_float_unchecked::(end) }) } #[inline] @@ -249,7 +251,7 @@ impl BitRead<'_, E> for bool { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream, _end: bool) -> Result { - Ok(stream.read_bool_unchecked()) + Ok(unsafe { stream.read_bool_unchecked() }) } #[inline] @@ -280,7 +282,7 @@ impl<'a, E: Endianness, T: BitRead<'a, E>> BitRead<'a, E> for Rc { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, end: bool) -> Result { - Ok(Rc::new(T::read_unchecked(stream, end)?)) + Ok(Rc::new(unsafe { T::read_unchecked(stream, end)? })) } #[inline] @@ -297,7 +299,7 @@ impl<'a, E: Endianness, T: BitRead<'a, E>> BitRead<'a, E> for Arc { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, end: bool) -> Result { - Ok(Arc::new(T::read_unchecked(stream, end)?)) + Ok(Arc::new(unsafe { T::read_unchecked(stream, end)? })) } #[inline] @@ -314,7 +316,7 @@ impl<'a, E: Endianness, T: BitRead<'a, E>> BitRead<'a, E> for Box { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, end: bool) -> Result { - Ok(Box::new(T::read_unchecked(stream, end)?)) + Ok(Box::new(unsafe { T::read_unchecked(stream, end)? })) } #[inline] @@ -333,7 +335,7 @@ macro_rules! impl_read_tuple { #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, end: bool) -> Result { - Ok(($(<$type>::read_unchecked(stream, end)?),*)) + Ok(($(unsafe {<$type>::read_unchecked(stream, end)?}),*)) } #[inline] @@ -348,6 +350,31 @@ impl_read_tuple!(T1, T2); impl_read_tuple!(T1, T2, T3); impl_read_tuple!(T1, T2, T3, T4); +/// Extracts the values from an array of `MaybeUninit` containers. +/// +/// # Safety +/// +/// It is up to the caller to guarantee that all elements of the array are +/// in an initialized state. +unsafe fn assume_init_array(maybe_uninit: [MaybeUninit; N]) -> [T; N] { + // to be replaced with `From<[MaybeUninit; N]> for MaybeUninit<[T; N]>` and a normal `assume_init` + // once the MSRV is 1.95 + unsafe { (&maybe_uninit as *const _ as *const [T; N]).read() } +} + +/// Read an array item by item +fn ready_array( + mut read_item: impl FnMut() -> Result, +) -> Result<[T; N], E> { + let mut array = [const { >::uninit() }; N]; + for item in array.iter_mut() { + let val = read_item()?; + item.write(val); + } + // SAFETY: we initialized all items in the loop + Ok(unsafe { assume_init_array(array) }) +} + impl<'a, E: Endianness, T: BitRead<'a, E>, const N: usize> BitRead<'a, E> for [T; N] { #[inline] fn read(stream: &mut BitReadStream<'a, E>) -> Result { @@ -356,34 +383,16 @@ impl<'a, E: Endianness, T: BitRead<'a, E>, const N: usize> BitRead<'a, E> for [T let end = stream.check_read(bit_size * N)?; unsafe { Self::read_unchecked(stream, end) } } - None => { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - let mut array = - unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }; - for item in array.iter_mut() { - unsafe { - // length is already checked - let val = stream.read()?; - item.as_mut_ptr().write(val) - } - } - unsafe { Ok((&array as *const _ as *const [T; N]).read()) } - } + None => ready_array(|| stream.read()), } } #[inline] unsafe fn read_unchecked(stream: &mut BitReadStream<'a, E>, end: bool) -> Result { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - let mut array = MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init(); - - for item in array.iter_mut() { - // length is already checked - let val = stream.read_unchecked(end)?; - item.as_mut_ptr().write(val); - } - - Ok((&array as *const _ as *const [T; N]).read()) + ready_array(|| { + // SAFETY: the caller is required to have checked the bounds + unsafe { stream.read_unchecked(end) } + }) } #[inline] @@ -459,6 +468,7 @@ pub trait BitReadSized<'a, E: Endianness>: Sized { /// Read the type from stream fn read(stream: &mut BitReadStream<'a, E>, size: usize) -> Result; + /// SAFETY: the caller is required to check the bounds of the read #[doc(hidden)] #[inline] unsafe fn read_unchecked( @@ -502,7 +512,7 @@ macro_rules! impl_read_int_sized { size: usize, end: bool, ) -> Result<$type> { - Ok(stream.read_int_unchecked::<$type>(size, end)) + Ok(unsafe { stream.read_int_unchecked::<$type>(size, end) }) } #[inline] @@ -626,7 +636,7 @@ impl<'a, E: Endianness, T: BitRead<'a, E>> BitReadSized<'a, E> for Vec { ) -> Result { let mut vec = Vec::with_capacity(min(size, 128)); for _ in 0..size { - vec.push(stream.read_unchecked(end)?) + vec.push(unsafe { stream.read_unchecked(end)? }) } Ok(vec) } @@ -668,8 +678,8 @@ impl<'a, E: Endianness, K: BitRead<'a, E> + Eq + Hash, T: BitRead<'a, E>> BitRea ) -> Result { let mut map = HashMap::with_capacity(min(size, 128)); for _ in 0..size { - let key = stream.read_unchecked(end)?; - let value = stream.read_unchecked(end)?; + let key = unsafe { stream.read_unchecked(end)? }; + let value = unsafe { stream.read_unchecked(end)? }; map.insert(key, value); } Ok(map) @@ -765,7 +775,7 @@ impl<'a, E: Endianness, T: BitReadSized<'a, E>> BitReadSized<'a, E> for Arc { size: usize, end: bool, ) -> Result { - Ok(Arc::new(T::read_unchecked(stream, size, end)?)) + Ok(Arc::new(unsafe { T::read_unchecked(stream, size, end)? })) } #[inline] @@ -786,7 +796,7 @@ impl<'a, E: Endianness, T: BitReadSized<'a, E>> BitReadSized<'a, E> for Rc { size: usize, end: bool, ) -> Result { - Ok(Rc::new(T::read_unchecked(stream, size, end)?)) + Ok(Rc::new(unsafe { T::read_unchecked(stream, size, end)? })) } #[inline] @@ -807,7 +817,7 @@ impl<'a, E: Endianness, T: BitReadSized<'a, E>> BitReadSized<'a, E> for Box { size: usize, end: bool, ) -> Result { - Ok(Box::new(T::read_unchecked(stream, size, end)?)) + Ok(Box::new(unsafe { T::read_unchecked(stream, size, end)? })) } #[inline] @@ -824,19 +834,7 @@ impl<'a, E: Endianness, T: BitReadSized<'a, E>, const N: usize> BitReadSized<'a, let end = stream.check_read(bit_size * N)?; unsafe { Self::read_unchecked(stream, size, end) } } - None => { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - let mut array = - unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }; - for item in array.iter_mut() { - unsafe { - // length is already checked - let val = stream.read_sized(size)?; - item.as_mut_ptr().write(val) - } - } - unsafe { Ok((&array as *const _ as *const [T; N]).read()) } - } + None => ready_array(|| stream.read_sized(size)), } } @@ -846,16 +844,10 @@ impl<'a, E: Endianness, T: BitReadSized<'a, E>, const N: usize> BitReadSized<'a, size: usize, end: bool, ) -> Result { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - let mut array = MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init(); - - for item in array.iter_mut() { - // length is already checked - let val = stream.read_sized_unchecked(size, end)?; - item.as_mut_ptr().write(val); - } - - Ok((&array as *const _ as *const [T; N]).read()) + ready_array(|| { + // SAFETY: the caller is required to have checked the bounds + unsafe { stream.read_sized_unchecked(size, end) } + }) } #[inline] diff --git a/src/readbuffer.rs b/src/readbuffer.rs index b5110d8..0b1a101 100644 --- a/src/readbuffer.rs +++ b/src/readbuffer.rs @@ -243,24 +243,27 @@ where if end { let mut bytes = [0; USIZE_SIZE]; let count = min(USIZE_SIZE, self.slice.len() - byte_index); - bytes[0..count] - .copy_from_slice(self.slice.get_unchecked(byte_index..byte_index + count)); + bytes[0..count].copy_from_slice(unsafe { + self.slice.get_unchecked(byte_index..byte_index + count) + }); bytes } else { debug_assert!(byte_index + USIZE_SIZE <= self.slice.len()); // this is safe because all calling paths check that byte_index is less than the unpadded // length (because they check based on bit_len), so with padding byte_index + USIZE_SIZE is // always within bounds - self.slice - .get_unchecked(byte_index..byte_index + USIZE_SIZE) - .try_into() - .unwrap() + unsafe { + self.slice + .get_unchecked(byte_index..byte_index + USIZE_SIZE) + .try_into() + .unwrap() + } } } /// note that only the bottom USIZE - 1 bytes are usable unsafe fn read_shifted_usize(&self, byte_index: usize, shift: usize, end: bool) -> usize { - let raw_bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index, end); + let raw_bytes: [u8; USIZE_SIZE] = unsafe { self.read_usize_bytes(byte_index, end) }; let raw_usize: usize = usize::from_le_bytes(raw_bytes); raw_usize >> shift } @@ -269,7 +272,7 @@ where let byte_index = position / 8; let bit_offset = position & 7; - let bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index, end); + let bytes: [u8; USIZE_SIZE] = unsafe { self.read_usize_bytes(byte_index, end) }; let container = if E::is_le() { usize::from_le_bytes(bytes) @@ -339,7 +342,7 @@ where let byte_index = position / 8; let bit_offset = position & 7; - let byte = self.slice.get_unchecked(byte_index); + let byte = unsafe { self.slice.get_unchecked(byte_index) }; if E::is_le() { let shifted = byte >> bit_offset; shifted & 1u8 == 1 @@ -410,9 +413,9 @@ where let fit_usize = count + bit_offset < usize::BITS as usize; let value = if fit_usize { - self.read_fit_usize(position, count, end) + unsafe { self.read_fit_usize(position, count, end) } } else { - self.read_no_fit_usize(position, count, end) + unsafe { self.read_no_fit_usize(position, count, end) } }; if count == type_bit_size { @@ -427,7 +430,7 @@ where where T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt, { - let raw = self.read_usize(position, count, end); + let raw = unsafe { self.read_usize(position, count, end) }; T::from_unchecked(raw) } @@ -443,7 +446,7 @@ where while left_to_read > 0 { let bits_left = self.bit_len() - read_pos; let read = min(min(left_to_read, max_read), bits_left); - let data = T::from_unchecked(self.read_usize(read_pos, read, end)); + let data = T::from_unchecked(unsafe { self.read_usize(read_pos, read, end) }); if E::is_le() { acc |= data << bit_offset; } else { @@ -528,7 +531,7 @@ where if E::is_le() { while byte_left > USIZE_SIZE - 1 { - let raw = self.read_shifted_usize(read_pos, shift, false); + let raw = unsafe { self.read_shifted_usize(read_pos, shift, false) }; let bytes = raw.to_le_bytes(); let read_bytes = USIZE_SIZE - 1; let usable_bytes = &bytes[0..read_bytes]; @@ -538,13 +541,13 @@ where byte_left -= read_bytes; } - let bytes = self.read_shifted_usize(read_pos, shift, true).to_le_bytes(); + let bytes = unsafe { self.read_shifted_usize(read_pos, shift, true).to_le_bytes() }; let usable_bytes = &bytes[0..byte_left]; output.extend_from_slice(usable_bytes); } else { let mut pos = position; while byte_left > 0 { - output.push(self.read_int_unchecked::(pos, 8, true)); + output.push(unsafe { self.read_int_unchecked::(pos, 8, true) }); byte_left -= 1; pos += 8; } @@ -562,7 +565,7 @@ where } let mut output = Vec::with_capacity(byte_count); - self.read_bytes_unchecked_owned(position, byte_count, &mut output); + unsafe { self.read_bytes_unchecked_owned(position, byte_count, &mut output) }; Cow::Owned(output) } @@ -770,7 +773,7 @@ where .unwrap(); T::from_bytes::(bytes) } else { - let int = self.read_int_unchecked(position, size_of::() * 8, end); + let int = unsafe { self.read_int_unchecked(position, size_of::() * 8, end) }; T::from_int(int) } } diff --git a/src/readstream.rs b/src/readstream.rs index 69de89d..c26d5aa 100644 --- a/src/readstream.rs +++ b/src/readstream.rs @@ -104,7 +104,7 @@ where #[doc(hidden)] #[inline] pub unsafe fn read_bool_unchecked(&mut self) -> bool { - let result = self.buffer.read_bool_unchecked(self.pos); + let result = unsafe { self.buffer.read_bool_unchecked(self.pos) }; self.pos += 1; result } @@ -156,7 +156,7 @@ where where T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + WrappingSub, { - let result = self.buffer.read_int_unchecked(self.pos, count, end); + let result = unsafe { self.buffer.read_int_unchecked(self.pos, count, end) }; self.pos += count; result } @@ -207,7 +207,7 @@ where T: Float + UncheckedPrimitiveFloat, { let count = size_of::() * 8; - let result = self.buffer.read_float_unchecked(self.pos, end); + let result = unsafe { self.buffer.read_float_unchecked(self.pos, end) }; self.pos += count; result } @@ -255,7 +255,7 @@ where #[inline] pub unsafe fn read_bytes_unchecked(&mut self, byte_count: usize) -> Cow<'a, [u8]> { let count = byte_count * 8; - let result = self.buffer.read_bytes_unchecked(self.pos, byte_count); + let result = unsafe { self.buffer.read_bytes_unchecked(self.pos, byte_count) }; self.pos += count; result } @@ -623,7 +623,7 @@ where #[doc(hidden)] #[inline] pub unsafe fn read_unchecked>(&mut self, end: bool) -> Result { - T::read_unchecked(self, end) + unsafe { T::read_unchecked(self, end) } } /// Read a value based on the provided type and size @@ -695,7 +695,7 @@ where size: usize, end: bool, ) -> Result { - T::read_unchecked(self, size, end) + unsafe { T::read_unchecked(self, size, end) } } /// Check if we can read a number of bits from the stream @@ -862,14 +862,14 @@ impl<'a, E: Endianness> schemars::JsonSchema for BitReadStream<'a, E> { "BitReadStream".into() } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema { #[derive(schemars::JsonSchema)] #[allow(dead_code)] struct StreamSchema { data: Vec, bit_length: usize, } - StreamSchema::json_schema(gen) + StreamSchema::json_schema(generator) } } @@ -879,7 +879,7 @@ impl<'a, E: Endianness> schemars_1::JsonSchema for BitReadStream<'a, E> { "BitReadStream".into() } - fn json_schema(gen: &mut schemars_1::SchemaGenerator) -> schemars_1::Schema { + fn json_schema(generator: &mut schemars_1::SchemaGenerator) -> schemars_1::Schema { use schemars_1 as schemars; #[derive(schemars_1::JsonSchema)] @@ -888,6 +888,6 @@ impl<'a, E: Endianness> schemars_1::JsonSchema for BitReadStream<'a, E> { data: Vec, bit_length: usize, } - StreamSchema::json_schema(gen) + StreamSchema::json_schema(generator) } }