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