@@ -262,32 +262,20 @@ impl <A, T> From<Builder<A>> for TypedReader<Builder<A>, T>
262
262
/// An object that allocates memory for a Cap'n Proto message as it is being built.
263
263
pub unsafe trait Allocator {
264
264
/// Allocates zeroed memory for a new segment, returning a pointer to the start of the segment
265
- /// and a u32 indicating the length of the segment (in words).
265
+ /// and a u32 indicating the length of the segment (in words). The allocated segment must be
266
+ /// at least `minimum_size` words long.
266
267
///
267
268
/// UNSAFETY ALERT: Implementors must ensure all of the following:
268
269
/// 1. the returned memory is initialized to all zeroes,
269
- /// 2. the returned memory is valid for the remaining lifetime of the Allocator object ,
270
+ /// 2. the returned memory is valid until deallocate_segment() is called on it ,
270
271
/// 3. the memory doesn't overlap with other allocated memory,
271
272
/// 4. the memory is 8-byte aligned (or the "unaligned" feature is enabled for the capnp crate).
272
273
fn allocate_segment ( & mut self , minimum_size : u32 ) -> ( * mut u8 , u32 ) ;
273
274
274
- /// Called before the Allocator is dropped, to allow the first segment to be re-zeroed
275
- /// before possibly being reused in another allocator.
276
- fn pre_drop ( & mut self , _segment0_currently_allocated : u32 ) { }
275
+ /// Indicates that a segment, previously allocated via allocate_segment(), is no longer in use.
276
+ fn deallocate_segment ( & mut self , ptr : * mut u8 , word_size : u32 , words_used : u32 ) ;
277
277
}
278
278
279
- /* TODO(version 0.9): update to a more user-friendly trait here?
280
- pub trait Allocator {
281
- /// Allocates memory for a new segment.
282
- fn allocate_segment(&mut self, minimum_size: u32) -> Result<()>;
283
-
284
- fn get_segment<'a>(&'a self, id: u32) -> &'a [u8];
285
- fn get_segment_mut<'a>(&'a mut self, id: u32) -> &'a mut [u8];
286
-
287
- fn pre_drop(&mut self, _segment0_currently_allocated: u32) {}
288
- }
289
- */
290
-
291
279
/// A container used to build a message.
292
280
pub struct Builder < A > where A : Allocator {
293
281
arena : BuilderArenaImpl < A > ,
@@ -380,6 +368,10 @@ impl <A> Builder<A> where A: Allocator {
380
368
nesting_limit : i32:: max_value ( )
381
369
} )
382
370
}
371
+
372
+ pub fn into_allocator ( self ) -> A {
373
+ self . arena . into_allocator ( )
374
+ }
383
375
}
384
376
385
377
impl < A > ReaderSegments for Builder < A > where A : Allocator {
@@ -392,11 +384,9 @@ impl <A> ReaderSegments for Builder<A> where A: Allocator {
392
384
}
393
385
}
394
386
395
- /// Standard segment allocator. Allocates each segment as a `Vec<Word>`, where `Word` has
396
- /// 8-byte alignment.
387
+ /// Standard segment allocator. Allocates each segment via `alloc::alloc::alloc_zeroed()`.
397
388
#[ derive( Debug ) ]
398
389
pub struct HeapAllocator {
399
- owned_memory : Vec < Vec < crate :: Word > > ,
400
390
next_size : u32 ,
401
391
allocation_strategy : AllocationStrategy ,
402
392
}
@@ -412,8 +402,7 @@ pub const SUGGESTED_ALLOCATION_STRATEGY: AllocationStrategy = AllocationStrategy
412
402
413
403
impl HeapAllocator {
414
404
pub fn new ( ) -> HeapAllocator {
415
- HeapAllocator { owned_memory : Vec :: new ( ) ,
416
- next_size : SUGGESTED_FIRST_SEGMENT_WORDS ,
405
+ HeapAllocator { next_size : SUGGESTED_FIRST_SEGMENT_WORDS ,
417
406
allocation_strategy : SUGGESTED_ALLOCATION_STRATEGY }
418
407
}
419
408
@@ -431,16 +420,24 @@ impl HeapAllocator {
431
420
unsafe impl Allocator for HeapAllocator {
432
421
fn allocate_segment ( & mut self , minimum_size : u32 ) -> ( * mut u8 , u32 ) {
433
422
let size = :: std:: cmp:: max ( minimum_size, self . next_size ) ;
434
- let mut new_words = crate :: Word :: allocate_zeroed_vec ( size as usize ) ;
435
- let ptr = new_words. as_mut_ptr ( ) as * mut u8 ;
436
- self . owned_memory . push ( new_words) ;
437
-
423
+ let ptr = unsafe {
424
+ alloc:: alloc:: alloc_zeroed ( alloc:: alloc:: Layout :: from_size_align ( size as usize * BYTES_PER_WORD , 8 ) . unwrap ( ) )
425
+ } ;
438
426
match self . allocation_strategy {
439
427
AllocationStrategy :: GrowHeuristically => { self . next_size += size; }
440
428
_ => { }
441
429
}
442
430
( ptr, size as u32 )
443
431
}
432
+
433
+ fn deallocate_segment ( & mut self , ptr : * mut u8 , word_size : u32 , _words_used : u32 ) {
434
+ unsafe {
435
+ alloc:: alloc:: dealloc ( ptr,
436
+ alloc:: alloc:: Layout :: from_size_align ( word_size as usize * BYTES_PER_WORD , 8 ) . unwrap ( ) ) ;
437
+ }
438
+ // TODO move this next_size stuff out of what the Allocator trait should be resposible for.
439
+ self . next_size = SUGGESTED_FIRST_SEGMENT_WORDS ;
440
+ }
444
441
}
445
442
446
443
impl Builder < HeapAllocator > {
@@ -449,57 +446,59 @@ impl Builder<HeapAllocator> {
449
446
}
450
447
}
451
448
452
- #[ derive( Debug ) ]
453
- pub struct ScratchSpace < ' a > {
454
- slice : & ' a mut [ u8 ] ,
455
- in_use : bool ,
456
- }
457
-
458
- impl < ' a > ScratchSpace < ' a > {
459
- pub fn new ( slice : & ' a mut [ u8 ] ) -> ScratchSpace < ' a > {
460
- ScratchSpace { slice : slice, in_use : false }
461
- }
462
- }
463
-
464
- pub struct ScratchSpaceHeapAllocator < ' a , ' b : ' a > {
465
- scratch_space : & ' a mut ScratchSpace < ' b > ,
449
+ /// An Allocator whose first segment is a backed by a user-provided buffer.
450
+ pub struct ScratchSpaceHeapAllocator < ' a > {
451
+ scratch_space : & ' a mut [ u8 ] ,
452
+ scratch_space_allocated : bool ,
466
453
allocator : HeapAllocator ,
467
454
}
468
455
469
- impl < ' a , ' b : ' a > ScratchSpaceHeapAllocator < ' a , ' b > {
470
- pub fn new ( scratch_space : & ' a mut ScratchSpace < ' b > ) -> ScratchSpaceHeapAllocator < ' a , ' b > {
456
+ impl < ' a > ScratchSpaceHeapAllocator < ' a > {
457
+ /// Writes zeroes into the entire buffer and constructs a new allocator from it.
458
+ ///
459
+ /// If you want to reuse the same buffer and to minimize the cost of zeroing for each message,
460
+ /// you can call `message::Builder::into_allocator()` to recover the allocator from
461
+ /// the previous message and then pass it into the new message.
462
+ pub fn new ( scratch_space : & ' a mut [ u8 ] ) -> ScratchSpaceHeapAllocator < ' a > {
463
+ // We need to ensure that the buffer is zeroed.
464
+ for b in & mut scratch_space[ ..] {
465
+ * b = 0 ;
466
+ }
471
467
ScratchSpaceHeapAllocator { scratch_space : scratch_space,
468
+ scratch_space_allocated : false ,
472
469
allocator : HeapAllocator :: new ( ) }
473
470
}
474
471
475
- pub fn second_segment_words ( self , value : u32 ) -> ScratchSpaceHeapAllocator < ' a , ' b > {
476
- ScratchSpaceHeapAllocator { scratch_space : self . scratch_space ,
477
- allocator : self . allocator . first_segment_words ( value) }
472
+ pub fn second_segment_words ( self , value : u32 ) -> ScratchSpaceHeapAllocator < ' a > {
473
+ ScratchSpaceHeapAllocator { allocator : self . allocator . first_segment_words ( value) , ..self }
478
474
479
475
}
480
476
481
- pub fn allocation_strategy ( self , value : AllocationStrategy ) -> ScratchSpaceHeapAllocator < ' a , ' b > {
482
- ScratchSpaceHeapAllocator { scratch_space : self . scratch_space ,
483
- allocator : self . allocator . allocation_strategy ( value) }
477
+ pub fn allocation_strategy ( self , value : AllocationStrategy ) -> ScratchSpaceHeapAllocator < ' a > {
478
+ ScratchSpaceHeapAllocator { allocator : self . allocator . allocation_strategy ( value) , ..self }
484
479
}
485
-
486
480
}
487
481
488
- unsafe impl < ' a , ' b : ' a > Allocator for ScratchSpaceHeapAllocator < ' a , ' b > {
482
+ unsafe impl < ' a > Allocator for ScratchSpaceHeapAllocator < ' a > {
489
483
fn allocate_segment ( & mut self , minimum_size : u32 ) -> ( * mut u8 , u32 ) {
490
- if ! self . scratch_space . in_use {
491
- self . scratch_space . in_use = true ;
492
- ( self . scratch_space . slice . as_mut_ptr ( ) , ( self . scratch_space . slice . len ( ) / BYTES_PER_WORD ) as u32 )
484
+ if ( minimum_size as usize ) < ( self . scratch_space . len ( ) / BYTES_PER_WORD ) && ! self . scratch_space_allocated {
485
+ self . scratch_space_allocated = true ;
486
+ ( self . scratch_space . as_mut_ptr ( ) , ( self . scratch_space . len ( ) / BYTES_PER_WORD ) as u32 )
493
487
} else {
494
488
self . allocator . allocate_segment ( minimum_size)
495
489
}
496
490
}
497
491
498
- fn pre_drop ( & mut self , segment0_currently_allocated : u32 ) {
499
- let ptr = self . scratch_space . slice . as_mut_ptr ( ) ;
500
- unsafe {
501
- :: std:: ptr:: write_bytes ( ptr, 0u8 , ( segment0_currently_allocated as usize ) * BYTES_PER_WORD ) ;
492
+ fn deallocate_segment ( & mut self , ptr : * mut u8 , word_size : u32 , words_used : u32 ) {
493
+ if ptr == self . scratch_space . as_mut_ptr ( ) {
494
+ // Rezero the slice to allow reuse of the allocator. We only need to write
495
+ // words that we know might contain nonzero values.
496
+ unsafe {
497
+ :: std:: ptr:: write_bytes ( ptr, 0u8 , ( words_used as usize ) * BYTES_PER_WORD ) ;
498
+ }
499
+ self . scratch_space_allocated = false ;
500
+ } else {
501
+ self . allocator . deallocate_segment ( ptr, word_size, words_used) ;
502
502
}
503
- self . scratch_space . in_use = false ;
504
503
}
505
504
}
0 commit comments