Skip to content

Commit 5c705cb

Browse files
committed
made StorageWithCapacity unsafe
to rely on post-conditions in unsafe code moved capacity calculations to it's own module
1 parent ffc76a4 commit 5c705cb

File tree

9 files changed

+120
-89
lines changed

9 files changed

+120
-89
lines changed

src/raw.rs

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -20,79 +20,18 @@ mod slice;
2020
mod uninit;
2121
mod zero_sized;
2222

23+
mod capacity;
24+
2325
#[cfg(any(doc, feature = "alloc"))]
2426
pub use heap::Heap;
2527

2628
pub use slice::UninitSlice;
2729
pub use uninit::UninitBuffer;
2830
pub use zero_sized::ZeroSized;
2931

30-
#[cold]
31-
#[inline(never)]
32-
#[cfg(feature = "nightly")]
33-
const fn capacity_calculation_overflow() -> ! { panic!("Tried to calculate the current capacity, but overflowed") }
34-
35-
#[cold]
36-
#[inline(never)]
37-
fn fixed_capacity_reserve_error(capacity: usize, new_capacity: usize) -> ! {
38-
panic!(
39-
"Tried to reserve {}, but used a fixed capacity storage of {}",
40-
new_capacity, capacity
41-
)
42-
}
43-
44-
#[cfg(not(any(
45-
target_pointer_width = "8",
46-
target_pointer_width = "16",
47-
target_pointer_width = "32",
48-
target_pointer_width = "64"
49-
)))]
50-
compile_error!("Cannot correctly calculate capacity on an 128-bit or larger architecture");
51-
52-
const fn capacity(old_capacity: usize, size_self: usize, size_other: usize) -> usize {
53-
#[cfg(target_pointer_width = "8")]
54-
type PointerNext = u16;
55-
#[cfg(target_pointer_width = "16")]
56-
type PointerNext = u32;
57-
#[cfg(target_pointer_width = "32")]
58-
type PointerNext = u64;
59-
#[cfg(target_pointer_width = "64")]
60-
type PointerNext = u128;
61-
62-
let size = (old_capacity as PointerNext) * (size_self as PointerNext) / (size_other as PointerNext);
63-
64-
#[cfg(not(feature = "nightly"))]
65-
{
66-
[size as usize][(size > usize::MAX as PointerNext) as usize]
67-
}
68-
69-
#[cfg(feature = "nightly")]
70-
{
71-
if size > usize::MAX as PointerNext {
72-
capacity_calculation_overflow()
73-
}
74-
75-
size as usize
76-
}
77-
}
78-
7932
/// A [`Storage`] that can only contain initialized `Storage::Item`
8033
pub unsafe trait StorageInit<T>: Storage<T> {}
8134

82-
/// Check if type `U` smaller than `T` and less aligned than `T`
83-
pub const fn is_compatible<T, U>() -> bool {
84-
use core::mem::{align_of, size_of};
85-
86-
size_of::<T>() >= size_of::<U>() && align_of::<T>() >= align_of::<U>()
87-
}
88-
89-
/// Check if type `U` is layout identical to `T`
90-
pub const fn is_identical<T, U>() -> bool {
91-
use core::mem::{align_of, size_of};
92-
93-
size_of::<T>() == size_of::<U>() && align_of::<T>() == align_of::<U>()
94-
}
95-
9635
/// A type that can hold `T`s, and potentially
9736
/// reserve space for more `Self::Items`s
9837
pub unsafe trait Storage<T> {
@@ -143,7 +82,12 @@ pub unsafe trait Storage<T> {
14382
}
14483

14584
/// A storage that can be initially created with a given capacity
146-
pub trait StorageWithCapacity<T>: Storage<T> + Default {
85+
///
86+
/// # Safety
87+
///
88+
/// The storage must have a capacity of at least `capacity` after
89+
/// `StorageWithCapacity::with_capacity` is called.
90+
pub unsafe trait StorageWithCapacity<T>: Storage<T> + Default {
14791
/// Creates a new storage with at least the given storage capacity
14892
fn with_capacity(capacity: usize) -> Self;
14993

@@ -193,7 +137,7 @@ unsafe impl<T, S: ?Sized + Storage<T>> Storage<T> for Box<S> {
193137
}
194138

195139
#[cfg(feature = "alloc")]
196-
impl<T, S: ?Sized + StorageWithCapacity<T>> StorageWithCapacity<T> for Box<S> {
140+
unsafe impl<T, S: ?Sized + StorageWithCapacity<T>> StorageWithCapacity<T> for Box<S> {
197141
fn with_capacity(capacity: usize) -> Self { Box::new(S::with_capacity(capacity)) }
198142

199143
#[doc(hidden)]

src/raw/alloc.rs

Whitespace-only changes.

src/raw/array.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::raw::{AllocError, Storage, StorageWithCapacity};
22

33
unsafe impl<T: Copy, const N: usize> crate::raw::StorageInit<T> for [T; N] {}
4-
impl<T: Default + Copy, const N: usize> StorageWithCapacity<T> for [T; N]
4+
unsafe impl<T: Default + Copy, const N: usize> StorageWithCapacity<T> for [T; N]
55
where
66
Self: Default,
77
{
88
fn with_capacity(capacity: usize) -> Self {
99
if capacity > N {
10-
crate::raw::fixed_capacity_reserve_error(N, capacity)
10+
crate::raw::capacity::fixed_capacity_reserve_error(N, capacity)
1111
}
1212

1313
Self::default()
@@ -37,7 +37,7 @@ unsafe impl<T: Copy, const N: usize> Storage<T> for [T; N] {
3737

3838
fn reserve(&mut self, capacity: usize) {
3939
if capacity > N {
40-
crate::raw::fixed_capacity_reserve_error(N, capacity)
40+
crate::raw::capacity::fixed_capacity_reserve_error(N, capacity)
4141
}
4242
}
4343

src/raw/capacity.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#[cold]
2+
#[inline(never)]
3+
#[cfg(feature = "nightly")]
4+
pub(in crate::raw) const fn capacity_calculation_overflow() -> ! {
5+
panic!("Tried to calculate the current capacity, but overflowed")
6+
}
7+
8+
#[cold]
9+
#[inline(never)]
10+
pub(in crate::raw) fn fixed_capacity_reserve_error(capacity: usize, new_capacity: usize) -> ! {
11+
panic!(
12+
"Tried to reserve {}, but used a fixed capacity storage of {}",
13+
new_capacity, capacity
14+
)
15+
}
16+
17+
#[cfg(not(any(
18+
target_pointer_width = "8",
19+
target_pointer_width = "16",
20+
target_pointer_width = "32",
21+
target_pointer_width = "64"
22+
)))]
23+
compile_error!("Cannot correctly calculate capacity on an 128-bit or larger architecture");
24+
25+
pub(in crate::raw) enum Round {
26+
Up,
27+
Down,
28+
}
29+
30+
pub(in crate::raw) const fn capacity(old_capacity: usize, size_self: usize, size_other: usize, round: Round) -> usize {
31+
#[cfg(target_pointer_width = "8")]
32+
type PointerNext = u16;
33+
#[cfg(target_pointer_width = "16")]
34+
type PointerNext = u32;
35+
#[cfg(target_pointer_width = "32")]
36+
type PointerNext = u64;
37+
#[cfg(target_pointer_width = "64")]
38+
type PointerNext = u128;
39+
40+
if size_other == 0 {
41+
return usize::MAX
42+
}
43+
44+
let size = (old_capacity as PointerNext) * (size_self as PointerNext);
45+
46+
let size = match round {
47+
// this can't overflow
48+
//
49+
// old_capacity : n-bit number 0..pow2(n)
50+
// size_self : n-bit number 0..pow2(n)
51+
// size_other : n-bit number 1..pow2(n)
52+
// PointerNext : 2*n-bit number 0..pow2(n)
53+
//
54+
// size = 0..=(pow2(n)-1) * 0..=(pow2(n)-1)
55+
// = 0..=(pow2(n) * pow2(n) - 2 * pow2(n) - 1)
56+
// = 0..=(pow2(2 * n) - 2 * pow2(n) - 1)
57+
//
58+
// size + size_other - 1 = 0..=(pow2(2 * n) - 2 * pow2(n) - 1) + 1..=pow2(n) - 1..=1
59+
// = 0..=(pow2(2 * n) - 2 * pow2(n) - 1) + 0..=pow2(n)
60+
// = 0..=(pow2(2 * n) - 2 * pow2(n) - 1 + pow2(n))
61+
// = 0..=(pow2(2 * n) - pow2(n) - 1) < pow2(2 * n)
62+
//
63+
Round::Up => size.wrapping_add(size_other as PointerNext).wrapping_sub(1),
64+
Round::Down => size,
65+
};
66+
67+
// this can't overflow pow2(n)
68+
//
69+
// size = 0..=(pow2(2 * n) - pow2(n) - 1)
70+
// size / size_other = 0..=(pow2(2 * n) - pow2(n) - 1) / pow2(n)
71+
// = 0..=(pow2(n) - 1 - 1 / pow2(n)) < pow2(n)
72+
//
73+
(size / (size_other as PointerNext)) as usize
74+
}

src/raw/heap/nightly.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::raw::{AllocError, Storage, StorageWithCapacity};
1+
use crate::raw::{
2+
capacity::{capacity, Round},
3+
AllocError, Storage, StorageWithCapacity,
4+
};
25

36
use core::{
47
alloc::Layout,
@@ -116,21 +119,21 @@ impl<T, A: AllocRef + Default> Default for Heap<T, A> {
116119
unsafe impl<T, U, A: ?Sized + AllocRef> Storage<U> for Heap<T, A> {
117120
const IS_ALIGNED: bool = align_of::<T>() >= align_of::<U>();
118121

119-
fn capacity(&self) -> usize { crate::raw::capacity(self.capacity, size_of::<T>(), size_of::<U>()) }
122+
fn capacity(&self) -> usize { capacity(self.capacity, size_of::<T>(), size_of::<U>(), Round::Down) }
120123

121124
fn as_ptr(&self) -> *const U { self.ptr.as_ptr().cast() }
122125

123126
fn as_mut_ptr(&mut self) -> *mut U { self.ptr.as_ptr().cast() }
124127

125128
fn reserve(&mut self, new_capacity: usize) {
126-
let new_capacity = crate::raw::capacity(new_capacity, size_of::<U>(), size_of::<T>());
129+
let new_capacity = capacity(new_capacity, size_of::<U>(), size_of::<T>(), Round::Up);
127130
if self.capacity < new_capacity {
128131
let _ = self.reserve_slow(new_capacity, OnFailure::Abort);
129132
}
130133
}
131134

132135
fn try_reserve(&mut self, new_capacity: usize) -> Result<(), AllocError> {
133-
let new_capacity = crate::raw::capacity(new_capacity, size_of::<U>(), size_of::<T>());
136+
let new_capacity = capacity(new_capacity, size_of::<U>(), size_of::<T>(), Round::Up);
134137
if self.capacity < new_capacity {
135138
self.reserve_slow(new_capacity, OnFailure::Error)
136139
} else {
@@ -163,8 +166,10 @@ impl<T, A: Default + AllocRef> Heap<T, A> {
163166
}
164167
}
165168

166-
impl<T, U, A: Default + AllocRef> StorageWithCapacity<U> for Heap<T, A> {
167-
fn with_capacity(capacity: usize) -> Self { Self::with_capacity(capacity) }
169+
unsafe impl<T, U, A: Default + AllocRef> StorageWithCapacity<U> for Heap<T, A> {
170+
fn with_capacity(cap: usize) -> Self {
171+
Self::with_capacity(capacity(cap, size_of::<U>(), size_of::<T>(), Round::Up))
172+
}
168173
}
169174

170175
impl<T, A: ?Sized + AllocRef> Heap<T, A> {

src/raw/heap/stable.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::raw::{AllocError, Storage, StorageWithCapacity};
1+
use crate::raw::{
2+
capacity::{capacity, Round},
3+
AllocError, Storage, StorageWithCapacity,
4+
};
25

36
use core::{
47
alloc::Layout,
@@ -67,21 +70,21 @@ impl<T> Default for Heap<T> {
6770
unsafe impl<T, U> Storage<U> for Heap<T> {
6871
const IS_ALIGNED: bool = align_of::<T>() >= align_of::<U>();
6972

70-
fn capacity(&self) -> usize { self.capacity }
73+
fn capacity(&self) -> usize { capacity(self.capacity, size_of::<T>(), size_of::<U>(), Round::Down) }
7174

7275
fn as_ptr(&self) -> *const U { self.ptr.as_ptr().cast() }
7376

7477
fn as_mut_ptr(&mut self) -> *mut U { self.ptr.as_ptr().cast() }
7578

7679
fn reserve(&mut self, new_capacity: usize) {
77-
let new_capacity = crate::raw::capacity(new_capacity, size_of::<U>(), size_of::<T>());
80+
let new_capacity = capacity(new_capacity, size_of::<U>(), size_of::<T>(), Round::Up);
7881
if self.capacity < new_capacity {
7982
let _ = self.reserve_slow(new_capacity, OnFailure::Abort);
8083
}
8184
}
8285

8386
fn try_reserve(&mut self, new_capacity: usize) -> Result<(), AllocError> {
84-
let new_capacity = crate::raw::capacity(new_capacity, size_of::<U>(), size_of::<T>());
87+
let new_capacity = capacity(new_capacity, size_of::<U>(), size_of::<T>(), Round::Up);
8588
if self.capacity < new_capacity {
8689
self.reserve_slow(new_capacity, OnFailure::Error)
8790
} else {
@@ -151,8 +154,10 @@ impl<T> Heap<T> {
151154
}
152155
}
153156

154-
impl<T, U> StorageWithCapacity<U> for Heap<T> {
155-
fn with_capacity(capacity: usize) -> Self { Self::with_capacity(capacity) }
157+
unsafe impl<T, U> StorageWithCapacity<U> for Heap<T> {
158+
fn with_capacity(cap: usize) -> Self {
159+
Self::with_capacity(capacity(cap, size_of::<U>(), size_of::<T>(), Round::Up))
160+
}
156161
}
157162

158163
impl<T> Heap<T> {

src/raw/slice.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::raw::{AllocError, Storage};
1+
use crate::raw::{
2+
capacity::{capacity, fixed_capacity_reserve_error, Round},
3+
AllocError, Storage,
4+
};
25

36
use core::mem::{align_of, size_of, MaybeUninit};
47

@@ -40,16 +43,16 @@ impl<T> UninitSlice<T> {
4043
unsafe impl<T, U> Storage<U> for UninitSlice<T> {
4144
const IS_ALIGNED: bool = align_of::<T>() >= align_of::<U>();
4245

43-
fn capacity(&self) -> usize { crate::raw::capacity(self.0.len(), size_of::<T>(), size_of::<U>()) }
46+
fn capacity(&self) -> usize { capacity(self.0.len(), size_of::<T>(), size_of::<U>(), Round::Down) }
4447

4548
fn as_ptr(&self) -> *const U { self.0.as_ptr().cast() }
4649

4750
fn as_mut_ptr(&mut self) -> *mut U { self.0.as_mut_ptr().cast() }
4851

4952
fn reserve(&mut self, new_capacity: usize) {
50-
let new_capacity = crate::raw::capacity(new_capacity, size_of::<U>(), size_of::<T>());
53+
let new_capacity = capacity(new_capacity, size_of::<U>(), size_of::<T>(), Round::Up);
5154
if new_capacity > self.0.len() {
52-
crate::raw::fixed_capacity_reserve_error(self.0.len(), new_capacity)
55+
fixed_capacity_reserve_error(self.0.len(), new_capacity)
5356
}
5457
}
5558

@@ -74,7 +77,7 @@ unsafe impl<T: Copy> Storage<T> for [T] {
7477

7578
fn reserve(&mut self, capacity: usize) {
7679
if capacity > self.len() {
77-
crate::raw::fixed_capacity_reserve_error(self.len(), capacity)
80+
fixed_capacity_reserve_error(self.len(), capacity)
7881
}
7982
}
8083

src/raw/uninit.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ impl<T, A> Clone for UninitBuffer<T, A> {
8484
fn clone(&self) -> Self { Self::default() }
8585
}
8686

87-
impl<U, T, A> StorageWithCapacity<U> for UninitBuffer<T, A> {
87+
unsafe impl<U, T, A> StorageWithCapacity<U> for UninitBuffer<T, A> {
8888
fn with_capacity(capacity: usize) -> Self {
8989
let max_capacity = size::<U, T, A>();
9090
if capacity > max_capacity {
91-
crate::raw::fixed_capacity_reserve_error(max_capacity, capacity)
91+
crate::raw::capacity::fixed_capacity_reserve_error(max_capacity, capacity)
9292
}
9393

9494
Self::default()
@@ -119,7 +119,7 @@ unsafe impl<U, T, A> Storage<U> for UninitBuffer<T, A> {
119119
fn reserve(&mut self, new_capacity: usize) {
120120
let capacity = size::<U, T, A>();
121121
if new_capacity > capacity {
122-
crate::raw::fixed_capacity_reserve_error(capacity, new_capacity)
122+
crate::raw::capacity::fixed_capacity_reserve_error(capacity, new_capacity)
123123
}
124124
}
125125

src/raw/zero_sized.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ unsafe impl<T> Storage<T> for ZeroSized<T> {
5959
fn capacity(&self) -> usize { usize::MAX }
6060
}
6161

62-
impl<T> StorageWithCapacity<T> for ZeroSized<T> {
62+
unsafe impl<T> StorageWithCapacity<T> for ZeroSized<T> {
6363
#[inline]
6464
fn with_capacity(_: usize) -> Self { Self::NEW }
6565
}

0 commit comments

Comments
 (0)