/// scoped_buffer.rs /// /// Solves the common pattern where we are performing some kind of recursive /// pattern while using a temporary buffer. At the start, or during the /// procedure, we push stuff into the buffer. At the end we take out what we /// have put in. /// /// It is unsafe because we're using pointers to circumvent borrowing rules in /// the name of code cleanliness. The correctness of use is checked in debug /// mode at runtime. use std::iter::FromIterator; macro_rules! hide { ($v:block) => { #[cfg(debug_assertions)] $v }; ($v:expr) => { #[cfg(debug_assertions)] $v }; } pub(crate) struct ScopedBuffer { pub inner: Vec, } impl ScopedBuffer { pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: Vec::with_capacity(capacity), } } pub(crate) fn start_section(&mut self) -> ScopedSection { let start_size = self.inner.len() as u32; ScopedSection { inner: &mut self.inner, start_size, #[cfg(debug_assertions)] cur_size: start_size, } } } impl ScopedBuffer { pub(crate) fn start_section_initialized(&mut self, initialize_with: &[T]) -> ScopedSection { let start_size = self.inner.len() as u32; let _data_size = initialize_with.len() as u32; self.inner.extend_from_slice(initialize_with); ScopedSection{ inner: &mut self.inner, start_size, #[cfg(debug_assertions)] cur_size: start_size + _data_size, } } } #[cfg(debug_assertions)] impl Drop for ScopedBuffer { fn drop(&mut self) { // Make sure that everyone cleaned up the buffer neatly debug_assert!(self.inner.is_empty(), "dropped non-empty scoped buffer"); } } /// A section of the buffer. Keeps track of where we started the section. When /// done with the section one must call `into_vec` or `forget` to remove the /// section from the underlying buffer. This will also be done upon dropping the /// ScopedSection in case errors are being handled. pub(crate) struct ScopedSection { inner: *mut Vec, start_size: u32, #[cfg(debug_assertions)] cur_size: u32, } impl ScopedSection { #[inline] pub(crate) fn push(&mut self, value: T) { let vec = unsafe{&mut *self.inner}; hide!(debug_assert_eq!( vec.len(), self.cur_size as usize, "trying to push onto section, but size is larger than expected" )); vec.push(value); hide!(self.cur_size += 1); } #[inline] pub(crate) fn len(&self) -> usize { let vec = unsafe{&mut *self.inner}; hide!(debug_assert_eq!( vec.len(), self.cur_size as usize, "trying to get section length, but size is larger than expected" )); return vec.len() - self.start_size as usize; } #[inline] #[allow(unused_mut)] // used in debug mode pub(crate) fn forget(mut self) { let vec = unsafe{&mut *self.inner}; hide!({ debug_assert_eq!( vec.len(), self.cur_size as usize, "trying to forget section, but size is larger than expected" ); self.cur_size = self.start_size; }); vec.truncate(self.start_size as usize); } #[inline] #[allow(unused_mut)] // used in debug mode pub(crate) fn into_vec(mut self) -> Vec { let vec = unsafe{&mut *self.inner}; hide!({ debug_assert_eq!( vec.len(), self.cur_size as usize, "trying to turn section into vec, but size is larger than expected" ); self.cur_size = self.start_size; }); let section = Vec::from_iter(vec.drain(self.start_size as usize..)); section } } impl ScopedSection { pub(crate) fn iter_copied(&self) -> ScopedIter { return ScopedIter{ inner: self.inner, cur_index: self.start_size, last_index: unsafe{ (*self.inner).len() as u32 }, } } } impl std::ops::Index for ScopedSection { type Output = T; fn index(&self, index: usize) -> &Self::Output { let vec = unsafe{&*self.inner}; return &vec[self.start_size as usize + index] } } impl std::ops::IndexMut for ScopedSection { fn index_mut(&mut self, index: usize) -> &mut Self::Output { let vec = unsafe{&mut *self.inner}; return &mut vec[self.start_size as usize + index] } } #[cfg(debug_assertions)] impl Drop for ScopedSection { fn drop(&mut self) { let vec = unsafe{&mut *self.inner}; hide!(debug_assert_eq!(vec.len(), self.cur_size as usize)); vec.truncate(self.start_size as usize); } } /// Small utility for iterating over a section of the buffer. Same conditions as /// the buffer apply: each time we retrieve an element the buffer must have the /// same size as the moment of creation. pub(crate) struct ScopedIter { inner: *mut Vec, cur_index: u32, last_index: u32, } impl Iterator for ScopedIter { type Item = T; fn next(&mut self) -> Option { hide!(debug_assert_eq!(self.last_index as usize, unsafe { (*self.inner).len() })); if self.cur_index >= self.last_index { return None; } let vec = unsafe{ &*self.inner }; let index = self.cur_index as usize; self.cur_index += 1; return Some(vec[index]); } }