use std::fmt::Debug; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; /// A reusable allocation that can be lent as an empty `Vec` for any `T` with the same size /// and alignment as `A`. /// /// Example: `VersatileBuffer` can lend `Vec`, `Vec`, `Vec`, ... /// /// Hidden contract: `buffer.len()` is always 0. No `A` value is ever built: `A` only sets the /// size and alignment of the allocation. Only the allocation is reused. pub struct VersatileBuffer { buffer: Vec, } impl Default for VersatileBuffer { #[inline(always)] fn default() -> Self { VersatileBuffer { buffer: Vec::new() } } } impl VersatileBuffer { #[inline(always)] pub fn new() -> Self { Self::default() } #[inline(always)] pub fn with_capacity(capacity: usize) -> Self { VersatileBuffer { buffer: Vec::with_capacity(capacity), } } /// Lends the allocation as an empty `Vec`. /// /// The allocation goes back to `self` when the returned guard is dropped. /// If the guard is leaked (e.g. `mem::forget`), the allocation is leaked too and `self` /// starts again from an empty `Vec`. #[inline(always)] pub fn borrow(&mut self) -> ClearOnDrop<'_, T, A> { const { assert!(std::mem::size_of::() == std::mem::size_of::()); assert!(std::mem::align_of::() == std::mem::align_of::()); } let mut storage = ManuallyDrop::new(std::mem::take(&mut self.buffer)); let capacity = storage.capacity(); let ptr = storage.as_mut_ptr().cast::(); // SAFETY: `ptr` and `capacity` come from a `Vec` we own. `T` and `A` have the same // size and alignment (checked above), so the allocation is valid for a `Vec` with the // same capacity. Length is 0, so nothing is read. let values: Vec = unsafe { Vec::from_raw_parts(ptr, 0, capacity) }; ClearOnDrop { home: &mut self.buffer, values, } } } /// An empty `Vec` lent by a `VersatileBuffer`. /// /// On drop, the values are dropped and the allocation goes back to the `VersatileBuffer`. /// This also happens if the code using the vec panics. pub struct ClearOnDrop<'a, T, A> { home: &'a mut Vec, values: Vec, } impl Debug for ClearOnDrop<'_, T, A> { #[inline(always)] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.values.fmt(f) } } impl Drop for ClearOnDrop<'_, T, A> { #[inline(always)] fn drop(&mut self) { self.values.clear(); let mut values = ManuallyDrop::new(std::mem::take(&mut self.values)); let capacity = values.capacity(); let ptr = values.as_mut_ptr().cast::(); // SAFETY: same layout argument as in `VersatileBuffer::borrow`. `values` may have been // reallocated by `push`, but always with `T`'s layout, which equals `A`'s. let buffer: Vec = unsafe { Vec::from_raw_parts(ptr, 0, capacity) }; // `home` still holds the empty `Vec` left by `mem::take` in `borrow`. Forgetting it // instead of dropping it removes a dead dealloc branch, which the compiler cannot prove // dead on its own. std::mem::forget(std::mem::replace(self.home, buffer)); } } impl Deref for ClearOnDrop<'_, T, A> { type Target = Vec; #[inline(always)] fn deref(&self) -> &Self::Target { &self.values } } impl DerefMut for ClearOnDrop<'_, T, A> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.values } } #[cfg(test)] mod tests { use std::cell::Cell; use crate::versatile_buffer::{ClearOnDrop, VersatileBuffer}; #[test] fn test_versatile_borrowable_buffer() { let mut my_buff: VersatileBuffer = VersatileBuffer::default(); { let mut arr: ClearOnDrop = my_buff.borrow(); arr.push(1u64); arr.push(2u64); assert_eq!(&arr[..], &[1u64, 2u64]); } { let arr: ClearOnDrop = my_buff.borrow(); assert!(arr.is_empty()); } } #[test] fn test_versatile_buffer_keeps_capacity() { let mut buffer: VersatileBuffer = VersatileBuffer::with_capacity(16); { let mut values = buffer.borrow::(); assert!(values.capacity() >= 16); values.extend(0..100u64); } let values = buffer.borrow::(); assert!(values.is_empty()); assert!(values.capacity() >= 100); } #[test] fn test_versatile_buffer_forgotten_guard() { let mut buffer: VersatileBuffer = VersatileBuffer::new(); let mut values = buffer.borrow::(); values.extend([1u64, 2u64, 3u64]); std::mem::forget(values); let values = buffer.borrow::(); assert!(values.is_empty()); } #[test] fn test_versatile_buffer_16_bytes() { let mut buffer: VersatileBuffer<(u64, u64)> = VersatileBuffer::new(); { let mut values = buffer.borrow::<(u64, u32, u32)>(); values.push((1, 2, 3)); assert_eq!(&values[..], &[(1, 2, 3)]); } let values = buffer.borrow::<[u64; 2]>(); assert!(values.is_empty()); } struct DropCounter<'a>(&'a Cell); impl Drop for DropCounter<'_> { fn drop(&mut self) { self.0.set(self.0.get() + 1); } } #[test] fn test_versatile_buffer_drops_values() { let drop_count = Cell::new(0u32); let mut buffer: VersatileBuffer = VersatileBuffer::new(); { let mut values = buffer.borrow::(); for _ in 0..5 { values.push(DropCounter(&drop_count)); } assert_eq!(drop_count.get(), 0); } assert_eq!(drop_count.get(), 5); } }