Skip to content

Commit abe9c27

Browse files
committed
Add allocation functions
1 parent 8d8878a commit abe9c27

File tree

2 files changed

+411
-0
lines changed

2 files changed

+411
-0
lines changed

library/alloc/src/raw_rc/mod.rs

Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,20 @@
6262
6363
#![allow(dead_code)]
6464

65+
use core::alloc::{AllocError, Allocator};
6566
use core::cell::UnsafeCell;
67+
#[cfg(not(no_global_oom_handling))]
68+
use core::mem;
69+
#[cfg(not(no_global_oom_handling))]
70+
use core::ptr::{self, NonNull};
71+
72+
#[cfg(not(no_global_oom_handling))]
73+
use crate::alloc;
74+
use crate::raw_rc::rc_layout::RcLayout;
75+
use crate::raw_rc::rc_value_pointer::RcValuePointer;
6676

6777
mod rc_layout;
78+
mod rc_value_pointer;
6879

6980
/// Stores reference counts.
7081
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
@@ -83,3 +94,347 @@ impl RefCounts {
8394
Self { weak: UnsafeCell::new(1), strong: UnsafeCell::new(strong_count) }
8495
}
8596
}
97+
98+
/// Allocates uninitialized memory for a reference-counted allocation with allocator `alloc` and
99+
/// layout `RcLayout`. Returns a pointer to the value location.
100+
#[inline]
101+
fn allocate_uninit_raw_bytes<A>(
102+
alloc: &A,
103+
rc_layout: RcLayout,
104+
) -> Result<RcValuePointer, AllocError>
105+
where
106+
A: Allocator,
107+
{
108+
let allocation_result = alloc.allocate(rc_layout.get());
109+
110+
// SAFETY: `allocation_ptr` is allocated using `rc_layout`, so it is guaranteed to point to
111+
// a valid reference-counted allocation, so we can safety acquire the corresponding value
112+
// pointer.
113+
allocation_result.map(|allocation_ptr| unsafe {
114+
RcValuePointer::new(allocation_ptr.cast().byte_add(rc_layout.value_offset()))
115+
})
116+
}
117+
118+
/// Allocates zeroed memory for a reference-counted allocation with allocator `alloc` and layout
119+
/// `RcLayout`. Returns a pointer to the value location.
120+
#[inline]
121+
fn allocate_zeroed_raw_bytes<A>(
122+
alloc: &A,
123+
rc_layout: RcLayout,
124+
) -> Result<RcValuePointer, AllocError>
125+
where
126+
A: Allocator,
127+
{
128+
let allocation_result = alloc.allocate_zeroed(rc_layout.get());
129+
130+
// SAFETY: `allocation_ptr` is allocated using `rc_layout`, so it is guaranteed to point to
131+
// a valid reference-counted allocation, so we can safety acquire the corresponding value
132+
// pointer.
133+
allocation_result.map(|allocation_ptr| unsafe {
134+
RcValuePointer::new(allocation_ptr.cast().byte_add(rc_layout.value_offset()))
135+
})
136+
}
137+
138+
/// Initializes reference counters in a reference-counted allocation pointed to by `value_ptr`
139+
/// with strong count of `STRONG_COUNT` and weak count of 1.
140+
///
141+
/// # Safety
142+
///
143+
/// - `value_ptr` points to a valid reference-counted allocation.
144+
#[inline]
145+
unsafe fn init_rc_allocation<const STRONG_COUNT: usize>(value_ptr: RcValuePointer) {
146+
// SAFETY: Caller guarantees the `value_ptr` points to a valid reference-counted allocation, so
147+
// we can write to the corresponding `RefCounts` object.
148+
unsafe { value_ptr.ref_counts_ptr().write(const { RefCounts::new(STRONG_COUNT) }) };
149+
}
150+
151+
/// Tries to allocate a chunk of reference-counted memory that is described by `rc_layout` with
152+
/// `alloc`. The allocated memory has strong count of `STRONG_COUNT` and weak count of 1.
153+
fn try_allocate_uninit_in<A, const STRONG_COUNT: usize>(
154+
alloc: &A,
155+
rc_layout: RcLayout,
156+
) -> Result<RcValuePointer, AllocError>
157+
where
158+
A: Allocator,
159+
{
160+
allocate_uninit_raw_bytes(alloc, rc_layout).inspect(|&value_ptr| {
161+
// SAFETY: `value_ptr` is newly allocated, so it is guaranteed to be valid.
162+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) };
163+
})
164+
}
165+
166+
/// Creates an allocator of type `A`, then tries to allocate a chunk of reference-counted memory
167+
/// that is described by `rc_layout`.
168+
fn try_allocate_uninit<A, const STRONG_COUNT: usize>(
169+
rc_layout: RcLayout,
170+
) -> Result<(RcValuePointer, A), AllocError>
171+
where
172+
A: Allocator + Default,
173+
{
174+
let alloc = A::default();
175+
176+
try_allocate_uninit_in::<A, STRONG_COUNT>(&alloc, rc_layout).map(|value_ptr| (value_ptr, alloc))
177+
}
178+
179+
/// Tries to allocate a reference-counted memory that is described by `rc_layout` with `alloc`. The
180+
/// allocated memory has strong count of `STRONG_COUNT` and weak count of 1, and the value memory
181+
/// is all zero bytes.
182+
fn try_allocate_zeroed_in<A, const STRONG_COUNT: usize>(
183+
alloc: &A,
184+
rc_layout: RcLayout,
185+
) -> Result<RcValuePointer, AllocError>
186+
where
187+
A: Allocator,
188+
{
189+
allocate_zeroed_raw_bytes(alloc, rc_layout).inspect(|&value_ptr| {
190+
// SAFETY: `value_ptr` is newly allocated, so it is guaranteed to be valid.
191+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) }
192+
})
193+
}
194+
195+
/// Creates an allocator of type `A`, then tries to allocate a chunk of reference-counted memory
196+
/// with all zero bytes memory that is described by `rc_layout`.
197+
fn try_allocate_zeroed<A, const STRONG_COUNT: usize>(
198+
rc_layout: RcLayout,
199+
) -> Result<(RcValuePointer, A), AllocError>
200+
where
201+
A: Allocator + Default,
202+
{
203+
let alloc = A::default();
204+
205+
try_allocate_zeroed_in::<A, STRONG_COUNT>(&alloc, rc_layout).map(|value_ptr| (value_ptr, alloc))
206+
}
207+
208+
/// If `allocation_result` is `Ok`, initializes the reference counts with strong count
209+
/// `STRONG_COUNT` and weak count of 1 and returns a pointer to the value object, otherwise panic
210+
/// will be triggered by calling `alloc::handle_alloc_error`.
211+
///
212+
/// # Safety
213+
///
214+
/// If `allocation_result` is `Ok`, the pointer it contains must point to a valid reference-counted
215+
/// allocation that is allocated with `rc_layout`.
216+
#[cfg(not(no_global_oom_handling))]
217+
#[inline]
218+
unsafe fn handle_rc_allocation_result<const STRONG_COUNT: usize>(
219+
allocation_result: Result<RcValuePointer, AllocError>,
220+
rc_layout: RcLayout,
221+
) -> RcValuePointer {
222+
match allocation_result {
223+
Ok(value_ptr) => {
224+
// SAFETY: Caller guarantees the `value_ptr` points to a valid reference-counted`
225+
// allocation.
226+
unsafe { init_rc_allocation::<STRONG_COUNT>(value_ptr) };
227+
228+
value_ptr
229+
}
230+
Err(AllocError) => alloc::handle_alloc_error(rc_layout.get()),
231+
}
232+
}
233+
234+
/// Allocates reference-counted memory that is described by `rc_layout` with `alloc`. The allocated
235+
/// memory has strong count of `STRONG_COUNT` and weak count of 1. If the allocation fails, panic
236+
/// will be triggered by calling `alloc::handle_alloc_error`.
237+
#[cfg(not(no_global_oom_handling))]
238+
#[inline]
239+
fn allocate_uninit_in<A, const STRONG_COUNT: usize>(
240+
alloc: &A,
241+
rc_layout: RcLayout,
242+
) -> RcValuePointer
243+
where
244+
A: Allocator,
245+
{
246+
let allocation_result = allocate_uninit_raw_bytes(alloc, rc_layout);
247+
248+
// SAFETY: `allocation_result` is the allocation result using `rc_layout`, which satisfies the
249+
// safety requirement of `handle_rc_allocation_result`.
250+
unsafe { handle_rc_allocation_result::<STRONG_COUNT>(allocation_result, rc_layout) }
251+
}
252+
253+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory that is
254+
/// described by `rc_layout`.
255+
#[cfg(not(no_global_oom_handling))]
256+
#[inline]
257+
fn allocate_uninit<A, const STRONG_COUNT: usize>(rc_layout: RcLayout) -> (RcValuePointer, A)
258+
where
259+
A: Allocator + Default,
260+
{
261+
let alloc = A::default();
262+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(&alloc, rc_layout);
263+
264+
(value_ptr, alloc)
265+
}
266+
267+
/// Allocates reference-counted memory that is described by `rc_layout` with `alloc`. The allocated
268+
/// memory has strong count of `STRONG_COUNT` and weak count of 1, and the value memory is all zero
269+
/// bytes. If the allocation fails, panic will be triggered by calling `alloc::handle_alloc_error`.
270+
#[cfg(not(no_global_oom_handling))]
271+
fn allocate_zeroed_in<A, const STRONG_COUNT: usize>(
272+
alloc: &A,
273+
rc_layout: RcLayout,
274+
) -> RcValuePointer
275+
where
276+
A: Allocator,
277+
{
278+
let allocation_result = allocate_zeroed_raw_bytes(alloc, rc_layout);
279+
280+
// SAFETY: `allocation_result` is the allocation result using `rc_layout`, which satisfies the
281+
// safety requirement of `handle_rc_allocation_result`.
282+
unsafe { handle_rc_allocation_result::<STRONG_COUNT>(allocation_result, rc_layout) }
283+
}
284+
285+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory with all
286+
/// zero bytes that is described by `rc_layout`.
287+
#[cfg(not(no_global_oom_handling))]
288+
fn allocate_zeroed<A, const STRONG_COUNT: usize>(rc_layout: RcLayout) -> (RcValuePointer, A)
289+
where
290+
A: Allocator + Default,
291+
{
292+
let alloc = A::default();
293+
let value_ptr = allocate_zeroed_in::<A, STRONG_COUNT>(&alloc, rc_layout);
294+
295+
(value_ptr, alloc)
296+
}
297+
298+
/// Allocates a reference-counted memory chunk for storing a value according to `rc_layout`, then
299+
/// initialize the value with `f`. If `f` panics, the allocated memory will be deallocated.
300+
#[cfg(not(no_global_oom_handling))]
301+
#[inline]
302+
fn allocate_with_in<A, F, const STRONG_COUNT: usize>(
303+
alloc: &A,
304+
rc_layout: RcLayout,
305+
f: F,
306+
) -> RcValuePointer
307+
where
308+
A: Allocator,
309+
F: FnOnce(RcValuePointer),
310+
{
311+
struct Guard<'a, A>
312+
where
313+
A: Allocator,
314+
{
315+
value_ptr: RcValuePointer,
316+
alloc: &'a A,
317+
rc_layout: RcLayout,
318+
}
319+
320+
impl<'a, A> Drop for Guard<'a, A>
321+
where
322+
A: Allocator,
323+
{
324+
fn drop(&mut self) {
325+
unsafe { deallocate::<A>(self.value_ptr, self.alloc, self.rc_layout) };
326+
}
327+
}
328+
329+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
330+
let guard = Guard { value_ptr, alloc, rc_layout };
331+
332+
f(value_ptr);
333+
334+
mem::forget(guard);
335+
336+
value_ptr
337+
}
338+
339+
/// Creates an allocator of type `A`, then allocate a chunk of reference-counted memory that is
340+
/// described by `rc_layout`. `f` will be called with a pointer that points the value storage to
341+
/// initialize the allocated memory. If `f` panics, the allocated memory will be deallocated.
342+
#[cfg(not(no_global_oom_handling))]
343+
#[inline]
344+
fn allocate_with<A, F, const STRONG_COUNT: usize>(rc_layout: RcLayout, f: F) -> (RcValuePointer, A)
345+
where
346+
A: Allocator + Default,
347+
F: FnOnce(RcValuePointer),
348+
{
349+
let alloc = A::default();
350+
let value_ptr = allocate_with_in::<A, F, STRONG_COUNT>(&alloc, rc_layout, f);
351+
352+
(value_ptr, alloc)
353+
}
354+
355+
/// Allocates reference-counted memory that has strong count of `STRONG_COUNT` and weak count of 1.
356+
/// The value will be initialized with data pointed to by `src_ptr`.
357+
///
358+
/// # Safety
359+
///
360+
/// - Memory pointed to by `src_ptr` has enough data to read for filling the value in an allocation
361+
/// that is described by `rc_layout`.
362+
#[cfg(not(no_global_oom_handling))]
363+
#[inline]
364+
unsafe fn allocate_with_bytes_in<A, const STRONG_COUNT: usize>(
365+
src_ptr: NonNull<()>,
366+
alloc: &A,
367+
rc_layout: RcLayout,
368+
) -> RcValuePointer
369+
where
370+
A: Allocator,
371+
{
372+
let value_ptr = allocate_uninit_in::<A, STRONG_COUNT>(alloc, rc_layout);
373+
let value_size = rc_layout.value_size();
374+
375+
unsafe {
376+
ptr::copy_nonoverlapping::<u8>(
377+
src_ptr.as_ptr().cast(),
378+
value_ptr.as_ptr().as_ptr().cast(),
379+
value_size,
380+
);
381+
}
382+
383+
value_ptr
384+
}
385+
386+
/// Allocates a chunk of reference-counted memory with a value that is copied from `value`. This is
387+
/// safe because the return value is a pointer, which will not cause double unless caller calls the
388+
/// destructor manually, which requires `unsafe` codes.
389+
#[cfg(not(no_global_oom_handling))]
390+
#[inline]
391+
fn allocate_with_value_in<T, A, const STRONG_COUNT: usize>(src: &T, alloc: &A) -> NonNull<T>
392+
where
393+
T: ?Sized,
394+
A: Allocator,
395+
{
396+
let src_ptr = NonNull::from(src);
397+
398+
// SAFETY: `src_ptr` is created from a reference, so it has correct metadata.
399+
let rc_layout = unsafe { RcLayout::from_value_ptr(src_ptr) };
400+
401+
let (src_ptr, metadata) = src_ptr.to_raw_parts();
402+
403+
// SAFETY: `src_ptr` comes from a reference to `T`, so it is guaranteed to have enough data to
404+
// fill the value in an allocation that is described by `rc_layout`.
405+
let value_ptr = unsafe { allocate_with_bytes_in::<A, STRONG_COUNT>(src_ptr, alloc, rc_layout) };
406+
407+
NonNull::from_raw_parts(value_ptr.as_ptr(), metadata)
408+
}
409+
410+
/// Creates an allocator of type `A`, then allocates a chunk of reference-counted memory with value
411+
/// copied from `value`.
412+
#[cfg(not(no_global_oom_handling))]
413+
#[inline]
414+
fn allocate_with_value<T, A, const STRONG_COUNT: usize>(value: &T) -> (NonNull<T>, A)
415+
where
416+
T: ?Sized,
417+
A: Allocator + Default,
418+
{
419+
let alloc = A::default();
420+
let value_ptr = allocate_with_value_in::<T, A, STRONG_COUNT>(value, &alloc);
421+
422+
(value_ptr, alloc)
423+
}
424+
425+
/// Deallocates a reference-counted allocation with a value object pointed to by `value_ptr`.
426+
///
427+
/// # Safety
428+
///
429+
/// - `value_ptr` points to a valid reference-counted allocation that is allocated using
430+
/// `rc_layout`.
431+
#[inline]
432+
unsafe fn deallocate<A>(value_ptr: RcValuePointer, alloc: &A, rc_layout: RcLayout)
433+
where
434+
A: Allocator,
435+
{
436+
let value_offset = rc_layout.value_offset();
437+
let allocation_ptr = unsafe { value_ptr.as_ptr().byte_sub(value_offset) };
438+
439+
unsafe { alloc.deallocate(allocation_ptr.cast(), rc_layout.get()) }
440+
}

0 commit comments

Comments
 (0)