This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / third_party / gix-pack / src / cache / object.rs
4.1 kB 116 lines
1//! This module is a bit 'misplaced' if spelled out like '`gix_pack::cache::object::`*' but is best placed here for code reuse and 2//! general usefulness. 3use crate::cache; 4 5#[cfg(feature = "object-cache-dynamic")] 6mod memory { 7 use std::num::NonZeroUsize; 8 9 use clru::WeightScale; 10 11 use crate::{cache, cache::set_vec_to_slice}; 12 13 struct Entry { 14 data: Vec<u8>, 15 kind: gix_object::Kind, 16 } 17 18 type Key = gix_hash::ObjectId; 19 20 struct CustomScale; 21 22 impl WeightScale<Key, Entry> for CustomScale { 23 fn weight(&self, key: &Key, value: &Entry) -> usize { 24 value.data.len() + std::mem::size_of::<Entry>() + key.as_bytes().len() 25 } 26 } 27 28 /// An LRU cache with hash map backing and an eviction rule based on the memory usage for object data in bytes. 29 pub struct MemoryCappedHashmap { 30 inner: clru::CLruCache<Key, Entry, gix_hashtable::hash::Builder, CustomScale>, 31 free_list: Vec<Vec<u8>>, 32 debug: gix_features::cache::Debug, 33 } 34 35 impl MemoryCappedHashmap { 36 /// The amount of bytes we can hold in total, or the value we saw in `new(…)`. 37 pub fn capacity(&self) -> usize { 38 self.inner.capacity() 39 } 40 /// Return a new instance which evicts least recently used items if it uses more than `memory_cap_in_bytes` 41 /// object data. 42 pub fn new(memory_cap_in_bytes: usize) -> MemoryCappedHashmap { 43 MemoryCappedHashmap { 44 inner: clru::CLruCache::with_config( 45 clru::CLruCacheConfig::new( 46 NonZeroUsize::new(memory_cap_in_bytes).expect("non zero"), 47 ) 48 .with_hasher(gix_hashtable::hash::Builder) 49 .with_scale(CustomScale), 50 ), 51 free_list: Vec::new(), 52 debug: gix_features::cache::Debug::new(format!( 53 "MemoryCappedObjectHashmap({memory_cap_in_bytes}B)" 54 )), 55 } 56 } 57 } 58 59 impl cache::Object for MemoryCappedHashmap { 60 /// Put the object going by `id` of `kind` with `data` into the cache. 61 fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) { 62 self.debug.put(); 63 let Some(data) = set_vec_to_slice(self.free_list.pop().unwrap_or_default(), data) 64 else { 65 return; 66 }; 67 let res = self.inner.put_with_weight(id, Entry { data, kind }); 68 match res { 69 Ok(Some(previous_entry)) => self.free_list.push(previous_entry.data), 70 Ok(None) => {} 71 Err((_key, value)) => self.free_list.push(value.data), 72 } 73 } 74 75 /// Try to retrieve the object named `id` and place its data into `out` if available and return `Some(kind)` if found. 76 fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> { 77 let res = self.inner.get(id).and_then(|e| { 78 set_vec_to_slice(out, &e.data)?; 79 Some(e.kind) 80 }); 81 if res.is_some() { 82 self.debug.hit(); 83 } else { 84 self.debug.miss(); 85 } 86 res 87 } 88 } 89} 90#[cfg(feature = "object-cache-dynamic")] 91pub use memory::MemoryCappedHashmap; 92 93/// A cache implementation that doesn't do any caching. 94pub struct Never; 95 96impl cache::Object for Never { 97 /// Noop 98 fn put(&mut self, _id: gix_hash::ObjectId, _kind: gix_object::Kind, _data: &[u8]) {} 99 100 /// Noop 101 fn get(&mut self, _id: &gix_hash::ObjectId, _out: &mut Vec<u8>) -> Option<gix_object::Kind> { 102 None 103 } 104} 105 106impl<T: cache::Object + ?Sized> cache::Object for Box<T> { 107 fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) { 108 use std::ops::DerefMut; 109 self.deref_mut().put(id, kind, data); 110 } 111 112 fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> { 113 use std::ops::DerefMut; 114 self.deref_mut().get(id, out) 115 } 116}