Skip to content

Commit decaeed

Browse files
committed
FIX: Miri, unaligned access
* introduces a helper union to fix simplify alignment calculations no need for cmp and phantom data anymore * add simple testcase that triggered the miri issue * change end_ptr_atomic_mut(), using the rewritten start_ptr()
1 parent 4bcab1f commit decaeed

File tree

2 files changed

+27
-21
lines changed

2 files changed

+27
-21
lines changed

src/lib.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
extern crate alloc;
44

55
use core::{
6-
cmp,
76
fmt::Debug,
8-
marker::PhantomData,
97
mem::{self, ManuallyDrop},
108
ops::{Deref, DerefMut, Index, IndexMut},
119
ptr,
@@ -24,6 +22,12 @@ struct HeaderVecHeader<H> {
2422
len: usize,
2523
}
2624

25+
// This union will be properly aligned and sized to store headers followed by T's.
26+
union AlignedHeader<H, T> {
27+
_header: ManuallyDrop<HeaderVecHeader<H>>,
28+
_data: ManuallyDrop<[T; 0]>,
29+
}
30+
2731
/// A vector with a header of your choosing behind a thin pointer
2832
///
2933
/// # Example
@@ -47,8 +51,7 @@ struct HeaderVecHeader<H> {
4751
/// All of the data, like our header `OurHeaderType { a: 2 }`, the length of the vector: `2`,
4852
/// and the contents of the vector `['x', 'z']` resides on the other side of the pointer.
4953
pub struct HeaderVec<H, T> {
50-
ptr: *mut T,
51-
_phantom: PhantomData<H>,
54+
ptr: *mut AlignedHeader<H, T>,
5255
}
5356

5457
impl<H, T> HeaderVec<H, T> {
@@ -58,20 +61,17 @@ impl<H, T> HeaderVec<H, T> {
5861

5962
pub fn with_capacity(capacity: usize, head: H) -> Self {
6063
assert!(capacity > 0, "HeaderVec capacity cannot be 0");
61-
// Allocate the initial memory, which is unititialized.
64+
// Allocate the initial memory, which is uninitialized.
6265
let layout = Self::layout(capacity);
63-
let ptr = unsafe { alloc::alloc::alloc(layout) } as *mut T;
66+
let ptr = unsafe { alloc::alloc::alloc(layout) } as *mut AlignedHeader<H, T>;
6467

6568
// Handle out-of-memory.
6669
if ptr.is_null() {
6770
alloc::alloc::handle_alloc_error(layout);
6871
}
6972

7073
// Create self.
71-
let mut this = Self {
72-
ptr,
73-
_phantom: PhantomData,
74-
};
74+
let mut this = Self { ptr };
7575

7676
// Set the header.
7777
let header = this.header_mut();
@@ -204,10 +204,7 @@ impl<H, T> HeaderVec<H, T> {
204204
#[inline(always)]
205205
pub unsafe fn weak(&self) -> HeaderVecWeak<H, T> {
206206
HeaderVecWeak {
207-
header_vec: ManuallyDrop::new(Self {
208-
ptr: self.ptr,
209-
_phantom: PhantomData,
210-
}),
207+
header_vec: ManuallyDrop::new(Self { ptr: self.ptr }),
211208
}
212209
}
213210

@@ -305,7 +302,7 @@ impl<H, T> HeaderVec<H, T> {
305302
self.ptr as *mut u8,
306303
Self::layout(old_capacity),
307304
Self::elems_to_mem_bytes(new_capacity),
308-
) as *mut T
305+
) as *mut AlignedHeader<H, T>
309306
};
310307
// Handle out-of-memory.
311308
if ptr.is_null() {
@@ -377,10 +374,10 @@ impl<H, T> HeaderVec<H, T> {
377374

378375
/// Gives the offset in units of T (as if the pointer started at an array of T) that the slice actually starts at.
379376
#[inline(always)]
380-
fn offset() -> usize {
377+
const fn offset() -> usize {
381378
// The first location, in units of size_of::<T>(), that is after the header
382379
// It's the end of the header, rounded up to the nearest size_of::<T>()
383-
(mem::size_of::<HeaderVecHeader<H>>() + mem::size_of::<T>() - 1) / mem::size_of::<T>()
380+
mem::size_of::<AlignedHeader<H, T>>() / mem::size_of::<T>()
384381
}
385382

386383
/// Compute the number of elements (in units of T) to allocate for a given capacity.
@@ -400,21 +397,21 @@ impl<H, T> HeaderVec<H, T> {
400397
fn layout(capacity: usize) -> alloc::alloc::Layout {
401398
alloc::alloc::Layout::from_size_align(
402399
Self::elems_to_mem_bytes(capacity),
403-
cmp::max(mem::align_of::<H>(), mem::align_of::<T>()),
400+
mem::align_of::<AlignedHeader<H,T>>()
404401
)
405402
.expect("unable to produce memory layout with Hrc key type (is it a zero sized type? they are not permitted)")
406403
}
407404

408405
/// Gets the pointer to the start of the slice.
409406
#[inline(always)]
410407
fn start_ptr(&self) -> *const T {
411-
unsafe { self.ptr.add(Self::offset()) }
408+
unsafe { (self.ptr as *const T).add(Self::offset()) }
412409
}
413410

414411
/// Gets the pointer to the start of the slice.
415412
#[inline(always)]
416413
fn start_ptr_mut(&mut self) -> *mut T {
417-
unsafe { self.ptr.add(Self::offset()) }
414+
unsafe { (self.ptr as *mut T).add(Self::offset()) }
418415
}
419416

420417
#[inline(always)]
@@ -473,7 +470,7 @@ impl<H, T> HeaderVec<H, T> {
473470
/// uninitialized memory behind the last element.
474471
#[inline(always)]
475472
fn end_ptr_atomic_mut(&self) -> *mut T {
476-
unsafe { self.ptr.add(Self::offset()).add(self.len_atomic_acquire()) }
473+
unsafe { self.start_ptr().add(self.len_atomic_acquire()) as *mut T }
477474
}
478475

479476
/// Atomically adds an item to the end of the list without reallocation.

tests/simple.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,12 @@ fn test_head_array() {
4444
v_orig.as_slice().iter().copied().collect::<String>()
4545
);
4646
}
47+
48+
// This shown a miri error
49+
#[test]
50+
fn test_push() {
51+
let mut hv = HeaderVec::with_capacity(10, ());
52+
53+
hv.push(123);
54+
assert_eq!(hv[0], 123);
55+
}

0 commit comments

Comments
 (0)