Skip to content

Commit 010a418

Browse files
committed
Get rid of NonEmptyArray
This was inhibiting niche optimisations due to rust-lang/rust#119507
1 parent 295791a commit 010a418

File tree

4 files changed

+79
-159
lines changed

4 files changed

+79
-159
lines changed

src/array.rs

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
use std::{fmt::Debug, hash::Hash};
1+
use std::{fmt::Debug, hash::Hash, mem::ManuallyDrop, ptr::NonNull};
22

33
use crate::{
44
length::{InvalidLength, SmallLen, ValidLength},
55
logging::error,
6-
non_empty_array::NonEmptyFixedArray,
76
};
87

98
/// A fixed size array with length provided at creation denoted in a [`ValidLength`], by default [`u32`].
109
///
1110
/// See module level documentation for more information.
12-
#[derive(Clone)]
13-
pub struct FixedArray<T, LenT: ValidLength = SmallLen>(Option<NonEmptyFixedArray<T, LenT>>);
11+
pub struct FixedArray<T, LenT: ValidLength = SmallLen> {
12+
ptr: NonNull<T>,
13+
len: LenT,
14+
}
1415

1516
impl<T, LenT: ValidLength> FixedArray<T, LenT> {
1617
/// Alias to [`FixedArray::empty`].
@@ -22,14 +23,14 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
2223
/// Creates a new, empty [`FixedArray`] that cannot be pushed to.
2324
#[must_use]
2425
pub fn empty() -> Self {
25-
Self(None)
26+
Self {
27+
ptr: NonNull::dangling(),
28+
len: LenT::ZERO,
29+
}
2630
}
2731

2832
pub(crate) fn small_len(&self) -> LenT {
29-
self.0
30-
.as_ref()
31-
.map(NonEmptyFixedArray::small_len)
32-
.unwrap_or_default()
33+
self.len
3334
}
3435

3536
/// Returns the length of the [`FixedArray`].
@@ -41,7 +42,7 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
4142
/// Returns if the length is equal to 0.
4243
#[must_use]
4344
pub fn is_empty(&self) -> bool {
44-
self.0.is_none()
45+
self.len() == 0
4546
}
4647

4748
/// Converts [`FixedArray<T>`] to [`Vec<T>`], this operation should be cheap.
@@ -67,31 +68,56 @@ impl<T, LenT: ValidLength> FixedArray<T, LenT> {
6768
pub fn as_slice_mut(&mut self) -> &mut [T] {
6869
self
6970
}
70-
}
7171

72-
impl<T, LenT: ValidLength> Default for FixedArray<T, LenT> {
73-
/// Creates a new, empty [`FixedArray`] that cannot be pushed to.
74-
fn default() -> Self {
75-
Self::empty()
72+
/// Converts the [`FixedArray`] to it's original [`Box<T>`].
73+
///
74+
/// # Safety
75+
/// `self` must never be used again, and it is highly recommended to wrap in [`ManuallyDrop`] before calling.
76+
pub(crate) unsafe fn as_box(&mut self) -> Box<[T]> {
77+
let slice = self.as_slice_mut();
78+
79+
// SAFETY: `self` has been derived from `Box<[T]>`
80+
unsafe { Box::from_raw(slice) }
7681
}
7782
}
7883

84+
unsafe impl<T: Send, LenT: ValidLength> Send for FixedArray<T, LenT> {}
85+
unsafe impl<T: Sync, LenT: ValidLength> Sync for FixedArray<T, LenT> {}
86+
7987
impl<T, LenT: ValidLength> std::ops::Deref for FixedArray<T, LenT> {
8088
type Target = [T];
8189
fn deref(&self) -> &Self::Target {
82-
self.0
83-
.as_ref()
84-
.map(NonEmptyFixedArray::as_slice)
85-
.unwrap_or_default()
90+
// SAFETY: `self.ptr` and `self.len` are both valid and derived from `Box<[T]>`.
91+
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.small_len().to_usize()) }
8692
}
8793
}
8894

8995
impl<T, LenT: ValidLength> std::ops::DerefMut for FixedArray<T, LenT> {
9096
fn deref_mut(&mut self) -> &mut Self::Target {
91-
self.0
92-
.as_mut()
93-
.map(NonEmptyFixedArray::as_mut_slice)
94-
.unwrap_or_default()
97+
// SAFETY: `self.ptr` and `self.len` are both valid and derived from `Box<[T]>`.
98+
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.small_len().to_usize()) }
99+
}
100+
}
101+
102+
impl<T, LenT: ValidLength> Drop for FixedArray<T, LenT> {
103+
fn drop(&mut self) {
104+
// SAFETY: We never use `self` again, and we are in the drop impl.
105+
unsafe { self.as_box() };
106+
}
107+
}
108+
109+
impl<T, LenT: ValidLength> Default for FixedArray<T, LenT> {
110+
/// Creates a new, empty [`FixedArray`] that cannot be pushed to.
111+
fn default() -> Self {
112+
Self::empty()
113+
}
114+
}
115+
116+
impl<T: Clone, LenT: ValidLength> Clone for FixedArray<T, LenT> {
117+
fn clone(&self) -> Self {
118+
Box::<[T]>::from(self.as_slice())
119+
.try_into()
120+
.unwrap_or_else(|_| panic!("Length of array can't change when cloning"))
95121
}
96122
}
97123

@@ -165,7 +191,10 @@ impl<T, LenT: ValidLength> std::iter::FromIterator<T> for FixedArray<T, LenT> {
165191

166192
impl<T, LenT: ValidLength> From<FixedArray<T, LenT>> for Box<[T]> {
167193
fn from(value: FixedArray<T, LenT>) -> Self {
168-
value.0.map(Box::from).unwrap_or_default()
194+
let mut value = ManuallyDrop::new(value);
195+
196+
// SAFETY: We don't use value again, and it is ManuallyDrop.
197+
unsafe { value.as_box() }
169198
}
170199
}
171200

@@ -178,11 +207,13 @@ impl<T, LenT: ValidLength> From<FixedArray<T, LenT>> for Vec<T> {
178207
impl<T, LenT: ValidLength> TryFrom<Box<[T]>> for FixedArray<T, LenT> {
179208
type Error = InvalidLength<T>;
180209
fn try_from(boxed_array: Box<[T]>) -> Result<Self, Self::Error> {
181-
match NonEmptyFixedArray::try_from(boxed_array) {
182-
Ok(arr) => Ok(Self(Some(arr))),
183-
Err(None) => Ok(Self(None)),
184-
Err(Some(err)) => Err(err),
185-
}
210+
let (len, boxed_array) = LenT::from_usize(boxed_array)?;
211+
let array_ptr = Box::into_raw(boxed_array).cast::<T>();
212+
213+
Ok(Self {
214+
ptr: NonNull::new(array_ptr).expect("Box ptr != nullptr"),
215+
len,
216+
})
186217
}
187218
}
188219

src/length.rs

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::num::{NonZeroU16, NonZeroU32, NonZeroU8};
2-
31
mod sealed {
42
pub trait Sealed {}
53
impl Sealed for u8 {}
@@ -43,58 +41,22 @@ impl<T> std::fmt::Display for InvalidLength<T> {
4341
}
4442
}
4543

46-
pub trait NonZero<T: sealed::Sealed>: Copy {
47-
fn new(val: T) -> Option<Self>;
48-
fn expand(self) -> T;
49-
}
50-
51-
impl NonZero<u8> for NonZeroU8 {
52-
fn new(val: u8) -> Option<Self> {
53-
Self::new(val)
54-
}
55-
56-
fn expand(self) -> u8 {
57-
self.get()
58-
}
59-
}
60-
61-
impl NonZero<u16> for NonZeroU16 {
62-
fn new(val: u16) -> Option<Self> {
63-
Self::new(val)
64-
}
65-
66-
fn expand(self) -> u16 {
67-
self.get()
68-
}
69-
}
70-
71-
impl NonZero<u32> for NonZeroU32 {
72-
fn new(val: u32) -> Option<Self> {
73-
Self::new(val)
74-
}
75-
76-
fn expand(self) -> u32 {
77-
self.get()
78-
}
79-
}
80-
8144
/// A sealed trait to represent valid lengths for a [`FixedArray`].
8245
///
8346
/// This is implemented on `u32` for non-16 bit platforms, and `u16` on all platforms.
8447
///
8548
/// [`FixedArray`]: `crate::array::FixedArray`
8649
pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u32> {
50+
const ZERO: Self;
8751
const MAX: usize;
88-
type NonZero: NonZero<Self>;
8952

9053
/// # Errors
9154
///
9255
/// Errors if the val's length cannot fit into Self.
9356
#[allow(clippy::type_complexity)]
94-
fn from_usize<T>(val: Box<[T]>) -> Result<Option<(Self::NonZero, Box<[T]>)>, InvalidLength<T>> {
95-
match val.len().try_into().map(Self::NonZero::new) {
96-
Ok(None) => Ok(None),
97-
Ok(Some(len)) => Ok(Some((len, val))),
57+
fn from_usize<T>(val: Box<[T]>) -> Result<(Self, Box<[T]>), InvalidLength<T>> {
58+
match val.len().try_into() {
59+
Ok(len) => Ok((len, val)),
9860
Err(_) => Err(InvalidLength::new(std::any::type_name::<Self>(), val)),
9961
}
10062
}
@@ -103,19 +65,19 @@ pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u
10365
}
10466

10567
impl ValidLength for u8 {
68+
const ZERO: Self = 0;
10669
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
10770
const MAX: usize = u8::MAX as usize;
108-
type NonZero = NonZeroU8;
10971

11072
fn to_usize(self) -> usize {
11173
self.into()
11274
}
11375
}
11476

11577
impl ValidLength for u16 {
78+
const ZERO: Self = 0;
11679
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
11780
const MAX: usize = u16::MAX as usize;
118-
type NonZero = NonZeroU16;
11981

12082
fn to_usize(self) -> usize {
12183
self.into()
@@ -124,9 +86,9 @@ impl ValidLength for u16 {
12486

12587
#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
12688
impl ValidLength for u32 {
89+
const ZERO: Self = 0;
12790
#[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
12891
const MAX: usize = u32::MAX as usize;
129-
type NonZero = NonZeroU32;
13092

13193
fn to_usize(self) -> usize {
13294
self.try_into()

src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,19 @@ mod array;
2121
mod length;
2222
mod logging;
2323
mod string;
24-
// Internal only!
25-
mod non_empty_array;
2624

2725
pub use array::FixedArray;
2826
pub use length::ValidLength;
2927
pub use string::FixedString;
28+
29+
#[cfg(test)]
30+
mod test {
31+
use std::mem::size_of;
32+
33+
use crate::FixedString;
34+
35+
#[test]
36+
fn niche_test() {
37+
assert_eq!(size_of::<FixedString>(), size_of::<Option<FixedString>>());
38+
}
39+
}

src/non_empty_array.rs

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)