Sebastian Thielthanks clippybac57dd3 years agoHistory
189 lines · 8.9 KB · Rust
RawDownload
use std::ops::Deref; use git_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 git_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| {                                git_pack::data::decode::header::ResolvedBase::InPack(pack.entry(pack_offset))                            })                        }) {                            Ok(header) => Ok(header.into()),                            Err(git_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| {                                            git_pack::data::decode::header::ResolvedBase::InPack(                                                pack.entry(pack_offset),                                            )                                        })                                        .or_else(|| {                                            (id == base_id).then(|| {                                                git_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)    }}