summaryrefslogtreecommitdiffstats
path: root/vendor/gix-odb/src/store_impls/dynamic/header.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-odb/src/store_impls/dynamic/header.rs')
-rw-r--r--vendor/gix-odb/src/store_impls/dynamic/header.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/store_impls/dynamic/header.rs b/vendor/gix-odb/src/store_impls/dynamic/header.rs
new file mode 100644
index 000000000..a1fb770ed
--- /dev/null
+++ b/vendor/gix-odb/src/store_impls/dynamic/header.rs
@@ -0,0 +1,189 @@
+use std::ops::Deref;
+
+use gix_hash::oid;
+
+use super::find::Error;
+use crate::{
+ find::Header,
+ store::{find::error::DeltaBaseRecursion, handle, load_index},
+};
+
+impl<S> super::Handle<S>
+where
+ S: Deref<Target = super::Store> + Clone,
+{
+ fn try_header_inner<'b>(
+ &'b self,
+ mut id: &'b gix_hash::oid,
+ snapshot: &mut load_index::Snapshot,
+ recursion: Option<DeltaBaseRecursion<'_>>,
+ ) -> Result<Option<Header>, Error> {
+ if let Some(r) = recursion {
+ if r.depth >= self.max_recursion_depth {
+ return Err(Error::DeltaBaseRecursionLimit {
+ max_depth: self.max_recursion_depth,
+ id: r.original_id.to_owned(),
+ });
+ }
+ } else if !self.ignore_replacements {
+ if let Ok(pos) = self
+ .store
+ .replacements
+ .binary_search_by(|(map_this, _)| map_this.as_ref().cmp(id))
+ {
+ id = self.store.replacements[pos].1.as_ref();
+ }
+ }
+
+ 'outer: loop {
+ {
+ let marker = snapshot.marker;
+ for (idx, index) in snapshot.indices.iter_mut().enumerate() {
+ if let Some(handle::index_lookup::Outcome {
+ object_index: handle::IndexForObjectInPack { pack_id, pack_offset },
+ index_file,
+ pack: possibly_pack,
+ }) = index.lookup(id)
+ {
+ let pack = match possibly_pack {
+ Some(pack) => pack,
+ None => match self.store.load_pack(pack_id, marker)? {
+ Some(pack) => {
+ *possibly_pack = Some(pack);
+ possibly_pack.as_deref().expect("just put it in")
+ }
+ None => {
+ // The pack wasn't available anymore so we are supposed to try another round with a fresh index
+ match self.store.load_one_index(self.refresh, snapshot.marker)? {
+ Some(new_snapshot) => {
+ *snapshot = new_snapshot;
+ self.clear_cache();
+ continue 'outer;
+ }
+ None => {
+ // nothing new in the index, kind of unexpected to not have a pack but to also
+ // to have no new index yet. We set the new index before removing any slots, so
+ // this should be observable.
+ return Ok(None);
+ }
+ }
+ }
+ },
+ };
+ let entry = pack.entry(pack_offset);
+ let res = match pack.decode_header(entry, |id| {
+ index_file.pack_offset_by_id(id).map(|pack_offset| {
+ gix_pack::data::decode::header::ResolvedBase::InPack(pack.entry(pack_offset))
+ })
+ }) {
+ Ok(header) => Ok(header.into()),
+ Err(gix_pack::data::decode::Error::DeltaBaseUnresolved(base_id)) => {
+ // Only with multi-pack indices it's allowed to jump to refer to other packs within this
+ // multi-pack. Otherwise this would constitute a thin pack which is only allowed in transit.
+ // However, if we somehow end up with that, we will resolve it safely, even though we could
+ // avoid handling this case and error instead.
+ let hdr = self
+ .try_header_inner(
+ &base_id,
+ snapshot,
+ recursion
+ .map(|r| r.inc_depth())
+ .or_else(|| DeltaBaseRecursion::new(id).into()),
+ )
+ .map_err(|err| Error::DeltaBaseLookup {
+ err: Box::new(err),
+ base_id,
+ id: id.to_owned(),
+ })?
+ .ok_or_else(|| Error::DeltaBaseMissing {
+ base_id,
+ id: id.to_owned(),
+ })?;
+ let handle::index_lookup::Outcome {
+ object_index:
+ handle::IndexForObjectInPack {
+ pack_id: _,
+ pack_offset,
+ },
+ index_file,
+ pack: possibly_pack,
+ } = match snapshot.indices[idx].lookup(id) {
+ Some(res) => res,
+ None => {
+ let mut out = None;
+ for index in snapshot.indices.iter_mut() {
+ out = index.lookup(id);
+ if out.is_some() {
+ break;
+ }
+ }
+
+ out.unwrap_or_else(|| {
+ panic!("could not find object {id} in any index after looking up one of its base objects {base_id}")
+ })
+ }
+ };
+ let pack = possibly_pack
+ .as_ref()
+ .expect("pack to still be available like just now");
+ let entry = pack.entry(pack_offset);
+ pack.decode_header(entry, |id| {
+ index_file
+ .pack_offset_by_id(id)
+ .map(|pack_offset| {
+ gix_pack::data::decode::header::ResolvedBase::InPack(
+ pack.entry(pack_offset),
+ )
+ })
+ .or_else(|| {
+ (id == base_id).then(|| {
+ gix_pack::data::decode::header::ResolvedBase::OutOfPack {
+ kind: hdr.kind(),
+ num_deltas: hdr.num_deltas(),
+ }
+ })
+ })
+ })
+ .map(Into::into)
+ }
+ Err(err) => Err(err),
+ }?;
+
+ if idx != 0 {
+ snapshot.indices.swap(0, idx);
+ }
+ return Ok(Some(res));
+ }
+ }
+ }
+
+ for lodb in snapshot.loose_dbs.iter() {
+ // TODO: remove this double-lookup once the borrow checker allows it.
+ if lodb.contains(id) {
+ return lodb.try_header(id).map(|opt| opt.map(Into::into)).map_err(Into::into);
+ }
+ }
+
+ match self.store.load_one_index(self.refresh, snapshot.marker)? {
+ Some(new_snapshot) => {
+ *snapshot = new_snapshot;
+ self.clear_cache();
+ }
+ None => return Ok(None),
+ }
+ }
+ }
+}
+
+impl<S> crate::Header for super::Handle<S>
+where
+ S: Deref<Target = super::Store> + Clone,
+{
+ type Error = Error;
+
+ fn try_header(&self, id: impl AsRef<oid>) -> Result<Option<Header>, Self::Error> {
+ let id = id.as_ref();
+ let mut snapshot = self.snapshot.borrow_mut();
+ self.try_header_inner(id, &mut snapshot, None)
+ }
+}