Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117//! This module is a bit 'misplaced' if spelled out like '`gix_pack::cache::object::`*' but is best placed here for code reuse and//! general usefulness.use crate::cache;
#[cfg(feature = "object-cache-dynamic")]mod memory { use std::num::NonZeroUsize;
use clru::WeightScale;
use crate::{cache, cache::set_vec_to_slice};
struct Entry { data: Vec<u8>, kind: gix_object::Kind, }
type Key = gix_hash::ObjectId;
struct CustomScale;
impl WeightScale<Key, Entry> for CustomScale { fn weight(&self, key: &Key, value: &Entry) -> usize { value.data.len() + std::mem::size_of::<Entry>() + key.as_bytes().len() } }
/// An LRU cache with hash map backing and an eviction rule based on the memory usage for object data in bytes. pub struct MemoryCappedHashmap { inner: clru::CLruCache<Key, Entry, gix_hashtable::hash::Builder, CustomScale>, free_list: Vec<Vec<u8>>, debug: gix_features::cache::Debug, }
impl MemoryCappedHashmap { /// The amount of bytes we can hold in total, or the value we saw in `new(…)`. pub fn capacity(&self) -> usize { self.inner.capacity() } /// Return a new instance which evicts least recently used items if it uses more than `memory_cap_in_bytes` /// object data. pub fn new(memory_cap_in_bytes: usize) -> MemoryCappedHashmap { MemoryCappedHashmap { inner: clru::CLruCache::with_config( clru::CLruCacheConfig::new( NonZeroUsize::new(memory_cap_in_bytes).expect("non zero"), ) .with_hasher(gix_hashtable::hash::Builder) .with_scale(CustomScale), ), free_list: Vec::new(), debug: gix_features::cache::Debug::new(format!( "MemoryCappedObjectHashmap({memory_cap_in_bytes}B)" )), } } }
impl cache::Object for MemoryCappedHashmap { /// Put the object going by `id` of `kind` with `data` into the cache. fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) { self.debug.put(); let Some(data) = set_vec_to_slice(self.free_list.pop().unwrap_or_default(), data) else { return; }; let res = self.inner.put_with_weight(id, Entry { data, kind }); match res { Ok(Some(previous_entry)) => self.free_list.push(previous_entry.data), Ok(None) => {} Err((_key, value)) => self.free_list.push(value.data), } }
/// Try to retrieve the object named `id` and place its data into `out` if available and return `Some(kind)` if found. fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> { let res = self.inner.get(id).and_then(|e| { set_vec_to_slice(out, &e.data)?; Some(e.kind) }); if res.is_some() { self.debug.hit(); } else { self.debug.miss(); } res } }}#[cfg(feature = "object-cache-dynamic")]pub use memory::MemoryCappedHashmap;
/// A cache implementation that doesn't do any caching.pub struct Never;
impl cache::Object for Never { /// Noop fn put(&mut self, _id: gix_hash::ObjectId, _kind: gix_object::Kind, _data: &[u8]) {}
/// Noop fn get(&mut self, _id: &gix_hash::ObjectId, _out: &mut Vec<u8>) -> Option<gix_object::Kind> { None }}
impl<T: cache::Object + ?Sized> cache::Object for Box<T> { fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) { use std::ops::DerefMut; self.deref_mut().put(id, kind, data); }
fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> { use std::ops::DerefMut; self.deref_mut().get(id, out) }}