diff --git a/src/lib.rs b/src/lib.rs index 6cde348..0223934 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,6 +186,18 @@ fn buffer_len(cap: usize) -> usize { (cap + bits_per_storage() - 1) / bits_per_storage() } +/// A typed representation of a `SmallBitVec`'s internal storage. +/// +/// The layout of the data inside both enum variants is a private implementation detail. +pub enum InternalStorage { + /// The internal representation of a `SmallBitVec` that has not spilled to a + /// heap allocation. + Inline(usize), + + /// The contents of the heap allocation of a spilled `SmallBitVec`. + Spilled(Box<[usize]>), +} + impl SmallBitVec { /// Create an empty vector. #[inline] @@ -615,6 +627,66 @@ impl SmallBitVec { } } + /// Converts this `SmallBitVec` into its internal representation. + /// + /// The layout of the data inside both enum variants is a private implementation detail. + #[inline] + pub fn into_storage(self) -> InternalStorage { + if self.is_heap() { + let alloc_len = header_len() + self.header().buffer_len; + let ptr = self.header_raw() as *mut Storage; + let slice = unsafe { Box::from_raw(slice::from_raw_parts_mut(ptr, alloc_len)) }; + forget(self); + InternalStorage::Spilled(slice) + } else { + InternalStorage::Inline(self.data) + } + } + + /// Creates a `SmallBitVec` directly from the internal storage of another + /// `SmallBitVec`. + /// + /// # Safety + /// + /// This is highly unsafe. `storage` needs to have been previously generated + /// via `SmallBitVec::into_storage` (at least, it's highly likely to be + /// incorrect if it wasn't.) Violating this may cause problems like corrupting the + /// allocator's internal data structures. + /// + /// # Examples + /// + /// ``` + /// # use smallbitvec::{InternalStorage, SmallBitVec}; + /// + /// fn main() { + /// let v = SmallBitVec::from_elem(200, false); + /// + /// // Get the internal representation of the SmallBitVec. + /// // unless we transfer its ownership somewhere else. + /// let storage = v.into_storage(); + /// + /// /// Make a copy of the SmallBitVec's data. + /// let cloned_storage = match storage { + /// InternalStorage::Spilled(vs) => InternalStorage::Spilled(vs.clone()), + /// inline => inline, + /// }; + /// + /// /// Create a new SmallBitVec from the coped storage. + /// let v = unsafe { SmallBitVec::from_storage(cloned_storage) }; + /// } + /// ``` + pub unsafe fn from_storage(storage: InternalStorage) -> SmallBitVec { + match storage { + InternalStorage::Inline(data) => SmallBitVec { data }, + InternalStorage::Spilled(vs) => { + let ptr = Box::into_raw(vs); + SmallBitVec { + data: (ptr as *mut usize as usize) | HEAP_FLAG, + } + } + } + } + /// If the rightmost bit is set, then we treat it as inline storage. #[inline] fn is_inline(&self) -> bool {