Skip to content

Commit 9713321

Browse files
committed
Make HeaderVec being a NonNull<> instead a raw pointer
Since we always point to a valid allocation we can use NonNull here. This will benefit from the niche optimization: size_of::<HeaderVec<H,T>>() == size_of::<Option<HeaderVec<H,T>>> Also adds a test to verfiy that HeaderVec are always lean and niche optimized.
1 parent b34e0bd commit 9713321

File tree

2 files changed

+33
-16
lines changed

2 files changed

+33
-16
lines changed

src/lib.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use core::{
77
mem::{self, ManuallyDrop},
88
ops::{Deref, DerefMut, Index, IndexMut},
99
ptr,
10+
ptr::NonNull,
1011
slice::SliceIndex,
1112
};
1213

@@ -51,7 +52,7 @@ union AlignedHeader<H, T> {
5152
/// All of the data, like our header `OurHeaderType { a: 2 }`, the length of the vector: `2`,
5253
/// and the contents of the vector `['x', 'z']` resides on the other side of the pointer.
5354
pub struct HeaderVec<H, T> {
54-
ptr: *mut AlignedHeader<H, T>,
55+
ptr: NonNull<AlignedHeader<H, T>>,
5556
}
5657

5758
impl<H, T> HeaderVec<H, T> {
@@ -65,10 +66,10 @@ impl<H, T> HeaderVec<H, T> {
6566
let layout = Self::layout(capacity);
6667
let ptr = unsafe { alloc::alloc::alloc(layout) } as *mut AlignedHeader<H, T>;
6768

68-
// Handle out-of-memory.
69-
if ptr.is_null() {
69+
let Some(ptr) = NonNull::new(ptr) else {
70+
// Handle out-of-memory.
7071
alloc::alloc::handle_alloc_error(layout);
71-
}
72+
};
7273

7374
// Create self.
7475
let mut this = Self { ptr };
@@ -173,14 +174,14 @@ impl<H, T> HeaderVec<H, T> {
173174
/// This is useful to check if two nodes are the same. Use it with [`HeaderVec::is`].
174175
#[inline(always)]
175176
pub fn ptr(&self) -> *const () {
176-
self.ptr as *const ()
177+
self.ptr.as_ptr() as *const ()
177178
}
178179

179180
/// This is used to check if this is the `HeaderVec` that corresponds to the given pointer.
180181
/// This is useful for updating weak references after [`HeaderVec::push`] returns the pointer.
181182
#[inline(always)]
182183
pub fn is(&self, ptr: *const ()) -> bool {
183-
self.ptr as *const () == ptr
184+
self.ptr.as_ptr() as *const () == ptr
184185
}
185186

186187
/// Create a (dangerous) weak reference to the `HeaderVec`. This is useful to be able
@@ -300,19 +301,21 @@ impl<H, T> HeaderVec<H, T> {
300301
// Reallocate the pointer.
301302
let ptr = unsafe {
302303
alloc::alloc::realloc(
303-
self.ptr as *mut u8,
304+
self.ptr.as_ptr() as *mut u8,
304305
Self::layout(old_capacity),
305306
Self::elems_to_mem_bytes(new_capacity),
306307
) as *mut AlignedHeader<H, T>
307308
};
308-
// Handle out-of-memory.
309-
if ptr.is_null() {
309+
310+
let Some(ptr) = NonNull::new(ptr) else {
311+
// Handle out-of-memory.
310312
alloc::alloc::handle_alloc_error(Self::layout(new_capacity));
311-
}
313+
};
314+
312315
// Check if the new pointer is different than the old one.
313316
let previous_pointer = if ptr != self.ptr {
314317
// Give the user the old pointer so they can update everything.
315-
Some(self.ptr as *const ())
318+
Some(self.ptr())
316319
} else {
317320
None
318321
};
@@ -406,13 +409,13 @@ impl<H, T> HeaderVec<H, T> {
406409
/// Gets the pointer to the start of the slice.
407410
#[inline(always)]
408411
fn start_ptr(&self) -> *const T {
409-
unsafe { (self.ptr as *const T).add(Self::offset()) }
412+
unsafe { (self.ptr.as_ptr() as *const T).add(Self::offset()) }
410413
}
411414

412415
/// Gets the pointer to the start of the slice.
413416
#[inline(always)]
414417
fn start_ptr_mut(&mut self) -> *mut T {
415-
unsafe { (self.ptr as *mut T).add(Self::offset()) }
418+
unsafe { (self.ptr.as_ptr() as *mut T).add(Self::offset()) }
416419
}
417420

418421
/// Gets the pointer to the end of the slice. This returns a mutable pointer to
@@ -425,13 +428,13 @@ impl<H, T> HeaderVec<H, T> {
425428
#[inline(always)]
426429
fn header(&self) -> &HeaderVecHeader<H> {
427430
// The beginning of the memory is always the header.
428-
unsafe { &*(self.ptr as *const HeaderVecHeader<H>) }
431+
unsafe { &*(self.ptr.as_ptr() as *const HeaderVecHeader<H>) }
429432
}
430433

431434
#[inline(always)]
432435
fn header_mut(&mut self) -> &mut HeaderVecHeader<H> {
433436
// The beginning of the memory is always the header.
434-
unsafe { &mut *(self.ptr as *mut HeaderVecHeader<H>) }
437+
unsafe { &mut *(self.ptr.as_ptr() as *mut HeaderVecHeader<H>) }
435438
}
436439
}
437440

@@ -572,7 +575,7 @@ impl<H, T> Drop for HeaderVec<H, T> {
572575
for ix in 0..self.len_exact() {
573576
ptr::drop_in_place(self.start_ptr_mut().add(ix));
574577
}
575-
alloc::alloc::dealloc(self.ptr as *mut u8, Self::layout(self.capacity()));
578+
alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, Self::layout(self.capacity()));
576579
}
577580
}
578581
}

tests/simple.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ struct TestA {
1111
c: usize,
1212
}
1313

14+
#[test]
15+
fn test_sizeof() {
16+
// assert that HeaderVec is really a single lean pointer
17+
assert_eq!(
18+
core::mem::size_of::<HeaderVec<(), ()>>(),
19+
core::mem::size_of::<*mut ()>()
20+
);
21+
// and has space for niche optimization
22+
assert_eq!(
23+
core::mem::size_of::<HeaderVec<(), ()>>(),
24+
core::mem::size_of::<Option<HeaderVec<(), ()>>>()
25+
);
26+
}
27+
1428
#[test]
1529
fn test_head_array() {
1630
let mut v_orig = HeaderVec::new(TestA { a: 4, b: !0, c: 66 });

0 commit comments

Comments
 (0)