62
62
63
63
#![ allow( dead_code) ]
64
64
65
+ use core:: alloc:: { AllocError , Allocator } ;
65
66
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 ;
66
76
67
77
mod rc_layout;
78
+ mod rc_value_pointer;
68
79
69
80
/// Stores reference counts.
70
81
#[ cfg_attr( target_pointer_width = "16" , repr( C , align( 2 ) ) ) ]
@@ -83,3 +94,347 @@ impl RefCounts {
83
94
Self { weak : UnsafeCell :: new ( 1 ) , strong : UnsafeCell :: new ( strong_count) }
84
95
}
85
96
}
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