1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! This crate provides a `recycle` extension method for Vec.
//! It's intended to change the type of the Vec while "recycling"
//! the underlying allocation. This is a trick that is useful especially
//! when storing data with short lifetimes in Vec:
//! ```
//! # use std::error::Error;
//! # use recycle_vec::VecExt;
//! # 
//! # struct Stream;
//! # 
//! # impl Stream {
//! #     fn new() -> Self {
//! #         Stream
//! #     }
//! # 
//! #     fn next(&mut self) -> Option<&[u8]> {
//! #         Some(&b"hoge"[..])
//! #     }
//! # }
//! # 
//! # fn process(input: &[Object<'_>]) -> Result<(), Box<dyn Error>> {
//! #     Ok(())
//! # }
//! # 
//! # struct Object<'a> {
//! #     reference: &'a [u8],
//! # }
//! # 
//! # fn deserialize<'a>(input: &'a [u8], output: &mut Vec<Object<'a>>) -> Result<(), Box<dyn Error>> {
//! #     output.push(Object { reference: input });
//! #     Ok(())
//! # }
//! # 
//! # fn processor() -> Result<(), Box<dyn Error>> {
//! #    let mut stream = Stream::new();
//! #    
//!     let mut objects: Vec<Object<'static>> = Vec::new();
//! 
//!     while let Some(byte_chunk) = stream.next() { // byte_chunk only lives this scope
//!         let mut objects_temp: Vec<Object<'_>> = objects.recycle();
//! 
//!         // Zero-copy parsing; Object has references to chunk
//!         deserialize(byte_chunk, &mut objects_temp)?;
//!         process(&objects_temp)?;
//! 
//!         objects = objects_temp.recycle();
//!     } // byte_chunk lifetime ends
//! # 
//! #    Ok(())
//! # }
//! ```
//! # Notes about safety
//! This crate uses internally `unsafe` to achieve it's functionality.
//! However, it provides a safe interface. To achieve safety, it does
//! the following precautions:
//! 1. It truncates the `Vec` to zero length, dropping all the values.
//! This ensures that no values of arbitrary types are transmuted
//! accidentally.
//! 2. It checks that the sizes and alignments of the source and target
//! types match. This ensures that the underlying block of memory backing
//! `Vec` is compatible layout-wise.
//! 3. It creates a new `Vec` value using `from_raw_parts`, instead of
//! transmuting, an operation whose soundness would be questionable.

#![no_std]

extern crate alloc;
use alloc::vec::Vec;

/// A trait that provides an API for recycling Vec's internal buffers
pub trait VecExt<T> {

	/// Allows re-interpreting the type of a Vec to reuse the allocation.
	/// The vector is emptied and any values contained in it will be dropped.
	/// The target type must have the same size and alignment as the source type.
	/// This API doesn't transmute any values of T to U, because it makes sure
	/// to empty the vector before any unsafe operations.
	/// 
	/// # Panics
	/// Panics if the size or alignment of the source and target types don't match.
	/// **Note about stabilization:** This contract is enforceable at compile-time,
	/// so we'll want to wait until const asserts become stable and modify this
	/// API to cause a compile error instead of panicking before stabilizing it.
	fn recycle<U>(self) -> Vec<U>;
}

impl<T> VecExt<T> for Vec<T> {
	fn recycle<U>(mut self) -> Vec<U> {
		self.truncate(0);
		// TODO make these const asserts once it becomes possible
		assert!(core::mem::size_of::<T>() == core::mem::size_of::<U>());
		assert!(core::mem::align_of::<T>() == core::mem::align_of::<U>());
		let cap = self.capacity();
		let ptr = self.as_mut_ptr() as *mut U;
		core::mem::forget(self);
		unsafe { Vec::from_raw_parts(ptr, 0, cap) }
	}
}