fast-cache 0.1.0

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

#[derive(Debug)]
pub(super) struct FastPointEntry {
    hash: u64,
    key_tag: u64,
    key_len: usize,
    key_inline: [u8; FastPointEntry::INLINE_KEY_CAP],
    key_heap: Option<Box<[u8]>>,
    value: SharedBytes,
}

impl FastPointEntry {
    const INLINE_KEY_CAP: usize = 16;

    #[inline(always)]
    fn new(hash: u64, key_tag: u64, key: &[u8], value: SharedBytes) -> Self {
        let mut key_inline = [0u8; Self::INLINE_KEY_CAP];
        let key_heap = if key.len() <= Self::INLINE_KEY_CAP {
            key_inline[..key.len()].copy_from_slice(key);
            None
        } else {
            Some(key.to_vec().into_boxed_slice())
        };
        Self {
            hash,
            key_tag,
            key_len: key.len(),
            key_inline,
            key_heap,
            value,
        }
    }

    #[inline(always)]
    fn key_matches(&self, key: &[u8]) -> bool {
        if self.key_len != key.len() {
            return false;
        }
        if self.key_len <= Self::INLINE_KEY_CAP {
            bytes_equal_hot(&self.key_inline[..self.key_len], key)
        } else {
            bytes_equal_hot(
                self.key_heap
                    .as_deref()
                    .expect("large fast point key has heap storage"),
                key,
            )
        }
    }

    pub(super) fn into_flat_entry(self) -> FlatEntry {
        let key = match self.key_heap {
            Some(key) => key,
            None => self.key_inline[..self.key_len].to_vec().into_boxed_slice(),
        };
        FlatEntry {
            hash: self.hash,
            key_tag: self.key_tag,
            key_len: self.key_len,
            key,
            value: self.value,
            expire_at_ms: None,
            access: EntryAccessMeta {
                last_touch: 0,
                frequency: 1,
            },
        }
    }

    fn to_stored_entry(&self) -> StoredEntry {
        let key = if self.key_len <= Self::INLINE_KEY_CAP {
            self.key_inline[..self.key_len].to_vec()
        } else {
            self.key_heap
                .as_deref()
                .expect("large fast point key has heap storage")
                .to_vec()
        };
        StoredEntry {
            key,
            value: self.value.as_ref().to_vec(),
            expire_at_ms: None,
        }
    }
}

#[derive(Debug)]
pub(super) enum FastPointMutation {
    Inserted {
        key_len: usize,
        value_len: usize,
    },
    Replaced {
        old_value: Option<SharedBytes>,
        old_value_len: usize,
        new_value_len: usize,
    },
}

#[derive(Debug, Clone, Copy)]
struct FastPointSlot {
    hash: u64,
    key_tag: u64,
    key_len: u32,
    entry_index: u32,
}

impl FastPointSlot {
    const EMPTY_INDEX: u32 = u32::MAX;

    #[inline(always)]
    fn empty() -> Self {
        Self {
            hash: 0,
            key_tag: 0,
            key_len: 0,
            entry_index: Self::EMPTY_INDEX,
        }
    }

    #[inline(always)]
    fn is_empty(self) -> bool {
        self.entry_index == Self::EMPTY_INDEX
    }
}

#[derive(Debug)]
pub(super) struct FastPointMap {
    active: bool,
    slots: Vec<FastPointSlot>,
    entries: Vec<FastPointEntry>,
}

impl Default for FastPointMap {
    fn default() -> Self {
        Self {
            active: true,
            slots: Vec::new(),
            entries: Vec::new(),
        }
    }
}

impl FastPointMap {
    const INITIAL_SLOTS: usize = 1024;
    const MAX_LOAD_NUMERATOR: usize = 7;
    const MAX_LOAD_DENOMINATOR: usize = 10;

    #[inline(always)]
    pub(super) fn is_active(&self) -> bool {
        self.active
    }

    #[inline(always)]
    pub(super) fn len(&self) -> usize {
        if self.active { self.entries.len() } else { 0 }
    }

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

    #[inline(always)]
    pub(super) fn take_entries_and_disable(&mut self) -> Vec<FastPointEntry> {
        self.active = false;
        self.slots.clear();
        mem::take(&mut self.entries)
    }

    #[inline(always)]
    pub(super) fn get(&self, hash: u64, key: &[u8]) -> Option<&SharedBytes> {
        if !self.active || self.slots.is_empty() {
            return None;
        }
        let key_len = key.len();
        if key_len > u32::MAX as usize {
            return None;
        }

        let mask = self.slots.len() - 1;
        let mut index = fast_point_bucket(hash, mask);
        loop {
            let slot = self.slots[index];
            if slot.is_empty() {
                return None;
            }
            if slot.hash == hash && slot.key_len as usize == key_len {
                #[cfg(not(feature = "unsafe"))]
                let entry = self.entries.get(slot.entry_index as usize)?;
                #[cfg(feature = "unsafe")]
                let entry = unsafe { self.entries.get_unchecked(slot.entry_index as usize) };
                if entry.hash == hash && entry.key_matches(key) {
                    return Some(&entry.value);
                }
            }
            index = (index + 1) & mask;
        }
    }

    #[inline(always)]
    pub(super) fn get_tagged(
        &self,
        hash: u64,
        key_tag: u64,
        key_len: usize,
    ) -> Option<&SharedBytes> {
        if !self.active || self.slots.is_empty() || key_len > u32::MAX as usize {
            return None;
        }

        let mask = self.slots.len() - 1;
        let mut index = fast_point_bucket(hash, mask);
        loop {
            let slot = self.slots[index];
            if slot.is_empty() {
                return None;
            }
            if slot.hash == hash && slot.key_tag == key_tag && slot.key_len as usize == key_len {
                #[cfg(not(feature = "unsafe"))]
                let entry = self.entries.get(slot.entry_index as usize)?;
                #[cfg(feature = "unsafe")]
                let entry = unsafe { self.entries.get_unchecked(slot.entry_index as usize) };
                if entry.hash == hash && entry.key_tag == key_tag && entry.key_len == key_len {
                    return Some(&entry.value);
                }
            }
            index = (index + 1) & mask;
        }
    }

    #[inline(always)]
    pub(super) unsafe fn upsert_slice(
        &mut self,
        hash: u64,
        key_tag: u64,
        key: &[u8],
        value: &[u8],
        #[cfg_attr(not(feature = "unsafe"), allow(unused_variables))] allow_in_place_replace: bool,
    ) -> Option<FastPointMutation> {
        if !self.active {
            return None;
        }
        if key.len() > u32::MAX as usize || self.entries.len() >= u32::MAX as usize {
            return None;
        }
        self.ensure_insert_capacity();
        if !self.active {
            return None;
        }

        let key_len = key.len();
        let mask = self.slots.len() - 1;
        let mut index = fast_point_bucket(hash, mask);
        loop {
            let slot = self.slots[index];
            if slot.is_empty() {
                let entry_index = self.entries.len();
                let stored_value = shared_bytes_from_slice(value);
                let value_len = stored_value.len();
                self.entries
                    .push(FastPointEntry::new(hash, key_tag, key, stored_value));
                self.slots[index] = FastPointSlot {
                    hash,
                    key_tag,
                    key_len: key_len as u32,
                    entry_index: entry_index as u32,
                };
                return Some(FastPointMutation::Inserted { key_len, value_len });
            }

            if slot.hash == hash && slot.key_len as usize == key_len {
                #[cfg(not(feature = "unsafe"))]
                let Some(entry) = self.entries.get_mut(slot.entry_index as usize) else {
                    self.active = false;
                    return None;
                };
                #[cfg(feature = "unsafe")]
                let entry = unsafe { self.entries.get_unchecked_mut(slot.entry_index as usize) };
                if entry.hash == hash && entry.key_matches(key) {
                    entry.key_tag = key_tag;
                    self.slots[index].key_tag = key_tag;
                    let old_value_len = entry.value.len();
                    #[cfg(feature = "unsafe")]
                    if old_value_len == value.len() && allow_in_place_replace {
                        unsafe {
                            copy_hot_value_bytes(
                                entry.value.as_ptr().cast_mut(),
                                value.as_ptr(),
                                value.len(),
                            );
                        }
                        return Some(FastPointMutation::Replaced {
                            old_value: None,
                            old_value_len,
                            new_value_len: old_value_len,
                        });
                    }
                    let new_value = shared_bytes_from_slice(value);
                    let new_value_len = new_value.len();
                    let old_value = mem::replace(&mut entry.value, new_value);
                    return Some(FastPointMutation::Replaced {
                        old_value: Some(old_value),
                        old_value_len,
                        new_value_len,
                    });
                }
            }

            index = (index + 1) & mask;
        }
    }

    fn ensure_insert_capacity(&mut self) {
        if self.slots.is_empty() {
            self.resize(Self::INITIAL_SLOTS);
            return;
        }

        let next_len = self.entries.len().saturating_add(1);
        if next_len.saturating_mul(Self::MAX_LOAD_DENOMINATOR)
            >= self.slots.len().saturating_mul(Self::MAX_LOAD_NUMERATOR)
        {
            self.resize(self.slots.len().saturating_mul(2));
        }
    }

    fn resize(&mut self, new_len: usize) {
        let new_len = new_len.next_power_of_two().max(Self::INITIAL_SLOTS);
        let mut new_slots = vec![FastPointSlot::empty(); new_len];
        let mask = new_len - 1;
        for (entry_index, entry) in self.entries.iter().enumerate() {
            let mut index = fast_point_bucket(entry.hash, mask);
            loop {
                if new_slots[index].is_empty() {
                    new_slots[index] = FastPointSlot {
                        hash: entry.hash,
                        key_tag: entry.key_tag,
                        key_len: entry.key_len as u32,
                        entry_index: entry_index as u32,
                    };
                    break;
                }
                index = (index + 1) & mask;
            }
        }
        self.slots = new_slots;
    }

    pub(super) fn snapshot_entries(&self) -> Vec<StoredEntry> {
        self.entries
            .iter()
            .map(FastPointEntry::to_stored_entry)
            .collect()
    }
}

#[inline(always)]
fn fast_point_bucket(hash: u64, mask: usize) -> usize {
    let mixed = hash ^ (hash >> 32);
    mixed as usize & mask
}