6262
6363#![ allow( dead_code) ]
6464
65+ use core:: alloc:: { AllocError , Allocator } ;
6566use 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
6777mod 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