gix_index/extension/
link.rs1use crate::extension::{Link, Signature};
2
3pub const SIGNATURE: Signature = *b"link";
5
6#[derive(Clone)]
8pub struct Bitmaps {
9 pub delete: gix_bitmap::ewah::Vec,
11 pub replace: gix_bitmap::ewah::Vec,
13}
14
15pub mod decode {
17
18 #[derive(Debug, thiserror::Error)]
20 #[allow(missing_docs)]
21 pub enum Error {
22 #[error("{0}")]
23 Corrupt(&'static str),
24 #[error("{kind} bitmap corrupt")]
25 BitmapDecode {
26 err: gix_bitmap::ewah::decode::Error,
27 kind: &'static str,
28 },
29 }
30
31 impl From<std::num::TryFromIntError> for Error {
32 fn from(_: std::num::TryFromIntError) -> Self {
33 Self::Corrupt("error in bitmap iteration trying to convert from u64 to usize")
34 }
35 }
36}
37
38pub(crate) fn decode(data: &[u8], object_hash: gix_hash::Kind) -> Result<Link, decode::Error> {
39 let (id, data) = data
40 .split_at_checked(object_hash.len_in_bytes())
41 .ok_or(decode::Error::Corrupt(
42 "link extension too short to read share index checksum",
43 ))
44 .map(|(id, d)| (gix_hash::ObjectId::from_bytes_or_panic(id), d))?;
45
46 if data.is_empty() {
47 return Ok(Link {
48 shared_index_checksum: id,
49 bitmaps: None,
50 });
51 }
52
53 let (delete, data) =
54 gix_bitmap::ewah::decode(data).map_err(|err| decode::Error::BitmapDecode { kind: "delete", err })?;
55 let (replace, data) =
56 gix_bitmap::ewah::decode(data).map_err(|err| decode::Error::BitmapDecode { kind: "replace", err })?;
57
58 if !data.is_empty() {
59 return Err(decode::Error::Corrupt("garbage trailing link extension"));
60 }
61
62 Ok(Link {
63 shared_index_checksum: id,
64 bitmaps: Some(Bitmaps { delete, replace }),
65 })
66}
67
68impl Link {
69 pub(crate) fn dissolve_into(
70 self,
71 split_index: &mut crate::File,
72 object_hash: gix_hash::Kind,
73 skip_hash: bool,
74 options: crate::decode::Options,
75 ) -> Result<(), crate::file::init::Error> {
76 let shared_index_path = split_index
77 .path
78 .parent()
79 .expect("split index file in .git folder")
80 .join(format!("sharedindex.{}", self.shared_index_checksum));
81 let mut shared_index = crate::File::at(
82 shared_index_path,
83 object_hash,
84 skip_hash,
85 crate::decode::Options {
86 expected_checksum: self.shared_index_checksum.into(),
87 ..options
88 },
89 )?;
90
91 if let Some(bitmaps) = self.bitmaps {
92 let mut split_entry_index = 0;
93
94 let mut err = None;
95 bitmaps.replace.for_each_set_bit(|replace_index| {
96 let shared_entry = match shared_index.entries.get_mut(replace_index) {
97 Some(e) => e,
98 None => {
99 err = decode::Error::Corrupt("replace bitmap length exceeds shared index length - more entries in bitmap than found in shared index").into();
100 return None
101 }
102 };
103
104 if shared_entry.flags.contains(crate::entry::Flags::REMOVE) {
105 err = decode::Error::Corrupt("entry is marked as both replace and delete").into();
106 return None
107 }
108
109 let split_entry = match split_index.entries.get(split_entry_index) {
110 Some(e) => e,
111 None => {
112 err = decode::Error::Corrupt("replace bitmap length exceeds split index length - more entries in bitmap than found in split index").into();
113 return None
114 }
115 };
116 if !split_entry.path.is_empty() {
117 err = decode::Error::Corrupt("paths in split index entries that are for replacement should be empty").into();
118 return None
119 }
120 if shared_entry.path.is_empty() {
121 err = decode::Error::Corrupt("paths in shared index entries that are replaced should not be empty").into();
122 return None
123 }
124 shared_entry.stat = split_entry.stat;
125 shared_entry.id = split_entry.id;
126 shared_entry.flags = split_entry.flags;
127 shared_entry.mode = split_entry.mode;
128
129 split_entry_index += 1;
130 Some(())
131 });
132 if let Some(err) = err {
133 return Err(err.into());
134 }
135
136 let split_index_path_backing = std::mem::take(&mut split_index.path_backing);
137 for mut split_entry in split_index.entries.drain(split_entry_index..) {
138 let start = shared_index.path_backing.len();
139 let split_index_path = split_entry.path.clone();
140
141 split_entry.path = start..start + split_entry.path.len();
142 shared_index.entries.push(split_entry);
143
144 shared_index
145 .path_backing
146 .extend_from_slice(&split_index_path_backing[split_index_path]);
147 }
148
149 bitmaps.delete.for_each_set_bit(|delete_index| {
150 let shared_entry = match shared_index.entries.get_mut(delete_index) {
151 Some(e) => e,
152 None => {
153 err = decode::Error::Corrupt("delete bitmap length exceeds shared index length - more entries in bitmap than found in shared index").into();
154 return None
155 }
156 };
157 shared_entry.flags.insert(crate::entry::Flags::REMOVE);
158 Some(())
159 });
160 if let Some(err) = err {
161 return Err(err.into());
162 }
163
164 shared_index
165 .entries
166 .retain(|e| !e.flags.contains(crate::entry::Flags::REMOVE));
167
168 let mut shared_entries = std::mem::take(&mut shared_index.entries);
169 shared_entries.sort_by(|a, b| a.cmp(b, &shared_index.state));
170
171 split_index.entries = shared_entries;
172 split_index.path_backing = std::mem::take(&mut shared_index.path_backing);
173 }
174
175 Ok(())
176 }
177}