1use super::DecodeEntry;
2
3#[cfg(feature = "pack-cache-lru-dynamic")]
4mod memory {
5 use std::num::NonZeroUsize;
6
7 use clru::WeightScale;
8
9 use super::DecodeEntry;
10
11 struct Entry {
12 data: Vec<u8>,
13 kind: git_object::Kind,
14 compressed_size: usize,
15 }
16
17 type Key = (u32, u64);
18 struct CustomScale;
19
20 impl WeightScale<Key, Entry> for CustomScale {
21 fn weight(&self, _key: &Key, value: &Entry) -> usize {
22 value.data.len()
23 }
24 }
25
26 pub struct MemoryCappedHashmap {
28 inner: clru::CLruCache<Key, Entry, std::collections::hash_map::RandomState, CustomScale>,
29 free_list: Vec<Vec<u8>>,
30 debug: git_features::cache::Debug,
31 }
32
33 impl MemoryCappedHashmap {
34 pub fn new(memory_cap_in_bytes: usize) -> MemoryCappedHashmap {
37 MemoryCappedHashmap {
38 inner: clru::CLruCache::with_config(
39 clru::CLruCacheConfig::new(NonZeroUsize::new(memory_cap_in_bytes).expect("non zero"))
40 .with_scale(CustomScale),
41 ),
42 free_list: Vec::new(),
43 debug: git_features::cache::Debug::new(format!("MemoryCappedHashmap({memory_cap_in_bytes}B)")),
44 }
45 }
46 }
47
48 impl DecodeEntry for MemoryCappedHashmap {
49 fn put(&mut self, pack_id: u32, offset: u64, data: &[u8], kind: git_object::Kind, compressed_size: usize) {
50 self.debug.put();
51 if let Ok(Some(previous_entry)) = self.inner.put_with_weight(
52 (pack_id, offset),
53 Entry {
54 data: self
55 .free_list
56 .pop()
57 .map(|mut v| {
58 v.clear();
59 v.resize(data.len(), 0);
60 v.copy_from_slice(data);
61 v
62 })
63 .unwrap_or_else(|| Vec::from(data)),
64 kind,
65 compressed_size,
66 },
67 ) {
68 self.free_list.push(previous_entry.data)
69 }
70 }
71
72 fn get(&mut self, pack_id: u32, offset: u64, out: &mut Vec<u8>) -> Option<(git_object::Kind, usize)> {
73 let res = self.inner.get(&(pack_id, offset)).map(|e| {
74 out.resize(e.data.len(), 0);
75 out.copy_from_slice(&e.data);
76 (e.kind, e.compressed_size)
77 });
78 if res.is_some() {
79 self.debug.hit()
80 } else {
81 self.debug.miss()
82 }
83 res
84 }
85 }
86}
87
88#[cfg(feature = "pack-cache-lru-dynamic")]
89pub use memory::MemoryCappedHashmap;
90
91#[cfg(feature = "pack-cache-lru-static")]
92mod _static {
93 use super::DecodeEntry;
94 struct Entry {
95 pack_id: u32,
96 offset: u64,
97 data: Vec<u8>,
98 kind: git_object::Kind,
99 compressed_size: usize,
100 }
101
102 pub struct StaticLinkedList<const SIZE: usize> {
106 inner: uluru::LRUCache<Entry, SIZE>,
107 free_list: Vec<Vec<u8>>,
108 debug: git_features::cache::Debug,
109 }
110
111 impl<const SIZE: usize> Default for StaticLinkedList<SIZE> {
112 fn default() -> Self {
113 StaticLinkedList {
114 inner: Default::default(),
115 free_list: Vec::new(),
116 debug: git_features::cache::Debug::new(format!("StaticLinkedList<{SIZE}>")),
117 }
118 }
119 }
120
121 impl<const SIZE: usize> DecodeEntry for StaticLinkedList<SIZE> {
122 fn put(&mut self, pack_id: u32, offset: u64, data: &[u8], kind: git_object::Kind, compressed_size: usize) {
123 self.debug.put();
124 if let Some(previous) = self.inner.insert(Entry {
125 offset,
126 pack_id,
127 data: self
128 .free_list
129 .pop()
130 .map(|mut v| {
131 v.clear();
132 v.resize(data.len(), 0);
133 v.copy_from_slice(data);
134 v
135 })
136 .unwrap_or_else(|| Vec::from(data)),
137 kind,
138 compressed_size,
139 }) {
140 self.free_list.push(previous.data)
141 }
142 }
143
144 fn get(&mut self, pack_id: u32, offset: u64, out: &mut Vec<u8>) -> Option<(git_object::Kind, usize)> {
145 let res = self.inner.lookup(|e: &mut Entry| {
146 if e.pack_id == pack_id && e.offset == offset {
147 out.resize(e.data.len(), 0);
148 out.copy_from_slice(&e.data);
149 Some((e.kind, e.compressed_size))
150 } else {
151 None
152 }
153 });
154 if res.is_some() {
155 self.debug.hit()
156 } else {
157 self.debug.miss()
158 }
159 res
160 }
161 }
162}
163
164#[cfg(feature = "pack-cache-lru-static")]
165pub use _static::StaticLinkedList;