Skip to content

Commit 3070db1

Browse files
committed
Add Allocator-helper to allocate prefix and safety-tester
1 parent 6265286 commit 3070db1

File tree

6 files changed

+431
-0
lines changed

6 files changed

+431
-0
lines changed

library/core/src/alloc/helper.rs

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use crate::{
2+
alloc::{AllocError, Allocator, Layout},
3+
fmt,
4+
marker::PhantomData,
5+
ptr::NonNull,
6+
};
7+
8+
/// An allocator that requests some extra memory from the parent allocator for storing a prefix and/or a suffix.
9+
///
10+
/// The alignment of the memory block is the maximum of the alignment of `Prefix` and the requested
11+
/// alignment. This may introduce an unused padding between `Prefix` and the returned memory.
12+
///
13+
/// To get a pointer to the prefix, [`prefix()`] may be called.
14+
///
15+
/// [`prefix()`]: Self::prefix
16+
///
17+
/// Consider
18+
///
19+
/// ```rust,ignore (not real code)
20+
/// #[repr(C)]
21+
/// struct Struct {
22+
/// t: T,
23+
/// data: Data,
24+
/// }
25+
/// ```
26+
///
27+
/// where `Data` is a type with layout `layout`.
28+
///
29+
/// When this allocator creates an allocation for layout `layout`, the pointer can be
30+
/// offset by `-offsetof(Struct, data)` and the resulting pointer points is an allocation
31+
/// of `A` for `Layout::new::<Struct>()`.
32+
#[unstable(feature = "allocator_api_internals", issue = "none")]
33+
pub struct PrefixAllocator<Alloc, Prefix = ()> {
34+
/// The parent allocator to be used as backend
35+
pub parent: Alloc,
36+
_prefix: PhantomData<*const Prefix>,
37+
}
38+
39+
impl<Alloc: fmt::Debug, Prefix> fmt::Debug for PrefixAllocator<Alloc, Prefix> {
40+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41+
f.debug_struct("Affix").field("parent", &self.parent).finish()
42+
}
43+
}
44+
45+
impl<Alloc: Default, Prefix> Default for PrefixAllocator<Alloc, Prefix> {
46+
fn default() -> Self {
47+
Self::new(Alloc::default())
48+
}
49+
}
50+
51+
impl<Alloc: Clone, Prefix> Clone for PrefixAllocator<Alloc, Prefix> {
52+
fn clone(&self) -> Self {
53+
Self::new(self.parent.clone())
54+
}
55+
}
56+
57+
impl<Alloc: Copy, Prefix> Copy for PrefixAllocator<Alloc, Prefix> {}
58+
59+
impl<Alloc: PartialEq, Prefix> PartialEq for PrefixAllocator<Alloc, Prefix> {
60+
fn eq(&self, other: &Self) -> bool {
61+
self.parent.eq(&other.parent)
62+
}
63+
}
64+
65+
impl<Alloc: Eq, Prefix> Eq for PrefixAllocator<Alloc, Prefix> {}
66+
67+
unsafe impl<Alloc: Send, Prefix> Send for PrefixAllocator<Alloc, Prefix> {}
68+
unsafe impl<Alloc: Sync, Prefix> Sync for PrefixAllocator<Alloc, Prefix> {}
69+
impl<Alloc: Unpin, Prefix> Unpin for PrefixAllocator<Alloc, Prefix> {}
70+
71+
impl<Alloc, Prefix> PrefixAllocator<Alloc, Prefix> {
72+
pub const fn new(parent: Alloc) -> Self {
73+
Self { parent, _prefix: PhantomData }
74+
}
75+
76+
/// Returns the offset between the `Prefix` and the stored data.
77+
#[inline]
78+
pub fn prefix_offset(layout: Layout) -> usize {
79+
let prefix_layout = Layout::new::<Prefix>();
80+
prefix_layout.size() + prefix_layout.padding_needed_for(layout.align())
81+
}
82+
83+
/// Returns a pointer to the prefix.
84+
///
85+
/// # Safety
86+
///
87+
/// * `ptr` must denote a block of memory *[currently allocated]* via this allocator, and
88+
/// * `layout` must *[fit]* that block of memory.
89+
///
90+
/// [currently allocated]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#currently-allocated-memory
91+
/// [fit]: https://doc.rust-lang.org/nightly/core/alloc/trait.AllocRef.html#memory-fitting
92+
#[inline]
93+
pub unsafe fn prefix(ptr: NonNull<u8>, layout: Layout) -> NonNull<Prefix> {
94+
let prefix_offset = Self::prefix_offset(layout);
95+
// SAFETY: `prefix_offset` is smaller (and not equal to) `ptr` as the same function for calculating `prefix_offset` is used when allocating.
96+
unsafe { NonNull::new_unchecked(ptr.as_ptr().sub(prefix_offset)).cast() }
97+
}
98+
99+
fn create_ptr(ptr: NonNull<[u8]>, offset_prefix: usize) -> NonNull<[u8]> {
100+
let len = ptr.len() - offset_prefix;
101+
102+
// SAFETY: `prefix_offset` is smaller (and not equal to) `ptr` as the same function for calculating `prefix_offset` is used when allocating.
103+
let ptr = unsafe { NonNull::new_unchecked(ptr.as_mut_ptr().add(offset_prefix)) };
104+
105+
NonNull::slice_from_raw_parts(ptr, len)
106+
}
107+
108+
#[inline]
109+
fn alloc_impl(
110+
layout: Layout,
111+
alloc: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
112+
) -> Result<NonNull<[u8]>, AllocError> {
113+
let (layout, offset_prefix) =
114+
Layout::new::<Prefix>().extend(layout).map_err(|_| AllocError)?;
115+
116+
Ok(Self::create_ptr(alloc(layout)?, offset_prefix))
117+
}
118+
}
119+
120+
unsafe impl<Alloc, Prefix> Allocator for PrefixAllocator<Alloc, Prefix>
121+
where
122+
Alloc: Allocator,
123+
{
124+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
125+
Self::alloc_impl(layout, |l| self.parent.allocate(l))
126+
}
127+
128+
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
129+
Self::alloc_impl(layout, |l| self.parent.allocate_zeroed(l))
130+
}
131+
132+
unsafe fn grow(
133+
&self,
134+
_ptr: NonNull<u8>,
135+
_old_layout: Layout,
136+
_new_layout: Layout,
137+
) -> Result<NonNull<[u8]>, AllocError> {
138+
// For (A)Rc it's not needed. When implementing please take care, if the alignment changes.
139+
unimplemented!("PrefixAllocator currently does not implement growing.");
140+
}
141+
142+
unsafe fn grow_zeroed(
143+
&self,
144+
_ptr: NonNull<u8>,
145+
_old_layout: Layout,
146+
_new_layout: Layout,
147+
) -> Result<NonNull<[u8]>, AllocError> {
148+
// For (A)Rc it's not needed. When implementing please take care, if the alignment changes.
149+
unimplemented!("PrefixAllocator currently does not implement growing.");
150+
}
151+
152+
unsafe fn shrink(
153+
&self,
154+
_ptr: NonNull<u8>,
155+
_old_layout: Layout,
156+
_new_layout: Layout,
157+
) -> Result<NonNull<[u8]>, AllocError> {
158+
// For (A)Rc it's not needed. When implementing please take care, if the alignment changes.
159+
unimplemented!("PrefixAllocator currently does not implement shrinking.");
160+
}
161+
162+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
163+
let (layout, prefix_offset) = Layout::new::<Prefix>().extend(layout).unwrap();
164+
// SAFETY: `prefix_offset` is smaller (and not equal to) `ptr` as the same function for calculating `prefix_offset` is used when allocating.
165+
unsafe {
166+
let base_ptr = NonNull::new_unchecked(ptr.as_ptr().sub(prefix_offset));
167+
self.parent.deallocate(base_ptr, layout)
168+
};
169+
}
170+
}

library/core/src/alloc/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
mod global;
66
mod layout;
77

8+
#[unstable(feature = "allocator_api_internals", issue = "none")]
9+
pub mod helper;
10+
811
#[stable(feature = "global_alloc", since = "1.28.0")]
912
pub use self::global::GlobalAlloc;
1013
#[stable(feature = "alloc_layout", since = "1.28.0")]

library/core/tests/alloc/mod.rs

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use core::alloc::{AllocError, Allocator, Layout};
2+
use core::ptr::NonNull;
3+
use std::{
4+
collections::HashMap,
5+
sync::{Mutex, PoisonError},
6+
};
7+
8+
mod const_unchecked_layout;
9+
mod prefix;
10+
11+
#[derive(Default)]
12+
/// Implements `Allocator` and checks it's unsafety conditions.
13+
struct Tracker<A> {
14+
alloc: A,
15+
map: Mutex<HashMap<NonNull<u8>, (usize, Layout)>>,
16+
}
17+
18+
impl<A> Tracker<A> {
19+
fn new(alloc: A) -> Self {
20+
Self { alloc, map: Default::default() }
21+
}
22+
23+
fn after_alloc(&self, layout: Layout, result: Result<NonNull<[u8]>, AllocError>) {
24+
if let Ok(ptr) = result {
25+
self.map
26+
.lock()
27+
.unwrap_or_else(PoisonError::into_inner)
28+
.insert(ptr.as_non_null_ptr(), (ptr.len(), layout));
29+
}
30+
}
31+
32+
fn before_dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
33+
let lock = self.map.lock().unwrap_or_else(PoisonError::into_inner);
34+
let (size, old_layout) = lock
35+
.get(&ptr)
36+
.expect("`ptr` must denote a block of memory currently allocated via this allocator");
37+
assert_eq!(
38+
layout.align(),
39+
old_layout.align(),
40+
"`layout` must fit that block of memory. Expected alignment of {}, got {}",
41+
old_layout.align(),
42+
layout.align()
43+
);
44+
if layout.size() < old_layout.size() || layout.size() > *size {
45+
if *size == old_layout.size() {
46+
panic!(
47+
"`layout` must fit that block of memory. Expected size of {}, got {}",
48+
old_layout.size(),
49+
layout.size()
50+
)
51+
} else {
52+
panic!(
53+
"`layout` must fit that block of memory. Expected size between {}..={}, \
54+
got {}",
55+
old_layout.size(),
56+
size,
57+
layout.size()
58+
)
59+
}
60+
}
61+
}
62+
63+
fn after_dealloc(&self, ptr: NonNull<u8>, _layout: Layout) {
64+
self.map.lock().unwrap_or_else(PoisonError::into_inner).remove(&ptr);
65+
}
66+
67+
fn before_grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) {
68+
assert!(
69+
new_layout.size() >= old_layout.size(),
70+
"`new_layout.size()` must be greater than or equal to `old_layout.size()`, expected {} >= {}",
71+
new_layout.size(),
72+
old_layout.size()
73+
);
74+
self.before_dealloc(ptr, old_layout)
75+
}
76+
77+
#[track_caller]
78+
fn after_grow(
79+
&self,
80+
ptr: NonNull<u8>,
81+
old_layout: Layout,
82+
new_layout: Layout,
83+
result: Result<NonNull<[u8]>, AllocError>,
84+
) {
85+
if result.is_ok() {
86+
self.after_dealloc(ptr, old_layout);
87+
self.after_alloc(new_layout, result);
88+
}
89+
}
90+
91+
fn before_shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) {
92+
assert!(
93+
new_layout.size() <= old_layout.size(),
94+
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`, expected {} >= {}",
95+
new_layout.size(),
96+
old_layout.size()
97+
);
98+
self.before_dealloc(ptr, old_layout)
99+
}
100+
101+
#[track_caller]
102+
fn after_shrink(
103+
&self,
104+
ptr: NonNull<u8>,
105+
old_layout: Layout,
106+
new_layout: Layout,
107+
result: Result<NonNull<[u8]>, AllocError>,
108+
) {
109+
if result.is_ok() {
110+
self.after_dealloc(ptr, old_layout);
111+
self.after_alloc(new_layout, result);
112+
}
113+
}
114+
}
115+
116+
unsafe impl<A: Allocator> Allocator for Tracker<A> {
117+
#[track_caller]
118+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, std::alloc::AllocError> {
119+
let result = self.alloc.allocate(layout);
120+
self.after_alloc(layout, result);
121+
result
122+
}
123+
124+
#[track_caller]
125+
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, std::alloc::AllocError> {
126+
let result = self.alloc.allocate_zeroed(layout);
127+
self.after_alloc(layout, result);
128+
result
129+
}
130+
131+
#[track_caller]
132+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
133+
self.before_dealloc(ptr, layout);
134+
unsafe { self.alloc.deallocate(ptr, layout) }
135+
self.after_dealloc(ptr, layout);
136+
}
137+
138+
#[track_caller]
139+
unsafe fn grow(
140+
&self,
141+
ptr: NonNull<u8>,
142+
old_layout: Layout,
143+
new_layout: Layout,
144+
) -> Result<NonNull<[u8]>, std::alloc::AllocError> {
145+
self.before_grow(ptr, old_layout, new_layout);
146+
let result = unsafe { self.alloc.grow(ptr, old_layout, new_layout) };
147+
self.after_grow(ptr, old_layout, new_layout, result);
148+
result
149+
}
150+
151+
#[track_caller]
152+
unsafe fn grow_zeroed(
153+
&self,
154+
ptr: NonNull<u8>,
155+
old_layout: Layout,
156+
new_layout: Layout,
157+
) -> Result<NonNull<[u8]>, std::alloc::AllocError> {
158+
self.before_grow(ptr, old_layout, new_layout);
159+
let result = unsafe { self.alloc.grow_zeroed(ptr, old_layout, new_layout) };
160+
self.after_grow(ptr, old_layout, new_layout, result);
161+
result
162+
}
163+
164+
#[track_caller]
165+
unsafe fn shrink(
166+
&self,
167+
ptr: NonNull<u8>,
168+
old_layout: Layout,
169+
new_layout: Layout,
170+
) -> Result<NonNull<[u8]>, std::alloc::AllocError> {
171+
self.before_shrink(ptr, old_layout, new_layout);
172+
let result = unsafe { self.alloc.shrink(ptr, old_layout, new_layout) };
173+
self.after_shrink(ptr, old_layout, new_layout, result);
174+
result
175+
}
176+
}
177+
178+
impl<A> Drop for Tracker<A> {
179+
fn drop(&mut self) {
180+
let lock = self.map.lock().unwrap_or_else(PoisonError::into_inner);
181+
if !lock.is_empty() {
182+
panic!("Missing deallocations {:#?}", lock);
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)