Skip to content

Commit 343a3e1

Browse files
committed
Add allocation functions
1 parent e39fdb7 commit 343a3e1

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
@@ -110,6 +110,7 @@
110110
#![feature(deprecated_suggestion)]
111111
#![feature(deref_pure_trait)]
112112
#![feature(dispatch_from_dyn)]
113+
#![feature(drop_guard)]
113114
#![feature(ergonomic_clones)]
114115
#![feature(error_generic_member_access)]
115116
#![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
@@ -63,7 +63,9 @@
6363

6464
use core::cell::UnsafeCell;
6565

66+
mod rc_alloc;
6667
mod rc_layout;
68+
mod rc_value_pointer;
6769

6870
/// Stores reference counts.
6971
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
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 using allocator `alloc` and
16+
/// layout `rc_layout`. 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 using allocator `alloc` and layout
35+
/// `rc_layout`. 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 counts in a reference-counted allocation pointed to by `value_ptr` with
54+
/// strong count of `STRONG_COUNT` and weak count of 1.
55+
///
56+
/// # Safety
57+
///
58+
/// - `value_ptr` must point 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 described by `rc_layout` with `alloc`. The
67+
/// allocated memory will have a strong count of `STRONG_COUNT` and a 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+
/// 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 reference-counted memory described by `rc_layout` with `alloc`. The allocated
97+
/// memory will have a strong count of `STRONG_COUNT` and a weak count of 1, and the value memory is
98+
/// 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 reference-counted memory with all zero
115+
/// bytes 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 of
128+
/// `STRONG_COUNT` and weak count of 1 and returns a pointer to the value object; otherwise,
129+
/// triggers a panic 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 was 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 allocates 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 allocates 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+
/// initializes 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 that
237+
/// 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 gets
239+
/// called.
240+
unsafe fn deallocate_on_drop<'a, A>(
241+
value_ptr: RcValuePointer,
242+
alloc: &'a A,
243+
rc_layout: RcLayout,
244+
) -> impl Drop + use<'a, A>
245+
where
246+
A: Allocator,
247+
{
248+
// SAFETY: Caller guarantees the validity of all arguments.
249+
DropGuard::new((), move |()| unsafe {
250+
deallocate::<A>(value_ptr, alloc, rc_layout);
251+
})
252+
}
253+
254+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
255+
let guard = unsafe { deallocate_on_drop(value_ptr, alloc, rc_layout) };
256+
257+
f(value_ptr);
258+
259+
mem::forget(guard);
260+
261+
value_ptr
262+
}
263+
264+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory that is
265+
/// described by `rc_layout`. `f` will be called with a pointer that points the value storage to
266+
/// initialize the allocated memory. If `f` panics, the allocated memory will be deallocated.
267+
#[cfg(not(no_global_oom_handling))]
268+
#[inline]
269+
pub(crate) fn allocate_with<A, F, const STRONG_COUNT: usize>(
270+
rc_layout: RcLayout,
271+
f: F,
272+
) -> (RcValuePointer, A)
273+
where
274+
A: Allocator + Default,
275+
F: FnOnce(RcValuePointer),
276+
{
277+
let alloc = A::default();
278+
let value_ptr = allocate_with_in::<A, F, STRONG_COUNT>(&alloc, rc_layout, f);
279+
280+
(value_ptr, alloc)
281+
}
282+
283+
/// Allocates reference-counted memory that has strong count of `STRONG_COUNT` and weak count of 1.
284+
/// The value will be initialized with data pointed to by `src_ptr`.
285+
///
286+
/// # Safety
287+
///
288+
/// - Memory pointed to by `src_ptr` has enough data to read for filling the value in an allocation
289+
/// that is described by `rc_layout`.
290+
#[cfg(not(no_global_oom_handling))]
291+
#[inline]
292+
pub(crate) unsafe fn allocate_with_bytes_in<A, const STRONG_COUNT: usize>(
293+
src_ptr: NonNull<()>,
294+
alloc: &A,
295+
rc_layout: RcLayout,
296+
) -> RcValuePointer
297+
where
298+
A: Allocator,
299+
{
300+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
301+
let value_size = rc_layout.value_size();
302+
303+
unsafe {
304+
ptr::copy_nonoverlapping::<u8>(
305+
src_ptr.as_ptr().cast(),
306+
value_ptr.as_ptr().as_ptr().cast(),
307+
value_size,
308+
);
309+
}
310+
311+
value_ptr
312+
}
313+
314+
/// Allocates a chunk of reference-counted memory with a value that is copied from `value`. This is
315+
/// safe because the return value is a pointer, which will not cause a double free unless the caller
316+
/// calls the destructor manually, which requires `unsafe` code.
317+
#[cfg(not(no_global_oom_handling))]
318+
#[inline]
319+
pub(crate) fn allocate_with_value_in<T, A, const STRONG_COUNT: usize>(
320+
src: &T,
321+
alloc: &A,
322+
) -> NonNull<T>
323+
where
324+
T: ?Sized,
325+
A: Allocator,
326+
{
327+
let src_ptr = NonNull::from(src);
328+
329+
// SAFETY: `src_ptr` is created from a reference, so it has correct metadata.
330+
let rc_layout = unsafe { RcLayout::from_value_ptr(src_ptr) };
331+
332+
let (src_ptr, metadata) = src_ptr.to_raw_parts();
333+
334+
// SAFETY: `src_ptr` comes from a reference to `T`, so it is guaranteed to have enough data to
335+
// fill the value in an allocation that is described by `rc_layout`.
336+
let value_ptr = unsafe { allocate_with_bytes_in::<A, STRONG_COUNT>(src_ptr, alloc, rc_layout) };
337+
338+
NonNull::from_raw_parts(value_ptr.as_ptr(), metadata)
339+
}
340+
341+
/// Creates an allocator of type `A`, then allocates a chunk of reference-counted memory with value
342+
/// copied from `value`.
343+
#[cfg(not(no_global_oom_handling))]
344+
#[inline]
345+
pub(crate) fn allocate_with_value<T, A, const STRONG_COUNT: usize>(value: &T) -> (NonNull<T>, A)
346+
where
347+
T: ?Sized,
348+
A: Allocator + Default,
349+
{
350+
let alloc = A::default();
351+
let value_ptr = allocate_with_value_in::<T, A, STRONG_COUNT>(value, &alloc);
352+
353+
(value_ptr, alloc)
354+
}
355+
356+
/// Deallocates a reference-counted allocation with a value object pointed to by `value_ptr`.
357+
///
358+
/// # Safety
359+
///
360+
/// - `value_ptr` points to a valid reference-counted allocation that was allocated using
361+
/// `rc_layout`.
362+
#[inline]
363+
pub(crate) unsafe fn deallocate<A>(value_ptr: RcValuePointer, alloc: &A, rc_layout: RcLayout)
364+
where
365+
A: Allocator,
366+
{
367+
let value_offset = rc_layout.value_offset();
368+
let allocation_ptr = unsafe { value_ptr.as_ptr().byte_sub(value_offset) };
369+
370+
unsafe { alloc.deallocate(allocation_ptr.cast(), rc_layout.get()) }
371+
}

0 commit comments

Comments
 (0)