diff --git a/benchmark/benchmark.rs b/benchmark/benchmark.rs index 3c273ba0d..5c050e596 100644 --- a/benchmark/benchmark.rs +++ b/benchmark/benchmark.rs @@ -148,8 +148,8 @@ fn pass_by_object(testcase: T, mut reuse: S, iters: u64) -> ::capnp::Resul let mut rng = common::FastRand::new(); let (mut allocator_req, mut allocator_res) = reuse.get_allocators(); for _ in 0..iters { - let mut message_req = message::Builder::new(allocator_req); - let mut message_res = message::Builder::new(allocator_res); + let mut message_req = message::Builder::new(&mut allocator_req); + let mut message_res = message::Builder::new(&mut allocator_res); let expected = testcase.setup_request( &mut rng, @@ -162,9 +162,6 @@ fn pass_by_object(testcase: T, mut reuse: S, iters: u64) -> ::capnp::Resul testcase.check_response( message_res.get_root_as_reader()?, expected)?; - - allocator_req = message_req.into_allocator(); - allocator_res = message_res.into_allocator(); } Ok(()) } @@ -177,8 +174,8 @@ fn pass_by_bytes(testcase: T, mut reuse: S, compression: C, iters: u64) let mut rng = common::FastRand::new(); let (mut allocator_req, mut allocator_res) = reuse.get_allocators(); for _ in 0..iters { - let mut message_req = message::Builder::new(allocator_req); - let mut message_res = message::Builder::new(allocator_res); + let mut message_req = message::Builder::new(&mut allocator_req); + let mut message_res = message::Builder::new(&mut allocator_res); let expected = { let request = message_req.init_root(); @@ -214,8 +211,6 @@ fn pass_by_bytes(testcase: T, mut reuse: S, compression: C, iters: u64) let response_reader = message_reader.get_root()?; testcase.check_response(response_reader, expected)?; - allocator_req = message_req.into_allocator(); - allocator_res = message_res.into_allocator(); } Ok(()) } @@ -229,7 +224,7 @@ fn server(testcase: T, mut reuse: S, compression: C, iters: u64, let (mut allocator_res, _) = reuse.get_allocators(); for _ in 0..iters { use std::io::Write; - let mut message_res = message::Builder::new(allocator_res); + let mut message_res = message::Builder::new(&mut allocator_res); { let response = message_res.init_root(); @@ -242,7 +237,6 @@ fn server(testcase: T, mut reuse: S, compression: C, iters: u64, compression.write_message(&mut out_buffered, &mut message_res)?; out_buffered.flush()?; - allocator_res = message_res.into_allocator(); } Ok(()) } @@ -259,7 +253,7 @@ fn sync_client(testcase: T, mut reuse: S, compression: C, iters: u64) let (mut allocator_req, _) = reuse.get_allocators(); for _ in 0..iters { use std::io::Write; - let mut message_req = message::Builder::new(allocator_req); + let mut message_req = message::Builder::new(&mut allocator_req); let expected = { let request = message_req.init_root(); @@ -273,7 +267,6 @@ fn sync_client(testcase: T, mut reuse: S, compression: C, iters: u64) Default::default())?; let response_reader = message_reader.get_root()?; testcase.check_response(response_reader, expected)?; - allocator_req = message_req.into_allocator(); } Ok(()) } diff --git a/capnp/src/message.rs b/capnp/src/message.rs index fbf90fc1a..e28f373d0 100644 --- a/capnp/src/message.rs +++ b/capnp/src/message.rs @@ -287,6 +287,7 @@ pub unsafe trait Allocator { fn allocate_segment(&mut self, minimum_size: u32) -> (*mut u8, u32); /// Indicates that a segment, previously allocated via allocate_segment(), is no longer in use. + /// `word_size` is the length of the segment in words, as returned from `allocate_segment()`. fn deallocate_segment(&mut self, ptr: *mut u8, word_size: u32, words_used: u32); } @@ -469,6 +470,15 @@ impl Builder { } /// An Allocator whose first segment is a backed by a user-provided buffer. +/// +/// Recall that an `Allocator` implementation must ensure that allocated segments are +/// initially *zeroed*. `ScratchSpaceHeapAllocator` ensures that is the case by zeroing +/// the entire buffer upon initial construction, and then zeroing any *potentially used* +/// part of the buffer upon `deallocate_segment()`. +/// +/// You can reuse a `ScratchSpaceHeapAllocator` by calling `message::Builder::into_allocator()`, +/// or by initally passing it to `message::Builder::new()` as a `&mut ScratchSpaceHeapAllocator`. +/// Such reuse can save significant amounts of zeroing. pub struct ScratchSpaceHeapAllocator<'a> { scratch_space: &'a mut [u8], scratch_space_allocated: bool, @@ -478,9 +488,9 @@ pub struct ScratchSpaceHeapAllocator<'a> { impl <'a> ScratchSpaceHeapAllocator<'a> { /// Writes zeroes into the entire buffer and constructs a new allocator from it. /// - /// If you want to reuse the same buffer and to minimize the cost of zeroing for each message, - /// you can call `message::Builder::into_allocator()` to recover the allocator from - /// the previous message and then pass it into the new message. + /// If the buffer is large, this operation could be relatively expensive. If you want to reuse + /// the same scratch space in a later message, you should reuse the entire + /// `ScratchSpaceHeapAllocator`, to avoid paying this full cost again. pub fn new(scratch_space: &'a mut [u8]) -> ScratchSpaceHeapAllocator<'a> { #[cfg(not(feature = "unaligned"))] { @@ -535,3 +545,14 @@ unsafe impl <'a> Allocator for ScratchSpaceHeapAllocator<'a> { } } } + +unsafe impl <'a, A> Allocator for &'a mut A where A: Allocator { + fn allocate_segment(&mut self, minimum_size: u32) -> (*mut u8, u32) { + (*self).allocate_segment(minimum_size) + } + + fn deallocate_segment(&mut self, ptr: *mut u8, word_size: u32, words_used: u32) { + (*self).deallocate_segment(ptr, word_size, words_used) + } +} +