177 lines · 6.4 KB · Rust
RawDownload
use crate::{    extension::{Link, Signature},    util::split_at_pos,}; /// The signature of the link extension.pub const SIGNATURE: Signature = *b"link"; /// Bitmaps to know which entries to delete or replace, even though details are still unknown.#[derive(Clone)]pub struct Bitmaps {    /// A bitmap to signal which entries to delete, maybe.    pub delete: git_bitmap::ewah::Vec,    /// A bitmap to signal which entries to replace, maybe.    pub replace: git_bitmap::ewah::Vec,} ///pub mod decode {     /// The error returned when decoding link extensions.    #[derive(Debug, thiserror::Error)]    #[allow(missing_docs)]    pub enum Error {        #[error("{0}")]        Corrupt(&'static str),        #[error("{kind} bitmap corrupt")]        BitmapDecode {            err: git_bitmap::ewah::decode::Error,            kind: &'static str,        },    }     impl From<std::num::TryFromIntError> for Error {        fn from(_: std::num::TryFromIntError) -> Self {            Self::Corrupt("error in bitmap iteration trying to convert from u64 to usize")        }    }} pub(crate) fn decode(data: &[u8], object_hash: git_hash::Kind) -> Result<Link, decode::Error> {    let (id, data) = split_at_pos(data, object_hash.len_in_bytes())        .ok_or(decode::Error::Corrupt(            "link extension too short to read share index checksum",        ))        .map(|(id, d)| (git_hash::ObjectId::from(id), d))?;     if data.is_empty() {        return Ok(Link {            shared_index_checksum: id,            bitmaps: None,        });    }     let (delete, data) =        git_bitmap::ewah::decode(data).map_err(|err| decode::Error::BitmapDecode { kind: "delete", err })?;    let (replace, data) =        git_bitmap::ewah::decode(data).map_err(|err| decode::Error::BitmapDecode { kind: "replace", err })?;     if !data.is_empty() {        return Err(decode::Error::Corrupt("garbage trailing link extension"));    }     Ok(Link {        shared_index_checksum: id,        bitmaps: Some(Bitmaps { delete, replace }),    })} impl Link {    pub(crate) fn dissolve_into(        self,        split_index: &mut crate::File,        object_hash: git_hash::Kind,        options: crate::decode::Options,    ) -> Result<(), crate::file::init::Error> {        let shared_index_path = split_index            .path            .parent()            .expect("split index file in .git folder")            .join(format!("sharedindex.{}", self.shared_index_checksum));        let mut shared_index = crate::File::at(            &shared_index_path,            object_hash,            crate::decode::Options {                expected_checksum: self.shared_index_checksum.into(),                ..options            },        )?;         if let Some(bitmaps) = self.bitmaps {            let mut split_entry_index = 0;             let mut err = None;            bitmaps.replace.for_each_set_bit(|replace_index| {                let shared_entry = match shared_index.entries.get_mut(replace_index) {                    Some(e) => e,                    None => {                        err = decode::Error::Corrupt("replace bitmap length exceeds shared index length - more entries in bitmap than found in shared index").into();                        return None                    }                };                 if shared_entry.flags.contains(crate::entry::Flags::REMOVE) {                    err = decode::Error::Corrupt("entry is marked as both replace and delete").into();                    return None                }                 let split_entry = match split_index.entries.get(split_entry_index) {                    Some(e) => e,                    None => {                        err = decode::Error::Corrupt("replace bitmap length exceeds split index length - more entries in bitmap than found in split index").into();                        return None                    }                };                if !split_entry.path.is_empty() {                    err = decode::Error::Corrupt("paths in split index entries that are for replacement should be empty").into();                    return None                }                if shared_entry.path.is_empty() {                    err = decode::Error::Corrupt("paths in shared index entries that are replaced should not be empty").into();                    return None                }                shared_entry.stat = split_entry.stat;                shared_entry.id = split_entry.id;                shared_entry.flags = split_entry.flags;                shared_entry.mode = split_entry.mode;                 split_entry_index += 1;                Some(())            });            if let Some(err) = err {                return Err(err.into());            }             let split_index_path_backing = std::mem::take(&mut split_index.path_backing);            for mut split_entry in split_index.entries.drain(split_entry_index..) {                let start = shared_index.path_backing.len();                let split_index_path = split_entry.path.clone();                 split_entry.path = start..start + split_entry.path.len();                shared_index.entries.push(split_entry);                 shared_index                    .path_backing                    .extend_from_slice(&split_index_path_backing[split_index_path]);            }             bitmaps.delete.for_each_set_bit(|delete_index| {                let shared_entry = match shared_index.entries.get_mut(delete_index) {                    Some(e) => e,                    None => {                        err = decode::Error::Corrupt("delete bitmap length exceeds shared index length - more entries in bitmap than found in shared index").into();                        return None                    }                };                shared_entry.flags.insert(crate::entry::Flags::REMOVE);                Some(())            });            if let Some(err) = err {                return Err(err.into());            }             shared_index                .entries                .retain(|e| !e.flags.contains(crate::entry::Flags::REMOVE));             let mut shared_entries = std::mem::take(&mut shared_index.entries);            shared_entries.sort_by(|a, b| a.cmp(b, &shared_index.state));             split_index.entries = shared_entries;            split_index.path_backing = std::mem::take(&mut shared_index.path_backing);        }         Ok(())    }}