anathema-strings 0.2.2

Custom strings for Anathema
Documentation
use anathema_store::slab::Slab;

pub use self::transaction::Transaction;
use crate::StrIndex;
use crate::region::Region;

mod transaction;

const END: usize = 5;

fn bytes_to_str<'a>(bytes: &mut &'a [u8], slices: &'a Slab<u16, &str>) -> &'a str {
    match &bytes[..2] {
        [0xFF, 0xFF] => {
            let len = u16::from_ne_bytes([bytes[2], bytes[3]]) as usize;
            let s = unsafe { std::str::from_utf8_unchecked(&bytes[4..len + 4]) };
            *bytes = &bytes[4 + len..];
            s
        }
        index => {
            *bytes = &bytes[2..];
            let index = u16::from_ne_bytes([index[0], index[1]]);
            slices.get(index).unwrap()
        }
    }
}

fn remove_slice(bytes: &mut &[u8], slices: &mut Slab<u16, &str>) {
    while !bytes.is_empty() {
        match &bytes[..2] {
            [0xFF, 0xFF] => {
                let len = u16::from_ne_bytes([bytes[2], bytes[3]]) as usize;
                *bytes = &bytes[4 + len..];
            }
            index => {
                *bytes = &bytes[2..];
                let index = u16::from_ne_bytes([index[0], index[1]]);
                slices.remove(index);
            }
        }
    }
}

#[derive(Debug)]
pub(super) struct Storage<'slice> {
    inner: Vec<u8>,
    slices: Slab<u16, &'slice str>,
    free: Slab<u8, Vec<Region>>,
    buffer: Vec<u8>,
}

impl<'slice> Storage<'slice> {
    pub(super) fn empty() -> Self {
        let mut free = Slab::empty();
        for _ in 0..END {
            free.insert(vec![]);
        }

        Self {
            inner: vec![],
            slices: Slab::empty(),
            free,
            buffer: vec![],
        }
    }

    pub fn begin_insert(&mut self) -> Transaction<'_, 'slice> {
        Transaction::new(self)
    }

    pub fn get(&self, hoppstr: StrIndex) -> impl Iterator<Item = &str> + Clone {
        let mut bytes = &self.inner[hoppstr.index as usize..][..hoppstr.len as usize];

        std::iter::from_fn(move || {
            if !bytes.is_empty() {
                let s = bytes_to_str(&mut bytes, &self.slices);
                Some(s)
            } else {
                None
            }
        })
    }

    pub fn remove(&mut self, hoppstr: StrIndex) {
        let mut bytes = &self.inner[hoppstr.index as usize..][..hoppstr.len as usize];
        remove_slice(&mut bytes, &mut self.slices);
        let region = hoppstr.to_region();
        if let Some(regions) = self.free.get_mut(region.bucket_index()) {
            regions.push(region);
        }
    }
}