fast-cache 0.1.0

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

impl HashObject {
    pub(super) fn single(field: Bytes, value: Bytes) -> Self {
        let mut entries = SmallHashEntries::new();
        entries.push((field, value));
        Self::Small(entries)
    }

    pub(super) fn map_with_capacity(capacity: usize) -> Self {
        let mut map = FastHashMap::default();
        map.reserve(capacity);
        Self::Map(map)
    }

    pub(super) fn insert_slice(&mut self, field: &[u8], value: &[u8]) -> bool {
        match self {
            Self::Small(entries) => {
                if let Some((existing_field, existing_value)) = entries.first_mut()
                    && existing_field.as_slice() == field
                {
                    replace_bytes(existing_value, value);
                    return false;
                }
                if let Some((_, existing_value)) = entries
                    .iter_mut()
                    .find(|(existing, _)| existing.as_slice() == field)
                {
                    replace_bytes(existing_value, value);
                    return false;
                }
                if entries.len() < SMALL_HASH_INLINE {
                    entries.push((field.to_vec(), value.to_vec()));
                    return true;
                }

                let capacity = entries.len() + 1;
                let old_entries = std::mem::take(entries);
                *self = Self::map_with_capacity(capacity);
                let Self::Map(map) = self else {
                    unreachable!("hash promotion did not create map state");
                };
                for (old_field, old_value) in old_entries {
                    map.insert(old_field, old_value);
                }
                map.insert(field.to_vec(), value.to_vec());
                true
            }
            Self::Map(map) => map.insert(field.to_vec(), value.to_vec()).is_none(),
        }
    }

    pub(super) fn get(&self, field: &[u8]) -> Option<&Bytes> {
        match self {
            Self::Small(entries) => entries
                .iter()
                .find_map(|(existing, value)| (existing.as_slice() == field).then_some(value)),
            Self::Map(map) => map.get(field),
        }
    }

    pub(super) fn contains_key(&self, field: &[u8]) -> bool {
        self.get(field).is_some()
    }

    pub(super) fn fields(&self) -> Vec<Bytes> {
        let mut fields: Vec<Bytes> = match self {
            Self::Small(entries) => entries.iter().map(|(field, _)| field.clone()).collect(),
            Self::Map(map) => map.keys().cloned().collect(),
        };
        fields.sort();
        fields
    }

    pub(super) fn values(&self) -> Vec<Bytes> {
        match self {
            Self::Small(entries) => entries.iter().map(|(_, value)| value.clone()).collect(),
            Self::Map(map) => map.values().cloned().collect(),
        }
    }

    pub(super) fn entries(&self) -> Vec<(Bytes, Bytes)> {
        let mut entries = match self {
            Self::Small(entries) => entries.clone().into_vec(),
            Self::Map(map) => map
                .iter()
                .map(|(field, value)| (field.clone(), value.clone()))
                .collect(),
        };
        entries.sort_by(|(left, _), (right, _)| left.cmp(right));
        entries
    }

    pub(super) fn remove(&mut self, field: &[u8]) -> Option<Bytes> {
        match self {
            Self::Small(entries) => {
                let index = entries
                    .iter()
                    .position(|(existing, _)| existing.as_slice() == field)?;
                Some(entries.remove(index).1)
            }
            Self::Map(map) => map.remove(field),
        }
    }

    pub(super) fn len(&self) -> usize {
        match self {
            Self::Small(entries) => entries.len(),
            Self::Map(map) => map.len(),
        }
    }

    pub(super) fn is_empty(&self) -> bool {
        match self {
            Self::Small(entries) => entries.is_empty(),
            Self::Map(map) => map.is_empty(),
        }
    }
}