| @@ -1,202 +0,0 @@ |
| 1 | use crate::{ | | |
| 2 | pack::index::write::EntrySlice, | | |
| 3 | pack::tree::{Item, Tree}, | | |
| 4 | }; | | |
| 5 | use git_features::{parallel, parallel::in_parallel_if, progress::Progress}; | | |
| 6 | use git_object::HashKind; | | |
| 7 | | | |
| 8 | impl<T> Tree<T> | | |
| 9 | where | | |
| 10 | T: Default + Send, | | |
| 11 | { | | |
| 12 | pub fn traverse<F, P, MBFN, BR, MCFN>( | | |
| 13 | mut self, | | |
| 14 | should_run_in_parallel: impl FnOnce() -> bool, | | |
| 15 | resolve: F, | | |
| 16 | mut progress: P, | | |
| 17 | thread_limit: Option<usize>, | | |
| 18 | pack_entries_end: u64, | | |
| 19 | hash_kind: HashKind, | | |
| 20 | modify_base: MBFN, | | |
| 21 | modify_child: MCFN, | | |
| 22 | ) -> Result<Vec<Item<T>>, Box<dyn std::error::Error + Send + Sync>> | | |
| 23 | where | | |
| 24 | F: for<'r> Fn(EntrySlice, &'r mut Vec<u8>) -> Option<()> + Send + Sync, | | |
| 25 | P: Progress, | | |
| 26 | <P as Progress>::SubProgress: Send, | | |
| 27 | MBFN: for<'r> Fn(&'r mut T, &'r [u8], HashKind) -> BR, | | |
| 28 | BR: Clone, | | |
| 29 | MCFN: for<'r> Fn(&'r mut T, BR), | | |
| 30 | { | | |
| 31 | self.pack_entries_end = Some(pack_entries_end); | | |
| 32 | let (chunk_size, thread_limit, _) = parallel::optimize_chunk_size_and_thread_limit(1, None, thread_limit, None); | | |
| 33 | let reduce_progress = parking_lot::Mutex::new(progress.add_child("Resolving")); | | |
| 34 | | | |
| 35 | // SAFETY: We are owning 'self', and it's the UnsafeCell which we are supposed to use requiring unsafe on every access now. | | |
| 36 | #[allow(unsafe_code)] | | |
| 37 | let num_objects = unsafe { (*self.items.get()).len() } as u32; | | |
| 38 | in_parallel_if( | | |
| 39 | should_run_in_parallel, | | |
| 40 | self.iter_root_chunks(chunk_size), | | |
| 41 | thread_limit, | | |
| 42 | |thread_index| { | | |
| 43 | ( | | |
| 44 | Vec::<u8>::with_capacity(4096), | | |
| 45 | reduce_progress.lock().add_child(format!("thread {}", thread_index)), | | |
| 46 | ) | | |
| 47 | }, | | |
| 48 | // |root_nodes, state| apply_deltas(root_nodes, state, &resolver, kind.hash()), | | |
| 49 | |_root_nodes, _state| Ok(0), | | |
| 50 | Reducer::new(num_objects, &reduce_progress), | | |
| 51 | )?; | | |
| 52 | Ok(self.into_items()) | | |
| 53 | } | | |
| 54 | } | | |
| 55 | | | |
| 56 | pub(crate) struct Reducer<'a, P> { | | |
| 57 | item_count: usize, | | |
| 58 | progress: &'a parking_lot::Mutex<P>, | | |
| 59 | start: std::time::Instant, | | |
| 60 | } | | |
| 61 | | | |
| 62 | impl<'a, P> Reducer<'a, P> | | |
| 63 | where | | |
| 64 | P: Progress, | | |
| 65 | { | | |
| 66 | pub fn new(num_objects: u32, progress: &'a parking_lot::Mutex<P>) -> Self { | | |
| 67 | progress.lock().init(Some(num_objects), Some("objects")); | | |
| 68 | Reducer { | | |
| 69 | item_count: 0, | | |
| 70 | progress, | | |
| 71 | start: std::time::Instant::now(), | | |
| 72 | } | | |
| 73 | } | | |
| 74 | } | | |
| 75 | | | |
| 76 | impl<'a, P> parallel::Reducer for Reducer<'a, P> | | |
| 77 | where | | |
| 78 | P: Progress, | | |
| 79 | { | | |
| 80 | type Input = Result<usize, Box<dyn std::error::Error + Send + Sync>>; | | |
| 81 | type Output = (); | | |
| 82 | type Error = Box<dyn std::error::Error + Send + Sync>; | | |
| 83 | | | |
| 84 | fn feed(&mut self, input: Self::Input) -> Result<(), Self::Error> { | | |
| 85 | let input = input?; | | |
| 86 | self.item_count += input; | | |
| 87 | self.progress.lock().set(self.item_count as u32); | | |
| 88 | Ok(()) | | |
| 89 | } | | |
| 90 | | | |
| 91 | fn finalize(self) -> Result<Self::Output, Self::Error> { | | |
| 92 | self.progress | | |
| 93 | .lock() | | |
| 94 | .show_throughput(self.start, self.item_count as u32, "objects"); | | |
| 95 | Ok(()) | | |
| 96 | } | | |
| 97 | } | | |
| 98 | | | |
| 99 | mod apply { | | |
| 100 | use crate::{ | | |
| 101 | hash, loose, | | |
| 102 | pack::{self, index::write::EntrySlice, index::write::Error}, | | |
| 103 | zlib, | | |
| 104 | }; | | |
| 105 | use git_features::progress::Progress; | | |
| 106 | use git_object::{owned, HashKind}; | | |
| 107 | use std::{cell::RefCell, collections::BTreeMap, io}; | | |
| 108 | | | |
| 109 | pub(crate) fn apply_deltas<T, F, P, MBFN, BR, MCFN>( | | |
| 110 | nodes: Vec<pack::tree::Node<T>>, | | |
| 111 | (bytes_buf, progress): &mut (Vec<u8>, P), | | |
| 112 | resolve: F, | | |
| 113 | hash_kind: HashKind, | | |
| 114 | modify_base: MBFN, | | |
| 115 | modify_child: MCFN, | | |
| 116 | ) -> Result<usize, Error> | | |
| 117 | where | | |
| 118 | F: for<'r> Fn(EntrySlice, &'r mut Vec<u8>) -> Option<()> + Send + Sync, | | |
| 119 | P: Progress, | | |
| 120 | MBFN: for<'r> Fn(&'r mut T, &'r [u8], HashKind) -> BR, | | |
| 121 | BR: Clone, | | |
| 122 | MCFN: for<'r> Fn(&'r mut T, BR), | | |
| 123 | T: Default, | | |
| 124 | { | | |
| 125 | let mut decompressed_bytes_by_pack_offset = BTreeMap::new(); | | |
| 126 | let bytes_buf = RefCell::new(bytes_buf); | | |
| 127 | let mut num_objects = 0; | | |
| 128 | let decompress_from_resolver = |slice: EntrySlice| -> Result<Vec<u8>, Error> { | | |
| 129 | let mut bytes_buf = bytes_buf.borrow_mut(); | | |
| 130 | bytes_buf.resize((slice.end - slice.start) as usize, 0); | | |
| 131 | resolve(slice.clone(), &mut bytes_buf).ok_or_else(|| Error::ConsumeResolveFailed(slice.start))?; | | |
| 132 | let entry = pack::data::Entry::from_bytes(&bytes_buf, slice.start); | | |
| 133 | decompress_all_at_once( | | |
| 134 | &bytes_buf[entry.header_size() as usize..], | | |
| 135 | entry.decompressed_size as usize, | | |
| 136 | ) | | |
| 137 | }; | | |
| 138 | | | |
| 139 | // Traverse the tree breadth first and loose the data produced for the base as it won't be needed anymore. | | |
| 140 | progress.init(None, Some("objects")); | | |
| 141 | | | |
| 142 | // each node is a base, and its children always start out as deltas which become a base after applying them. | | |
| 143 | // These will be pushed onto our stack until all are processed | | |
| 144 | let root_level = 0; | | |
| 145 | let mut nodes: Vec<_> = nodes.into_iter().map(|n| (root_level, n)).collect(); | | |
| 146 | while let Some((level, mut base)) = nodes.pop() { | | |
| 147 | let base_bytes = if level == root_level { | | |
| 148 | decompress_from_resolver(base.entry_slice())? | | |
| 149 | } else { | | |
| 150 | decompressed_bytes_by_pack_offset | | |
| 151 | .remove(&base.offset()) | | |
| 152 | .expect("we store the resolved delta buffer when done") | | |
| 153 | }; | | |
| 154 | | | |
| 155 | let base_result = modify_base(&mut base.data, &base_bytes, hash_kind); | | |
| 156 | num_objects += 1; | | |
| 157 | for mut child in base.store_changes_then_into_child_iter() { | | |
| 158 | let delta_bytes = decompress_from_resolver(child.entry_slice())?; | | |
| 159 | let (base_size, consumed) = pack::data::decode::delta_header_size_ofs(&delta_bytes); | | |
| 160 | let mut header_ofs = consumed; | | |
| 161 | assert_eq!( | | |
| 162 | base_bytes.len(), | | |
| 163 | base_size as usize, | | |
| 164 | "recorded base size in delta does not match" | | |
| 165 | ); | | |
| 166 | let (result_size, consumed) = pack::data::decode::delta_header_size_ofs(&delta_bytes[consumed..]); | | |
| 167 | header_ofs += consumed; | | |
| 168 | | | |
| 169 | let mut fully_resolved_delta_bytes = bytes_buf.borrow_mut(); | | |
| 170 | fully_resolved_delta_bytes.resize(result_size as usize, 0); | | |
| 171 | pack::data::decode::apply_delta( | | |
| 172 | &base_bytes, | | |
| 173 | &mut fully_resolved_delta_bytes, | | |
| 174 | &delta_bytes[header_ofs..], | | |
| 175 | ); | | |
| 176 | | | |
| 177 | decompressed_bytes_by_pack_offset.insert(child.offset(), fully_resolved_delta_bytes.to_owned()); | | |
| 178 | modify_child(&mut child.data, base_result.clone()); | | |
| 179 | nodes.push((level + 1, child)); | | |
| 180 | } | | |
| 181 | } | | |
| 182 | | | |
| 183 | Ok(num_objects) | | |
| 184 | } | | |
| 185 | | | |
| 186 | fn compute_hash(kind: git_object::Kind, bytes: &[u8], hash_kind: HashKind) -> owned::Id { | | |
| 187 | let mut write = hash::Write::new(io::sink(), hash_kind); | | |
| 188 | loose::object::header::encode(kind, bytes.len() as u64, &mut write) | | |
| 189 | .expect("write to sink and hash cannot fail"); | | |
| 190 | write.hash.update(bytes); | | |
| 191 | owned::Id::from(write.hash.digest()) | | |
| 192 | } | | |
| 193 | | | |
| 194 | fn decompress_all_at_once(b: &[u8], decompressed_len: usize) -> Result<Vec<u8>, Error> { | | |
| 195 | let mut out = Vec::new(); | | |
| 196 | out.resize(decompressed_len, 0); | | |
| 197 | zlib::Inflate::default() | | |
| 198 | .once(&b, &mut out, true) | | |
| 199 | .map_err(|err| Error::ConsumeZlibInflate(err, "Failed to decompress entry"))?; | | |
| 200 | Ok(out) | | |
| 201 | } | | |
| 202 | } | | |