Skip to content

Commit 9355ddf

Browse files
committed
Add another niche to FixedArray
This is not currently usable, due to rust-lang/rust#119507, but may become useful in future compiler versions
1 parent a7e4d9f commit 9355ddf

File tree

3 files changed

+86
-21
lines changed

3 files changed

+86
-21
lines changed

src/array.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::{borrow::Cow, fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr::NonNull};
22

3-
use crate::length::{InvalidLength, SmallLen, ValidLength};
3+
use crate::length::{InvalidLength, NonZero, SmallLen, ValidLength};
44

55
/// A fixed size array with length provided at creation denoted in a [`ValidLength`], by default [`u32`].
66
///
77
/// See module level documentation for more information.
88
#[repr(packed)]
99
pub struct FixedArray<T, LenT: ValidLength = SmallLen> {
1010
ptr: NonNull<T>,
11-
len: LenT,
11+
len: LenT::NonZero,
1212
}
1313

1414
impl<T, LenT: ValidLength> FixedArray<T, LenT> {
@@ -23,15 +23,24 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
2323
pub fn empty() -> Self {
2424
Self {
2525
ptr: NonNull::dangling(),
26-
len: LenT::ZERO,
26+
len: LenT::DANGLING,
2727
}
2828
}
2929

3030
/// # Safety
31-
/// The `ptr.len()` and `len`'s value must match.
31+
/// If the slice is empty:
32+
/// - `len` must be equal to `LenT::DANGLING`
33+
///
34+
/// If the slice is not empty:
35+
/// - `len` must be equal to `ptr.len()`
3236
#[must_use]
33-
unsafe fn from_box(ptr: Box<[T]>, len: LenT) -> Self {
34-
debug_assert_eq!(ptr.len(), len.to_usize());
37+
unsafe fn from_box(ptr: Box<[T]>, len: LenT::NonZero) -> Self {
38+
#[cfg(debug_assertions)]
39+
if ptr.is_empty() {
40+
assert_eq!(len, LenT::DANGLING);
41+
} else {
42+
assert_eq!(len.into().to_usize(), ptr.len());
43+
}
3544

3645
let array_ptr = Box::into_raw(ptr).cast::<T>();
3746
Self {
@@ -41,7 +50,11 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
4150
}
4251

4352
pub(crate) fn small_len(&self) -> LenT {
44-
self.len
53+
if { self.ptr } == NonNull::dangling() {
54+
LenT::ZERO
55+
} else {
56+
self.len.into()
57+
}
4558
}
4659

4760
/// Returns the length of the [`FixedArray`].
@@ -233,7 +246,11 @@ impl<T, LenT: ValidLength> TryFrom<Box<[T]>> for FixedArray<T, LenT> {
233246
));
234247
};
235248

236-
// SAFETY: The length has been derived from the box's length.
249+
let len = LenT::NonZero::new(len).unwrap_or(LenT::DANGLING);
250+
251+
// SAFETY:
252+
// If the length was 0, the above `unwrap_or` has just set the value to `LenT::DANGLING`.
253+
// If the length was not 0, the `len` was derived from the box length.
237254
Ok(unsafe { Self::from_box(boxed_array, len) })
238255
}
239256
}
@@ -259,7 +276,7 @@ impl<T, LenT: ValidLength> From<Vec<T>> for FixedArray<T, LenT> {
259276
fn from(value: Vec<T>) -> Self {
260277
match value.into_boxed_slice().try_into() {
261278
Ok(arr) => arr,
262-
Err(err) => Self::from(truncate_vec(err, LenT::MAX)),
279+
Err(err) => Self::from(truncate_vec(err, LenT::MAX.to_usize())),
263280
}
264281
}
265282
}

src/length.rs

+56-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1+
use std::num::{NonZeroU16, NonZeroU32, NonZeroU8};
2+
13
use crate::inline::get_heap_threshold;
24

35
mod sealed {
4-
pub trait Sealed {}
5-
impl Sealed for u8 {}
6-
impl Sealed for u16 {}
6+
use std::num::{NonZeroU16, NonZeroU32, NonZeroU8};
7+
8+
pub trait LengthSealed {}
9+
impl LengthSealed for u8 {}
10+
impl LengthSealed for u16 {}
711
#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
8-
impl Sealed for u32 {}
12+
impl LengthSealed for u32 {}
13+
14+
pub trait NonZeroSealed {}
15+
impl NonZeroSealed for NonZeroU8 {}
16+
impl NonZeroSealed for NonZeroU16 {}
17+
#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
18+
impl NonZeroSealed for NonZeroU32 {}
919
}
1020

1121
#[derive(Debug)]
@@ -88,14 +98,42 @@ impl TryFrom<InvalidLength<u8>> for InvalidStrLength {
8898
}
8999
}
90100

101+
#[doc(hidden)]
102+
pub trait NonZero<Int: ValidLength>:
103+
sealed::NonZeroSealed + Into<Int> + Sized + Copy + PartialEq + std::fmt::Debug
104+
{
105+
fn new(val: Int) -> Option<Self>;
106+
}
107+
108+
impl NonZero<u8> for NonZeroU8 {
109+
fn new(val: u8) -> Option<Self> {
110+
NonZeroU8::new(val)
111+
}
112+
}
113+
114+
impl NonZero<u16> for NonZeroU16 {
115+
fn new(val: u16) -> Option<Self> {
116+
NonZeroU16::new(val)
117+
}
118+
}
119+
120+
impl NonZero<u32> for NonZeroU32 {
121+
fn new(val: u32) -> Option<Self> {
122+
NonZeroU32::new(val)
123+
}
124+
}
125+
91126
/// A sealed trait to represent valid lengths for a [`FixedArray`].
92127
///
93128
/// This is implemented on `u32` for non-16 bit platforms, and `u16` on all platforms.
94129
///
95130
/// [`FixedArray`]: `crate::array::FixedArray`
96-
pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u32> {
131+
pub trait ValidLength: sealed::LengthSealed + Copy + TryFrom<usize> + Into<u32> {
97132
const ZERO: Self;
98-
const MAX: usize;
133+
const MAX: Self;
134+
const DANGLING: Self::NonZero;
135+
136+
type NonZero: NonZero<Self>;
99137
#[cfg(feature = "typesize")]
100138
type InlineStrRepr: Copy + AsRef<[u8]> + AsMut<[u8]> + Default + typesize::TypeSize;
101139
#[cfg(not(feature = "typesize"))]
@@ -112,8 +150,10 @@ pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u
112150

113151
impl ValidLength for u8 {
114152
const ZERO: Self = 0;
115-
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
116-
const MAX: usize = u8::MAX as usize;
153+
const MAX: Self = Self::MAX;
154+
const DANGLING: Self::NonZero = Self::NonZero::MAX;
155+
156+
type NonZero = NonZeroU8;
117157
type InlineStrRepr = [u8; get_heap_threshold::<Self>()];
118158

119159
fn to_usize(self) -> usize {
@@ -123,8 +163,10 @@ impl ValidLength for u8 {
123163

124164
impl ValidLength for u16 {
125165
const ZERO: Self = 0;
126-
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
127-
const MAX: usize = u16::MAX as usize;
166+
const MAX: Self = Self::MAX;
167+
const DANGLING: Self::NonZero = Self::NonZero::MAX;
168+
169+
type NonZero = NonZeroU16;
128170
type InlineStrRepr = [u8; get_heap_threshold::<Self>()];
129171

130172
fn to_usize(self) -> usize {
@@ -135,8 +177,10 @@ impl ValidLength for u16 {
135177
#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
136178
impl ValidLength for u32 {
137179
const ZERO: Self = 0;
138-
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
139-
const MAX: usize = u32::MAX as usize;
180+
const MAX: Self = Self::MAX;
181+
const DANGLING: Self::NonZero = Self::NonZero::MAX;
182+
183+
type NonZero = NonZeroU32;
140184
type InlineStrRepr = [u8; get_heap_threshold::<Self>()];
141185

142186
fn to_usize(self) -> usize {

src/string.rs

+4
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,13 @@ mod test {
269269

270270
#[test]
271271
fn check_sizes() {
272+
type DoubleOpt<T> = Option<Option<T>>;
273+
272274
assert_eq!(std::mem::size_of::<Option<InlineString<[u8; 11]>>>(), 12);
273275
assert_eq!(std::mem::align_of::<Option<InlineString<[u8; 11]>>>(), 1);
274276
assert_eq!(std::mem::size_of::<Option<FixedArray<u8, u32>>>(), 12);
277+
// https://github.com/rust-lang/rust/issues/119507
278+
assert_eq!(std::mem::size_of::<DoubleOpt<FixedArray<u8, u32>>>(), 13);
275279
assert_eq!(std::mem::align_of::<Option<FixedArray<u8, u32>>>(), 1);
276280
// This sucks!! I want to fix this, soon.... this should so niche somehow.
277281
assert_eq!(std::mem::size_of::<FixedStringRepr<u32>>(), 13);

0 commit comments

Comments
 (0)