summaryrefslogtreecommitdiffstats
path: root/vendor/gix-index/src/extension/link.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-index/src/extension/link.rs')
-rw-r--r--vendor/gix-index/src/extension/link.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/vendor/gix-index/src/extension/link.rs b/vendor/gix-index/src/extension/link.rs
new file mode 100644
index 000000000..20ce9cb21
--- /dev/null
+++ b/vendor/gix-index/src/extension/link.rs
@@ -0,0 +1,177 @@
+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: gix_bitmap::ewah::Vec,
+ /// A bitmap to signal which entries to replace, maybe.
+ pub replace: gix_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: gix_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: gix_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)| (gix_hash::ObjectId::from(id), d))?;
+
+ if data.is_empty() {
+ return Ok(Link {
+ shared_index_checksum: id,
+ bitmaps: None,
+ });
+ }
+
+ let (delete, data) =
+ gix_bitmap::ewah::decode(data).map_err(|err| decode::Error::BitmapDecode { kind: "delete", err })?;
+ let (replace, data) =
+ gix_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: gix_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(())
+ }
+}