fast-cache 0.1.0

Embedded-first thread-per-core in-memory cache with optional Redis-compatible server
Documentation
use super::*;

impl ListChunk {
    pub(super) fn new() -> Self {
        Self {
            entries: std::array::from_fn(|_| None),
            head: 0,
            len: 0,
        }
    }

    #[inline(always)]
    pub(super) fn len(&self) -> usize {
        self.len
    }

    #[inline(always)]
    pub(super) fn is_empty(&self) -> bool {
        self.len == 0
    }

    #[inline(always)]
    pub(super) fn is_full(&self) -> bool {
        self.len == LIST_CHUNK_CAPACITY
    }

    #[inline(always)]
    pub(super) fn physical_index(&self, logical_index: usize) -> usize {
        (self.head + logical_index) % LIST_CHUNK_CAPACITY
    }

    #[inline(always)]
    pub(super) fn get(&self, index: usize) -> Option<&Bytes> {
        (index < self.len)
            .then(|| self.physical_index(index))
            .and_then(|index| self.entries[index].as_ref())
    }

    #[inline(always)]
    pub(super) fn set(&mut self, index: usize, value: Bytes) {
        let physical_index = self.physical_index(index);
        self.entries[physical_index] = Some(value);
    }

    #[inline(always)]
    pub(super) fn push_front(&mut self, value: Bytes) -> Result<(), Bytes> {
        if self.is_full() {
            return Err(value);
        }
        self.head = if self.len == 0 {
            0
        } else {
            (self.head + LIST_CHUNK_CAPACITY - 1) % LIST_CHUNK_CAPACITY
        };
        self.entries[self.head] = Some(value);
        self.len += 1;
        Ok(())
    }

    #[inline(always)]
    pub(super) fn push_back(&mut self, value: Bytes) -> Result<(), Bytes> {
        if self.is_full() {
            return Err(value);
        }
        let index = self.physical_index(self.len);
        self.entries[index] = Some(value);
        self.len += 1;
        Ok(())
    }

    #[inline(always)]
    pub(super) fn pop_front(&mut self) -> Option<Bytes> {
        if self.len == 0 {
            return None;
        }
        let value = self.entries[self.head].take();
        self.head = (self.head + 1) % LIST_CHUNK_CAPACITY;
        self.len -= 1;
        if self.len == 0 {
            self.head = 0;
        }
        value
    }

    #[inline(always)]
    pub(super) fn pop_back(&mut self) -> Option<Bytes> {
        if self.len == 0 {
            return None;
        }
        let index = self.physical_index(self.len - 1);
        let value = self.entries[index].take();
        self.len -= 1;
        if self.len == 0 {
            self.head = 0;
        }
        value
    }

    pub(super) fn remove(&mut self, index: usize) -> Option<Bytes> {
        if index >= self.len {
            return None;
        }
        let mut values = self.take_all();
        let value = values.remove(index);
        for value in values {
            self.push_back(value)
                .expect("chunk remains under capacity after remove");
        }
        Some(value)
    }

    pub(super) fn take_all(&mut self) -> Vec<Bytes> {
        let len = self.len;
        let mut values = Vec::with_capacity(len);
        for index in 0..len {
            let physical_index = self.physical_index(index);
            values.push(
                self.entries[physical_index]
                    .take()
                    .expect("list chunk slot missing value"),
            );
        }
        self.head = 0;
        self.len = 0;
        values
    }

    pub(super) fn iter(&self) -> ListChunkIter<'_> {
        ListChunkIter {
            chunk: self,
            index: 0,
        }
    }
}

impl<'a> Iterator for ListChunkIter<'a> {
    type Item = &'a Bytes;

    fn next(&mut self) -> Option<Self::Item> {
        let value = self.chunk.get(self.index)?;
        self.index += 1;
        Some(value)
    }
}