]> git.proxmox.com Git - rustc.git/blob - vendor/gix-pack/src/cache/object.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / gix-pack / src / cache / object.rs
1 //! This module is a bit 'misplaced' if spelled out like '`gix_pack::cache::object::`*' but is best placed here for code re-use and
2 //! general usefulness.
3 use crate::cache;
4
5 #[cfg(feature = "object-cache-dynamic")]
6 mod memory {
7 use std::num::NonZeroUsize;
8
9 use clru::WeightScale;
10
11 use crate::cache;
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(NonZeroUsize::new(memory_cap_in_bytes).expect("non zero"))
46 .with_hasher(gix_hashtable::hash::Builder)
47 .with_scale(CustomScale),
48 ),
49 free_list: Vec::new(),
50 debug: gix_features::cache::Debug::new(format!("MemoryCappedObjectHashmap({memory_cap_in_bytes}B)")),
51 }
52 }
53 }
54
55 impl cache::Object for MemoryCappedHashmap {
56 /// Put the object going by `id` of `kind` with `data` into the cache.
57 fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) {
58 self.debug.put();
59 let res = self.inner.put_with_weight(
60 id,
61 Entry {
62 data: self.free_list.pop().map_or_else(
63 || Vec::from(data),
64 |mut v| {
65 v.clear();
66 v.resize(data.len(), 0);
67 v.copy_from_slice(data);
68 v
69 },
70 ),
71 kind,
72 },
73 );
74 match res {
75 Ok(Some(previous_entry)) => self.free_list.push(previous_entry.data),
76 Ok(None) => {}
77 Err((_key, value)) => self.free_list.push(value.data),
78 }
79 }
80
81 /// Try to retrieve the object named `id` and place its data into `out` if available and return `Some(kind)` if found.
82 fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> {
83 let res = self.inner.get(id).map(|e| {
84 out.resize(e.data.len(), 0);
85 out.copy_from_slice(&e.data);
86 e.kind
87 });
88 if res.is_some() {
89 self.debug.hit()
90 } else {
91 self.debug.miss()
92 }
93 res
94 }
95 }
96 }
97 #[cfg(feature = "object-cache-dynamic")]
98 pub use memory::MemoryCappedHashmap;
99
100 /// A cache implementation that doesn't do any caching.
101 pub struct Never;
102
103 impl cache::Object for Never {
104 /// Noop
105 fn put(&mut self, _id: gix_hash::ObjectId, _kind: gix_object::Kind, _data: &[u8]) {}
106
107 /// Noop
108 fn get(&mut self, _id: &gix_hash::ObjectId, _out: &mut Vec<u8>) -> Option<gix_object::Kind> {
109 None
110 }
111 }
112
113 impl<T: cache::Object + ?Sized> cache::Object for Box<T> {
114 fn put(&mut self, id: gix_hash::ObjectId, kind: gix_object::Kind, data: &[u8]) {
115 use std::ops::DerefMut;
116 self.deref_mut().put(id, kind, data)
117 }
118
119 fn get(&mut self, id: &gix_hash::ObjectId, out: &mut Vec<u8>) -> Option<gix_object::Kind> {
120 use std::ops::DerefMut;
121 self.deref_mut().get(id, out)
122 }
123 }