relay-knowledge 1.1.14

Graph-database-based knowledge graph project.
Documentation
use std::collections::{HashMap, VecDeque};
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct ContentHashCache {
    capacity: usize,
    entries: HashMap<PathBuf, u64>,
    insertion_order: VecDeque<PathBuf>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ContentHashObservation {
    pub changed: bool,
    pub hash: u64,
}

impl ContentHashCache {
    pub fn new(capacity: usize) -> Self {
        Self {
            capacity,
            entries: HashMap::new(),
            insertion_order: VecDeque::new(),
        }
    }

    pub fn check_and_update(&mut self, path: PathBuf, content: &[u8]) -> ContentHashObservation {
        self.check_hash_and_update(path, content_hash64(content))
    }

    pub fn check_hash_and_update(
        &mut self,
        path: PathBuf,
        new_hash: u64,
    ) -> ContentHashObservation {
        let observation = self.observe_hash(&path, new_hash);
        if observation.changed {
            self.record_hash(path, new_hash);
        }
        observation
    }

    pub fn observe_hash(&self, path: &PathBuf, new_hash: u64) -> ContentHashObservation {
        if self.capacity == 0 {
            return ContentHashObservation {
                changed: true,
                hash: new_hash,
            };
        }
        let changed = match self.entries.get(path) {
            Some(&existing) => existing != new_hash,
            None => true,
        };
        ContentHashObservation {
            changed,
            hash: new_hash,
        }
    }

    pub fn record_hash(&mut self, path: PathBuf, new_hash: u64) {
        if self.capacity == 0 {
            return;
        }
        if self.entries.len() >= self.capacity && !self.entries.contains_key(&path) {
            self.evict_oldest();
        }
        if !self.entries.contains_key(&path) {
            self.insertion_order.push_back(path.clone());
        }
        self.entries.insert(path, new_hash);
    }

    pub fn remove(&mut self, path: &PathBuf) {
        self.entries.remove(path);
        self.insertion_order.retain(|entry| entry != path);
    }

    pub fn len(&self) -> usize {
        self.entries.len()
    }

    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    pub fn capacity(&self) -> usize {
        self.capacity
    }

    pub fn clear(&mut self) {
        self.entries.clear();
        self.insertion_order.clear();
    }

    pub(crate) fn snapshots(&self) -> Vec<(PathBuf, u64)> {
        self.insertion_order
            .iter()
            .filter_map(|path| self.entries.get(path).map(|hash| (path.clone(), *hash)))
            .collect()
    }

    fn evict_oldest(&mut self) {
        while let Some(oldest_key) = self.insertion_order.pop_front() {
            if self.entries.remove(&oldest_key).is_some() {
                break;
            }
        }
    }
}

pub(super) fn content_hash64(bytes: &[u8]) -> u64 {
    const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
    const FNV_PRIME: u64 = 0x100000001b3;

    let mut hash = FNV_OFFSET_BASIS;
    for byte in bytes {
        hash ^= u64::from(*byte);
        hash = hash.wrapping_mul(FNV_PRIME);
    }
    hash
}

#[cfg(test)]
#[path = "mod_tests.rs"]
mod tests;