Skip to content

Commit 284aa72

Browse files
committed
Add allocation functions
1 parent d50cfb8 commit 284aa72

File tree

4 files changed

+450
-0
lines changed

4 files changed

+450
-0
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
#![feature(deprecated_suggestion)]
116116
#![feature(deref_pure_trait)]
117117
#![feature(dispatch_from_dyn)]
118+
#![feature(drop_guard)]
118119
#![feature(ergonomic_clones)]
119120
#![feature(error_generic_member_access)]
120121
#![feature(exact_size_is_empty)]

library/alloc/src/raw_rc/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@
6464

6565
use core::cell::UnsafeCell;
6666

67+
mod rc_alloc;
6768
mod rc_layout;
69+
mod rc_value_pointer;
6870

6971
/// Stores reference counts.
7072
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
use core::alloc::{AllocError, Allocator};
2+
#[cfg(not(no_global_oom_handling))]
3+
use core::mem;
4+
#[cfg(not(no_global_oom_handling))]
5+
use core::mem::DropGuard;
6+
#[cfg(not(no_global_oom_handling))]
7+
use core::ptr::{self, NonNull};
8+
9+
#[cfg(not(no_global_oom_handling))]
10+
use crate::alloc;
11+
use crate::raw_rc::RefCounts;
12+
use crate::raw_rc::rc_layout::RcLayout;
13+
use crate::raw_rc::rc_value_pointer::RcValuePointer;
14+
15+
/// Allocates uninitialized memory for a reference-counted allocation with allocator `alloc` and
16+
/// layout `RcLayout`. Returns a pointer to the value location.
17+
#[inline]
18+
fn allocate_uninit_raw_bytes<A>(
19+
alloc: &A,
20+
rc_layout: RcLayout,
21+
) -> Result<RcValuePointer, AllocError>
22+
where
23+
A: Allocator,
24+
{
25+
let allocation_result = alloc.allocate(rc_layout.get());
26+
27+
allocation_result.map(|allocation_ptr| {
28+
// SAFETY: `allocation_ptr` is allocated with `rc_layout`, so the safety requirement of
29+
// `RcValuePointer::from_allocation_ptr` is trivially satisfied.
30+
unsafe { RcValuePointer::from_allocation_ptr(allocation_ptr.cast(), rc_layout) }
31+
})
32+
}
33+
34+
/// Allocates zeroed memory for a reference-counted allocation with allocator `alloc` and layout
35+
/// `RcLayout`. Returns a pointer to the value location.
36+
#[inline]
37+
fn allocate_zeroed_raw_bytes<A>(
38+
alloc: &A,
39+
rc_layout: RcLayout,
40+
) -> Result<RcValuePointer, AllocError>
41+
where
42+
A: Allocator,
43+
{
44+
let allocation_result = alloc.allocate_zeroed(rc_layout.get());
45+
46+
allocation_result.map(|allocation_ptr| {
47+
// SAFETY: `allocation_ptr` is allocated with `rc_layout`, so the safety requirement of
48+
// `RcValuePointer::from_allocation_ptr` is trivially satisfied.
49+
unsafe { RcValuePointer::from_allocation_ptr(allocation_ptr.cast(), rc_layout) }
50+
})
51+
}
52+
53+
/// Initializes reference counters in a reference-counted allocation pointed to by `value_ptr`
54+
/// with strong count of `STRONG_COUNT` and weak count of 1.
55+
///
56+
/// # Safety
57+
///
58+
/// - `value_ptr` points to a valid reference-counted allocation.
59+
#[inline]
60+
unsafe fn init_rc_allocation<const STRONG_COUNT: usize>(value_ptr: RcValuePointer) {
61+
// SAFETY: Caller guarantees the `value_ptr` points to a valid reference-counted allocation, so
62+
// we can write to the corresponding `RefCounts` object.
63+
unsafe { value_ptr.ref_counts_ptr().write(const { RefCounts::new(STRONG_COUNT) }) };
64+
}
65+
66+
/// Tries to allocate a chunk of reference-counted memory that is described by `rc_layout` with
67+
/// `alloc`. The allocated memory has strong count of `STRONG_COUNT` and weak count of 1.
68+
pub(crate) fn try_allocate_uninit_in<A, const STRONG_COUNT: usize>(
69+
alloc: &A,
70+
rc_layout: RcLayout,
71+
) -> Result<RcValuePointer, AllocError>
72+
where
73+
A: Allocator,
74+
{
75+
let value_ptr = allocate_uninit_raw_bytes(alloc, rc_layout)?;
76+
77+
// SAFETY: `value_ptr` is newly allocated, so it is guaranteed to be valid.
78+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) };
79+
80+
Ok(value_ptr)
81+
}
82+
83+
/// Creates an allocator of type `A`, then tries to allocate a chunk of reference-counted memory
84+
/// that is described by `rc_layout`.
85+
pub(crate) fn try_allocate_uninit<A, const STRONG_COUNT: usize>(
86+
rc_layout: RcLayout,
87+
) -> Result<(RcValuePointer, A), AllocError>
88+
where
89+
A: Allocator + Default,
90+
{
91+
let alloc = A::default();
92+
93+
try_allocate_uninit_in::<A, STRONG_COUNT>(&alloc, rc_layout).map(|value_ptr| (value_ptr, alloc))
94+
}
95+
96+
/// Tries to allocate a reference-counted memory that is described by `rc_layout` with `alloc`. The
97+
/// allocated memory has strong count of `STRONG_COUNT` and weak count of 1, and the value memory
98+
/// is all zero bytes.
99+
pub(crate) fn try_allocate_zeroed_in<A, const STRONG_COUNT: usize>(
100+
alloc: &A,
101+
rc_layout: RcLayout,
102+
) -> Result<RcValuePointer, AllocError>
103+
where
104+
A: Allocator,
105+
{
106+
let value_ptr = allocate_zeroed_raw_bytes(alloc, rc_layout)?;
107+
108+
// SAFETY: `value_ptr` is newly allocated, so it is guaranteed to be valid.
109+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) };
110+
111+
Ok(value_ptr)
112+
}
113+
114+
/// Creates an allocator of type `A`, then tries to allocate a chunk of reference-counted memory
115+
/// with all zero bytes memory that is described by `rc_layout`.
116+
pub(crate) fn try_allocate_zeroed<A, const STRONG_COUNT: usize>(
117+
rc_layout: RcLayout,
118+
) -> Result<(RcValuePointer, A), AllocError>
119+
where
120+
A: Allocator + Default,
121+
{
122+
let alloc = A::default();
123+
124+
try_allocate_zeroed_in::<A, STRONG_COUNT>(&alloc, rc_layout).map(|value_ptr| (value_ptr, alloc))
125+
}
126+
127+
/// If `allocation_result` is `Ok`, initializes the reference counts with strong count
128+
/// `STRONG_COUNT` and weak count of 1 and returns a pointer to the value object, otherwise panic
129+
/// will be triggered by calling `alloc::handle_alloc_error`.
130+
///
131+
/// # Safety
132+
///
133+
/// If `allocation_result` is `Ok`, the pointer it contains must point to a valid reference-counted
134+
/// allocation that is allocated with `rc_layout`.
135+
#[cfg(not(no_global_oom_handling))]
136+
#[inline]
137+
unsafe fn handle_rc_allocation_result<const STRONG_COUNT: usize>(
138+
allocation_result: Result<RcValuePointer, AllocError>,
139+
rc_layout: RcLayout,
140+
) -> RcValuePointer {
141+
match allocation_result {
142+
Ok(value_ptr) => {
143+
// SAFETY: Caller guarantees the `value_ptr` points to a valid reference-counted`
144+
// allocation.
145+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) };
146+
147+
value_ptr
148+
}
149+
Err(AllocError) => alloc::handle_alloc_error(rc_layout.get()),
150+
}
151+
}
152+
153+
/// Allocates reference-counted memory that is described by `rc_layout` with `alloc`. The allocated
154+
/// memory has strong count of `STRONG_COUNT` and weak count of 1. If the allocation fails, panic
155+
/// will be triggered by calling `alloc::handle_alloc_error`.
156+
#[cfg(not(no_global_oom_handling))]
157+
#[inline]
158+
pub(crate) fn allocate_uninit_in<A, const STRONG_COUNT: usize>(
159+
alloc: &A,
160+
rc_layout: RcLayout,
161+
) -> RcValuePointer
162+
where
163+
A: Allocator,
164+
{
165+
let allocation_result = allocate_uninit_raw_bytes(alloc, rc_layout);
166+
167+
// SAFETY: `allocation_result` is the allocation result using `rc_layout`, which satisfies the
168+
// safety requirement of `handle_rc_allocation_result`.
169+
unsafe { handle_rc_allocation_result::<STRONG_COUNT>(allocation_result, rc_layout) }
170+
}
171+
172+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory that is
173+
/// described by `rc_layout`.
174+
#[cfg(not(no_global_oom_handling))]
175+
#[inline]
176+
pub(crate) fn allocate_uninit<A, const STRONG_COUNT: usize>(
177+
rc_layout: RcLayout,
178+
) -> (RcValuePointer, A)
179+
where
180+
A: Allocator + Default,
181+
{
182+
let alloc = A::default();
183+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(&alloc, rc_layout);
184+
185+
(value_ptr, alloc)
186+
}
187+
188+
/// Allocates reference-counted memory that is described by `rc_layout` with `alloc`. The allocated
189+
/// memory has strong count of `STRONG_COUNT` and weak count of 1, and the value memory is all zero
190+
/// bytes. If the allocation fails, panic will be triggered by calling `alloc::handle_alloc_error`.
191+
#[cfg(not(no_global_oom_handling))]
192+
pub(crate) fn allocate_zeroed_in<A, const STRONG_COUNT: usize>(
193+
alloc: &A,
194+
rc_layout: RcLayout,
195+
) -> RcValuePointer
196+
where
197+
A: Allocator,
198+
{
199+
let allocation_result = allocate_zeroed_raw_bytes(alloc, rc_layout);
200+
201+
// SAFETY: `allocation_result` is the allocation result using `rc_layout`, which satisfies the
202+
// safety requirement of `handle_rc_allocation_result`.
203+
unsafe { handle_rc_allocation_result::<STRONG_COUNT>(allocation_result, rc_layout) }
204+
}
205+
206+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory with all
207+
/// zero bytes that is described by `rc_layout`.
208+
#[cfg(not(no_global_oom_handling))]
209+
pub(crate) fn allocate_zeroed<A, const STRONG_COUNT: usize>(
210+
rc_layout: RcLayout,
211+
) -> (RcValuePointer, A)
212+
where
213+
A: Allocator + Default,
214+
{
215+
let alloc = A::default();
216+
let value_ptr = allocate_zeroed_in::<A, STRONG_COUNT>(&alloc, rc_layout);
217+
218+
(value_ptr, alloc)
219+
}
220+
221+
/// Allocates a reference-counted memory chunk for storing a value according to `rc_layout`, then
222+
/// initialize the value with `f`. If `f` panics, the allocated memory will be deallocated.
223+
#[cfg(not(no_global_oom_handling))]
224+
#[inline]
225+
pub(crate) fn allocate_with_in<A, F, const STRONG_COUNT: usize>(
226+
alloc: &A,
227+
rc_layout: RcLayout,
228+
f: F,
229+
) -> RcValuePointer
230+
where
231+
A: Allocator,
232+
F: FnOnce(RcValuePointer),
233+
{
234+
/// # Safety
235+
///
236+
/// - `value_ptr` points to a valid value location within a reference counted allocation
237+
/// that can be described with `rc_layout` and can be deallocated with `alloc`.
238+
/// - No access to the allocation can happen if the destructor of the returned guard get called.
239+
unsafe fn deallocate_on_drop<'a, A>(
240+
value_ptr: RcValuePointer,
241+
alloc: &'a A,
242+
rc_layout: RcLayout,
243+
) -> impl Drop + use<'a, A>
244+
where
245+
A: Allocator,
246+
{
247+
// SAFETY: Caller guarantees the validity of all arguments.
248+
DropGuard::new((), move |()| unsafe {
249+
deallocate::<A>(value_ptr, alloc, rc_layout);
250+
})
251+
}
252+
253+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
254+
let guard = unsafe { deallocate_on_drop(value_ptr, alloc, rc_layout) };
255+
256+
f(value_ptr);
257+
258+
mem::forget(guard);
259+
260+
value_ptr
261+
}
262+
263+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory that is
264+
/// described by `rc_layout`. `f` will be called with a pointer that points the value storage to
265+
/// initialize the allocated memory. If `f` panics, the allocated memory will be deallocated.
266+
#[cfg(not(no_global_oom_handling))]
267+
#[inline]
268+
pub(crate) fn allocate_with<A, F, const STRONG_COUNT: usize>(
269+
rc_layout: RcLayout,
270+
f: F,
271+
) -> (RcValuePointer, A)
272+
where
273+
A: Allocator + Default,
274+
F: FnOnce(RcValuePointer),
275+
{
276+
let alloc = A::default();
277+
let value_ptr = allocate_with_in::<A, F, STRONG_COUNT>(&alloc, rc_layout, f);
278+
279+
(value_ptr, alloc)
280+
}
281+
282+
/// Allocates reference-counted memory that has strong count of `STRONG_COUNT` and weak count of 1.
283+
/// The value will be initialized with data pointed to by `src_ptr`.
284+
///
285+
/// # Safety
286+
///
287+
/// - Memory pointed to by `src_ptr` has enough data to read for filling the value in an allocation
288+
/// that is described by `rc_layout`.
289+
#[cfg(not(no_global_oom_handling))]
290+
#[inline]
291+
pub(crate) unsafe fn allocate_with_bytes_in<A, const STRONG_COUNT: usize>(
292+
src_ptr: NonNull<()>,
293+
alloc: &A,
294+
rc_layout: RcLayout,
295+
) -> RcValuePointer
296+
where
297+
A: Allocator,
298+
{
299+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
300+
let value_size = rc_layout.value_size();
301+
302+
unsafe {
303+
ptr::copy_nonoverlapping::<u8>(
304+
src_ptr.as_ptr().cast(),
305+
value_ptr.as_ptr().as_ptr().cast(),
306+
value_size,
307+
);
308+
}
309+
310+
value_ptr
311+
}
312+
313+
/// Allocates a chunk of reference-counted memory with a value that is copied from `value`. This is
314+
/// safe because the return value is a pointer, which will not cause double unless caller calls the
315+
/// destructor manually, which requires `unsafe` codes.
316+
#[cfg(not(no_global_oom_handling))]
317+
#[inline]
318+
pub(crate) fn allocate_with_value_in<T, A, const STRONG_COUNT: usize>(
319+
src: &T,
320+
alloc: &A,
321+
) -> NonNull<T>
322+
where
323+
T: ?Sized,
324+
A: Allocator,
325+
{
326+
let src_ptr = NonNull::from(src);
327+
328+
// SAFETY: `src_ptr` is created from a reference, so it has correct metadata.
329+
let rc_layout = unsafe { RcLayout::from_value_ptr(src_ptr) };
330+
331+
let (src_ptr, metadata) = src_ptr.to_raw_parts();
332+
333+
// SAFETY: `src_ptr` comes from a reference to `T`, so it is guaranteed to have enough data to
334+
// fill the value in an allocation that is described by `rc_layout`.
335+
let value_ptr = unsafe { allocate_with_bytes_in::<A, STRONG_COUNT>(src_ptr, alloc, rc_layout) };
336+
337+
NonNull::from_raw_parts(value_ptr.as_ptr(), metadata)
338+
}
339+
340+
/// Creates an allocator of type `A`, then allocates a chunk of reference-counted memory with value
341+
/// copied from `value`.
342+
#[cfg(not(no_global_oom_handling))]
343+
#[inline]
344+
pub(crate) fn allocate_with_value<T, A, const STRONG_COUNT: usize>(value: &T) -> (NonNull<T>, A)
345+
where
346+
T: ?Sized,
347+
A: Allocator + Default,
348+
{
349+
let alloc = A::default();
350+
let value_ptr = allocate_with_value_in::<T, A, STRONG_COUNT>(value, &alloc);
351+
352+
(value_ptr, alloc)
353+
}
354+
355+
/// Deallocates a reference-counted allocation with a value object pointed to by `value_ptr`.
356+
///
357+
/// # Safety
358+
///
359+
/// - `value_ptr` points to a valid reference-counted allocation that is allocated using
360+
/// `rc_layout`.
361+
#[inline]
362+
pub(crate) unsafe fn deallocate<A>(value_ptr: RcValuePointer, alloc: &A, rc_layout: RcLayout)
363+
where
364+
A: Allocator,
365+
{
366+
let value_offset = rc_layout.value_offset();
367+
let allocation_ptr = unsafe { value_ptr.as_ptr().byte_sub(value_offset) };
368+
369+
unsafe { alloc.deallocate(allocation_ptr.cast(), rc_layout.get()) }
370+
}

0 commit comments

Comments
 (0)