gix_pack/cache/delta/traverse/
mod.rs1use std::{collections::TryReserveError, sync::atomic::AtomicBool};
2
3use gix_features::{
4 progress::{self, DynNestedProgress, Progress},
5 threading,
6 threading::{Mutable, OwnShared},
7};
8
9use crate::{
10 cache::delta::{Tree, traverse::util::ItemSliceSync, tree::Item},
11 data::EntryRange,
12};
13
14mod resolve;
15pub(crate) mod util;
16
17pub(super) type SharedRefDeltaChildren = OwnShared<Mutable<super::tree::RefDeltaChildren>>;
19
20#[derive(thiserror::Error, Debug)]
22#[allow(missing_docs)]
23pub enum Error {
24 #[error("{message}")]
25 ZlibInflate {
26 source: gix_zlib::inflate::Error,
27 message: &'static str,
28 },
29 #[error("The resolver failed to obtain the pack entry bytes for the entry at {pack_offset}")]
30 ResolveFailed { pack_offset: u64 },
31 #[error(transparent)]
32 EntryType(#[from] crate::data::entry::decode::Error),
33 #[error("One of the object inspectors failed")]
34 Inspect(#[from] Box<dyn std::error::Error + Send + Sync>),
35 #[error("Interrupted")]
36 Interrupted,
37 #[error("Entry too large to fit in memory")]
38 OutOfMemory,
39 #[error(
40 "The base at {base_pack_offset} was referred to by a ref-delta, but it was never added to the tree as if the pack was still thin."
41 )]
42 OutOfPackRefDelta {
43 base_pack_offset: crate::data::Offset,
45 },
46 #[error("The ref-delta base object {base_id} could not be found")]
47 UnresolvedRefDelta {
48 base_id: gix_hash::ObjectId,
50 },
51 #[error("Failed to hash an object while resolving in-pack ref-deltas")]
52 ObjectHash(#[from] gix_hash::hasher::Error),
53 #[error("Failed to spawn thread when switching to work-stealing mode")]
54 SpawnThread(#[from] std::io::Error),
55 #[error(transparent)]
56 Delta(#[from] crate::data::delta::apply::Error),
57}
58
59impl From<TryReserveError> for Error {
60 #[cold]
61 fn from(_: TryReserveError) -> Self {
62 Self::OutOfMemory
63 }
64}
65
66pub struct Context<'a> {
68 pub entry: &'a crate::data::Entry,
70 pub entry_end: u64,
72 pub decompressed: &'a [u8],
74 pub level: u16,
77}
78
79pub struct Options<'a, 's> {
81 pub object_progress: Box<dyn DynNestedProgress>,
83 pub size_progress: &'s mut dyn Progress,
85 pub thread_limit: Option<usize>,
88 pub should_interrupt: &'a AtomicBool,
90 pub object_hash: gix_hash::Kind,
93 pub alloc_limit_bytes: Option<usize>,
96}
97
98pub struct Outcome<T> {
100 pub roots: Vec<Item<T>>,
102 pub children: Vec<Item<T>>,
104}
105
106impl<T> Tree<T>
107where
108 T: Send,
109{
110 pub fn traverse<F, MBFN, E, R>(
128 mut self,
129 resolve: F,
130 resolve_data: &R,
131 pack_entries_end: u64,
132 inspect_object: MBFN,
133 Options {
134 thread_limit,
135 mut object_progress,
136 size_progress,
137 should_interrupt,
138 object_hash,
139 alloc_limit_bytes,
140 }: Options<'_, '_>,
141 ) -> Result<Outcome<T>, Error>
142 where
143 F: for<'r> Fn(EntryRange, &'r R) -> Option<&'r [u8]> + Send + Clone,
144 R: Send + Sync,
145 MBFN: FnMut(&mut T, &dyn Progress, Context<'_>) -> Result<(), E> + Send + Clone,
146 E: std::error::Error + Send + Sync + 'static,
147 {
148 self.set_pack_entries_end_and_resolve_ref_offsets(pack_entries_end)?;
149
150 let num_objects = self.num_items();
151 let object_counter = {
152 let progress = &mut object_progress;
153 progress.init(Some(num_objects), progress::count("objects"));
154 progress.counter()
155 };
156 size_progress.init(None, progress::bytes());
157 let size_counter = size_progress.counter();
158 let resolver_progress = object_progress.add_child("delta resolver".into());
159
160 let start = std::time::Instant::now();
161 let (mut root_items, mut child_items_vec, ref_delta_children) = self.take_root_child_and_refs();
162 let ref_delta_children =
163 (!ref_delta_children.is_empty()).then(|| OwnShared::new(Mutable::new(ref_delta_children)));
164 let child_items = ItemSliceSync::new(&mut child_items_vec);
165 #[expect(unsafe_code)]
167 unsafe {
168 resolve::all(
169 &mut root_items,
170 &child_items,
171 thread_limit,
172 num_objects,
173 object_counter,
174 size_counter,
175 &resolver_progress,
176 resolve,
177 resolve_data,
178 inspect_object,
179 ref_delta_children.clone(),
180 object_hash,
181 alloc_limit_bytes,
182 should_interrupt,
183 )?;
184 }
185
186 if let Some(ref_delta_children) = ref_delta_children {
187 if let Some((base_id, _children)) = threading::lock(&ref_delta_children).first_key_value() {
188 return Err(Error::UnresolvedRefDelta { base_id: *base_id });
189 }
190 }
191
192 object_progress.show_throughput(start);
193 size_progress.show_throughput(start);
194
195 Ok(Outcome {
196 roots: root_items,
197 children: child_items_vec,
198 })
199 }
200}