Skip to content

Commit e69c788

Browse files
authored
Further optimize Clone::clone_from (#128)
1 parent f09cfcc commit e69c788

1 file changed

Lines changed: 58 additions & 27 deletions

File tree

src/lib.rs

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use core::cmp::Ordering;
3434
use core::hash::Hash;
3535
use core::iter::{Chain, FusedIterator};
3636
use core::mem::ManuallyDrop;
37+
use core::mem::MaybeUninit;
3738
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Index};
3839
use core::ptr::NonNull;
3940
pub use range::IndexRange;
@@ -70,7 +71,7 @@ fn vec_into_parts<T>(vec: Vec<T>) -> (NonNull<T>, usize, usize) {
7071
/// [0,1,0].
7172
#[derive(Debug, Eq)]
7273
pub struct FixedBitSet {
73-
pub(crate) data: NonNull<SimdBlock>,
74+
pub(crate) data: NonNull<MaybeUninit<SimdBlock>>,
7475
capacity: usize,
7576
/// length in bits
7677
pub(crate) length: usize,
@@ -104,7 +105,7 @@ impl FixedBitSet {
104105
fn from_blocks_and_len(data: Vec<SimdBlock>, length: usize) -> Self {
105106
let (data, capacity, _) = vec_into_parts(data);
106107
FixedBitSet {
107-
data,
108+
data: data.cast(),
108109
capacity,
109110
length,
110111
}
@@ -134,17 +135,32 @@ impl FixedBitSet {
134135
/// Grow capacity to **bits**, all new bits initialized to zero
135136
#[inline]
136137
pub fn grow(&mut self, bits: usize) {
137-
if bits <= self.length {
138-
return;
138+
#[cold]
139+
#[track_caller]
140+
#[inline(never)]
141+
fn do_grow(slf: &mut FixedBitSet, bits: usize) {
142+
// SAFETY: The provided fill is initialized to NONE.
143+
unsafe { slf.grow_inner(bits, MaybeUninit::new(SimdBlock::NONE)) };
139144
}
145+
146+
if bits > self.length {
147+
do_grow(self, bits);
148+
}
149+
}
150+
151+
/// # Safety
152+
/// If `fill` is uninitialized, the memory must not be accessed and must be immediately
153+
/// written over
154+
#[inline(always)]
155+
unsafe fn grow_inner(&mut self, bits: usize, fill: MaybeUninit<SimdBlock>) {
140156
// SAFETY: The data pointer and capacity were created from a Vec initially. The block
141157
// len is identical to that of the original.
142158
let mut data = unsafe {
143159
Vec::from_raw_parts(self.data.as_ptr(), self.simd_block_len(), self.capacity)
144160
};
145161
let (mut blocks, rem) = div_rem(bits, SimdBlock::BITS);
146162
blocks += (rem > 0) as usize;
147-
data.resize(blocks, SimdBlock::NONE);
163+
data.resize(blocks, fill);
148164
let (data, capacity, _) = vec_into_parts(data);
149165
self.data = data;
150166
self.capacity = capacity;
@@ -184,11 +200,25 @@ impl FixedBitSet {
184200
fn as_simd_slice(&self) -> &[SimdBlock] {
185201
// SAFETY: The slice constructed is within bounds of the underlying allocation. This function
186202
// is called with a read-only borrow so no other write can happen as long as the returned borrow lives.
187-
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.simd_block_len()) }
203+
unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.simd_block_len()) }
188204
}
189205

190206
#[inline]
191207
fn as_mut_simd_slice(&mut self) -> &mut [SimdBlock] {
208+
// SAFETY: The slice constructed is within bounds of the underlying allocation. This function
209+
// is called with a mutable borrow so no other read or write can happen as long as the returned borrow lives.
210+
unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr().cast(), self.simd_block_len()) }
211+
}
212+
213+
#[inline]
214+
fn as_simd_slice_uninit(&self) -> &[MaybeUninit<SimdBlock>] {
215+
// SAFETY: The slice constructed is within bounds of the underlying allocation. This function
216+
// is called with a read-only borrow so no other write can happen as long as the returned borrow lives.
217+
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.simd_block_len()) }
218+
}
219+
220+
#[inline]
221+
fn as_mut_simd_slice_uninit(&mut self) -> &mut [MaybeUninit<SimdBlock>] {
192222
// SAFETY: The slice constructed is within bounds of the underlying allocation. This function
193223
// is called with a mutable borrow so no other read or write can happen as long as the returned borrow lives.
194224
unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr(), self.simd_block_len()) }
@@ -758,8 +788,12 @@ impl FixedBitSet {
758788
let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
759789
// SAFETY: The data pointer and capacity were created from a Vec initially. The block
760790
// len is identical to that of the original.
761-
let data = unsafe {
762-
Vec::from_raw_parts(self.data.as_ptr(), self.simd_block_len(), self.capacity)
791+
let data: Vec<SimdBlock> = unsafe {
792+
Vec::from_raw_parts(
793+
self.data.as_ptr().cast(),
794+
self.simd_block_len(),
795+
self.capacity,
796+
)
763797
};
764798
let mut iter = slice.iter().copied();
765799

@@ -1409,27 +1443,24 @@ impl Clone for FixedBitSet {
14091443

14101444
#[inline]
14111445
fn clone_from(&mut self, source: &Self) {
1412-
{
1413-
let me = self.as_mut_simd_slice();
1414-
let them = source.as_simd_slice();
1415-
match me.len().cmp(&them.len()) {
1416-
Ordering::Greater => {
1417-
let (head, tail) = me.split_at_mut(them.len());
1418-
head.copy_from_slice(them);
1419-
tail.fill(SimdBlock::NONE);
1420-
self.length = source.length;
1421-
return;
1422-
}
1423-
Ordering::Equal => {
1424-
me.copy_from_slice(them);
1425-
self.length = source.length;
1426-
return;
1427-
}
1428-
// Self is smaller than the source, this requires allocation.
1429-
Ordering::Less => {}
1446+
if self.length < source.length {
1447+
// SAFETY: `fill` is uninitialized, but is immediately initialized from `source`.
1448+
unsafe { self.grow_inner(source.length, MaybeUninit::uninit()) };
1449+
}
1450+
let me = self.as_mut_simd_slice_uninit();
1451+
let them = source.as_simd_slice_uninit();
1452+
match me.len().cmp(&them.len()) {
1453+
Ordering::Greater => {
1454+
let (head, tail) = me.split_at_mut(them.len());
1455+
head.copy_from_slice(them);
1456+
tail.fill(MaybeUninit::new(SimdBlock::NONE));
14301457
}
1458+
Ordering::Equal => me.copy_from_slice(them),
1459+
// The grow_inner above ensures that self is at least as large as source.
1460+
// so this branch is unreachable.
1461+
Ordering::Less => {}
14311462
}
1432-
*self = source.clone();
1463+
self.length = source.length;
14331464
}
14341465
}
14351466

0 commit comments

Comments
 (0)