iroh_blobs/util/
temp_tag.rs1#![allow(dead_code)]
2use std::{
3 collections::HashMap,
4 sync::{Arc, Mutex, Weak},
5};
6
7use serde::{Deserialize, Serialize};
8use tracing::{trace, warn};
9
10use crate::{api::proto::Scope, BlobFormat, Hash, HashAndFormat};
11
12#[derive(Debug, Serialize, Deserialize)]
17#[must_use = "TempTag is a temporary tag that should be used to protect content while the process is running. \
18 If you want to keep the content alive, use TempTag::leak()"]
19pub struct TempTag {
20 inner: HashAndFormat,
22 #[serde(skip)]
24 on_drop: Option<Weak<dyn TagDrop>>,
25}
26
27impl AsRef<Hash> for TempTag {
28 fn as_ref(&self) -> &Hash {
29 &self.inner.hash
30 }
31}
32
33pub trait TagCounter: TagDrop + Sized {
42 fn on_create(&self, inner: &HashAndFormat);
44
45 fn as_weak(self: &Arc<Self>) -> Weak<dyn TagDrop> {
47 let on_drop: Arc<dyn TagDrop> = self.clone();
48 Arc::downgrade(&on_drop)
49 }
50
51 fn temp_tag(self: &Arc<Self>, inner: HashAndFormat) -> TempTag {
53 self.on_create(&inner);
54 TempTag::new(inner, Some(self.as_weak()))
55 }
56}
57
58pub trait TagDrop: std::fmt::Debug + Send + Sync + 'static {
61 fn on_drop(&self, inner: &HashAndFormat);
63}
64
65impl From<&TempTag> for HashAndFormat {
66 fn from(val: &TempTag) -> Self {
67 val.inner
68 }
69}
70
71impl From<TempTag> for HashAndFormat {
72 fn from(val: TempTag) -> Self {
73 val.inner
74 }
75}
76
77impl TempTag {
78 pub fn new(inner: HashAndFormat, on_drop: Option<Weak<dyn TagDrop>>) -> Self {
86 Self { inner, on_drop }
87 }
88
89 pub fn leaking_empty(format: BlobFormat) -> Self {
91 Self {
92 inner: HashAndFormat {
93 hash: Hash::EMPTY,
94 format,
95 },
96 on_drop: None,
97 }
98 }
99
100 pub fn hash(&self) -> Hash {
102 self.inner.hash
103 }
104
105 pub fn format(&self) -> BlobFormat {
107 self.inner.format
108 }
109
110 pub fn hash_and_format(&self) -> HashAndFormat {
112 self.inner
113 }
114
115 pub fn leak(&mut self) {
117 self.on_drop = None;
121 }
122}
123
124impl Drop for TempTag {
125 fn drop(&mut self) {
126 if let Some(on_drop) = self.on_drop.take() {
127 if let Some(on_drop) = on_drop.upgrade() {
128 on_drop.on_drop(&self.inner);
129 }
130 }
131 }
132}
133
134#[derive(Debug, Default, Clone)]
135struct TempCounters {
136 raw: u64,
138 hash_seq: u64,
140}
141
142impl TempCounters {
143 fn counter(&self, format: BlobFormat) -> u64 {
144 match format {
145 BlobFormat::Raw => self.raw,
146 BlobFormat::HashSeq => self.hash_seq,
147 }
148 }
149
150 fn counter_mut(&mut self, format: BlobFormat) -> &mut u64 {
151 match format {
152 BlobFormat::Raw => &mut self.raw,
153 BlobFormat::HashSeq => &mut self.hash_seq,
154 }
155 }
156
157 fn inc(&mut self, format: BlobFormat) {
158 let counter = self.counter_mut(format);
159 *counter = counter.checked_add(1).unwrap();
160 }
161
162 fn dec(&mut self, format: BlobFormat) {
163 let counter = self.counter_mut(format);
164 *counter = counter.saturating_sub(1);
165 }
166
167 fn is_empty(&self) -> bool {
168 self.raw == 0 && self.hash_seq == 0
169 }
170}
171
172#[derive(Debug, Default)]
173pub(crate) struct TempTags {
174 scopes: HashMap<Scope, Arc<TempTagScope>>,
175 next_scope: u64,
176}
177
178impl TempTags {
179 pub fn create_scope(&mut self) -> (Scope, Arc<TempTagScope>) {
180 self.next_scope += 1;
181 let id = Scope(self.next_scope);
182 let scope = self.scopes.entry(id).or_default();
183 (id, scope.clone())
184 }
185
186 pub fn end_scope(&mut self, scope: Scope) {
187 self.scopes.remove(&scope);
188 }
189
190 pub fn list(&self) -> Vec<HashAndFormat> {
191 self.scopes
192 .values()
193 .flat_map(|scope| scope.list())
194 .collect()
195 }
196
197 pub fn create(&mut self, scope: Scope, content: HashAndFormat) -> TempTag {
198 let scope = self.scopes.entry(scope).or_default();
199
200 scope.temp_tag(content)
201 }
202
203 pub fn contains(&self, hash: Hash) -> bool {
204 self.scopes
205 .values()
206 .any(|scope| scope.0.lock().unwrap().contains(&HashAndFormat::raw(hash)))
207 }
208}
209
210#[derive(Debug, Default)]
211pub(crate) struct TempTagScope(Mutex<TempCounterMap>);
212
213impl TempTagScope {
214 pub fn list(&self) -> impl Iterator<Item = HashAndFormat> + 'static {
215 let guard = self.0.lock().unwrap();
216 let res = guard.keys();
217 drop(guard);
218 res.into_iter()
219 }
220}
221
222impl TagDrop for TempTagScope {
223 fn on_drop(&self, inner: &HashAndFormat) {
224 trace!("Dropping temp tag {:?}", inner);
225 self.0.lock().unwrap().dec(inner);
226 }
227}
228
229impl TagCounter for TempTagScope {
230 fn on_create(&self, inner: &HashAndFormat) {
231 trace!("Creating temp tag {:?}", inner);
232 self.0.lock().unwrap().inc(*inner);
233 }
234}
235
236#[derive(Debug, Clone, Default)]
237pub(crate) struct TempCounterMap(HashMap<Hash, TempCounters>);
238
239impl TempCounterMap {
240 pub fn inc(&mut self, value: HashAndFormat) {
241 self.0.entry(value.hash).or_default().inc(value.format)
242 }
243
244 pub fn dec(&mut self, value: &HashAndFormat) {
245 let HashAndFormat { hash, format } = value;
246 let Some(counters) = self.0.get_mut(hash) else {
247 warn!("Decrementing non-existent temp tag");
248 return;
249 };
250 counters.dec(*format);
251 if counters.is_empty() {
252 self.0.remove(hash);
253 }
254 }
255
256 pub fn contains(&self, haf: &HashAndFormat) -> bool {
257 let Some(entry) = self.0.get(&haf.hash) else {
258 return false;
259 };
260 entry.counter(haf.format) > 0
261 }
262
263 pub fn keys(&self) -> Vec<HashAndFormat> {
264 let mut res = Vec::new();
265 for (k, v) in self.0.iter() {
266 if v.raw > 0 {
267 res.push(HashAndFormat::raw(*k));
268 }
269 if v.hash_seq > 0 {
270 res.push(HashAndFormat::hash_seq(*k));
271 }
272 }
273 res
274 }
275}