use std::sync::Arc;
const MAX_INLINE: usize = 38;
#[derive(Clone)]
pub(crate) enum Storage {
Inline(u8, [u8; MAX_INLINE]),
Heap(Arc<[u8]>),
}
impl Storage {
pub fn bytes(&self) -> &[u8] {
match self {
Storage::Inline(len, bytes) => &bytes[..(*len as usize)],
Storage::Heap(data) => &data,
}
}
pub fn from_slice(slice: &[u8]) -> Self {
let len = slice.len();
if len <= MAX_INLINE {
let mut data: [u8; MAX_INLINE] = [0; MAX_INLINE];
data[..len].copy_from_slice(slice);
Storage::Inline(len as u8, data)
} else {
Storage::Heap(slice.into())
}
}
pub fn from_slices(slices: &[&[u8]]) -> Self {
let n = slices.iter().fold(0usize, |a, s| a.saturating_add(s.len()));
if n <= MAX_INLINE {
let s = slices
.iter()
.fold(([0; MAX_INLINE], 0), |(mut array, i), s| {
array[i..i + s.len()].copy_from_slice(s);
(array, i + s.len())
});
Storage::Inline(n as u8, s.0)
} else {
let mut v = Vec::with_capacity(n);
for s in slices {
v.extend_from_slice(s)
}
Storage::Heap(v.into())
}
}
}
#[cfg(test)]
mod tests {
use super::{Storage, MAX_INLINE};
use quickcheck::quickcheck;
#[test]
fn struct_size() {
assert_eq!(std::mem::size_of::<Storage>(), 40);
}
#[test]
fn roundtrip() {
for i in 0..((MAX_INLINE + 10) as u8) {
let data = (0..i).collect::<Vec<u8>>();
let storage = Storage::from_slice(&data);
assert_eq!(data, storage.bytes());
}
}
fn check_invariants(storage: Storage) -> bool {
match storage {
Storage::Inline(len, _) => len as usize <= MAX_INLINE,
Storage::Heap(arc) => arc.len() > MAX_INLINE,
}
}
quickcheck! {
fn roundtrip_check(data: Vec<u8>) -> bool {
let storage = Storage::from_slice(&data);
storage.bytes() == data.as_slice() && check_invariants(storage)
}
fn from_slices_roundtrip_check(data: Vec<Vec<u8>>) -> bool {
let mut slices = Vec::new();
let mut expected = Vec::new();
for v in data.iter() {
slices.push(v.as_slice());
expected.extend_from_slice(&v);
}
let storage = Storage::from_slices(&slices);
storage.bytes() == expected.as_slice() && check_invariants(storage)
}
}
}