|
1 use std::fmt::Debug; |
1 use std::ops::Deref; |
2 use std::ops::Deref; |
2 |
3 |
3 use byteorder::{BigEndian, ByteOrder}; |
4 use byteorder::{BigEndian, ByteOrder}; |
4 |
5 |
5 use crate::errors::HgError; |
6 use crate::errors::HgError; |
6 use crate::revlog::node::Node; |
7 use crate::revlog::node::Node; |
7 use crate::revlog::{Revision, NULL_REVISION}; |
8 use crate::revlog::{Revision, NULL_REVISION}; |
|
9 use crate::UncheckedRevision; |
8 |
10 |
9 pub const INDEX_ENTRY_SIZE: usize = 64; |
11 pub const INDEX_ENTRY_SIZE: usize = 64; |
10 |
12 |
11 pub struct IndexHeader { |
13 pub struct IndexHeader { |
12 header_bytes: [u8; 4], |
14 header_bytes: [u8; 4], |
82 bytes: Box<dyn Deref<Target = [u8]> + Send>, |
84 bytes: Box<dyn Deref<Target = [u8]> + Send>, |
83 /// Offsets of starts of index blocks. |
85 /// Offsets of starts of index blocks. |
84 /// Only needed when the index is interleaved with data. |
86 /// Only needed when the index is interleaved with data. |
85 offsets: Option<Vec<usize>>, |
87 offsets: Option<Vec<usize>>, |
86 uses_generaldelta: bool, |
88 uses_generaldelta: bool, |
|
89 } |
|
90 |
|
91 impl Debug for Index { |
|
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
93 f.debug_struct("Index") |
|
94 .field("offsets", &self.offsets) |
|
95 .field("uses_generaldelta", &self.uses_generaldelta) |
|
96 .finish() |
|
97 } |
87 } |
98 } |
88 |
99 |
89 impl Index { |
100 impl Index { |
90 /// Create an index from bytes. |
101 /// Create an index from bytes. |
91 /// Calculate the start of each entry when is_inline is true. |
102 /// Calculate the start of each entry when is_inline is true. |
173 /// exists. |
184 /// exists. |
174 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> { |
185 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> { |
175 if rev == NULL_REVISION { |
186 if rev == NULL_REVISION { |
176 return None; |
187 return None; |
177 } |
188 } |
178 if let Some(offsets) = &self.offsets { |
189 Some(if let Some(offsets) = &self.offsets { |
179 self.get_entry_inline(rev, offsets) |
190 self.get_entry_inline(rev, offsets) |
180 } else { |
191 } else { |
181 self.get_entry_separated(rev) |
192 self.get_entry_separated(rev) |
182 } |
193 }) |
183 } |
194 } |
184 |
195 |
185 fn get_entry_inline( |
196 fn get_entry_inline( |
186 &self, |
197 &self, |
187 rev: Revision, |
198 rev: Revision, |
188 offsets: &[usize], |
199 offsets: &[usize], |
189 ) -> Option<IndexEntry> { |
200 ) -> IndexEntry { |
190 let start = *offsets.get(rev as usize)?; |
201 let start = offsets[rev as usize]; |
191 let end = start.checked_add(INDEX_ENTRY_SIZE)?; |
202 let end = start + INDEX_ENTRY_SIZE; |
192 let bytes = &self.bytes[start..end]; |
203 let bytes = &self.bytes[start..end]; |
193 |
204 |
194 // See IndexEntry for an explanation of this override. |
205 // See IndexEntry for an explanation of this override. |
195 let offset_override = Some(end); |
206 let offset_override = Some(end); |
196 |
207 |
197 Some(IndexEntry { |
208 IndexEntry { |
198 bytes, |
209 bytes, |
199 offset_override, |
210 offset_override, |
200 }) |
211 } |
201 } |
212 } |
202 |
213 |
203 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> { |
214 fn get_entry_separated(&self, rev: Revision) -> IndexEntry { |
204 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE; |
|
205 if rev as usize >= max_rev { |
|
206 return None; |
|
207 } |
|
208 let start = rev as usize * INDEX_ENTRY_SIZE; |
215 let start = rev as usize * INDEX_ENTRY_SIZE; |
209 let end = start + INDEX_ENTRY_SIZE; |
216 let end = start + INDEX_ENTRY_SIZE; |
210 let bytes = &self.bytes[start..end]; |
217 let bytes = &self.bytes[start..end]; |
211 |
218 |
212 // Override the offset of the first revision as its bytes are used |
219 // Override the offset of the first revision as its bytes are used |
213 // for the index's metadata (saving space because it is always 0) |
220 // for the index's metadata (saving space because it is always 0) |
214 let offset_override = if rev == 0 { Some(0) } else { None }; |
221 let offset_override = if rev == 0 { Some(0) } else { None }; |
215 |
222 |
216 Some(IndexEntry { |
223 IndexEntry { |
217 bytes, |
224 bytes, |
218 offset_override, |
225 offset_override, |
219 }) |
226 } |
220 } |
227 } |
221 } |
228 } |
222 |
229 |
223 impl super::RevlogIndex for Index { |
230 impl super::RevlogIndex for Index { |
224 fn len(&self) -> usize { |
231 fn len(&self) -> usize { |
271 pub fn uncompressed_len(&self) -> i32 { |
278 pub fn uncompressed_len(&self) -> i32 { |
272 BigEndian::read_i32(&self.bytes[12..=15]) |
279 BigEndian::read_i32(&self.bytes[12..=15]) |
273 } |
280 } |
274 |
281 |
275 /// Return the revision upon which the data has been derived. |
282 /// Return the revision upon which the data has been derived. |
276 pub fn base_revision_or_base_of_delta_chain(&self) -> Revision { |
283 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision { |
277 // TODO Maybe return an Option when base_revision == rev? |
284 // TODO Maybe return an Option when base_revision == rev? |
278 // Requires to add rev to IndexEntry |
285 // Requires to add rev to IndexEntry |
279 |
286 |
280 BigEndian::read_i32(&self.bytes[16..]) |
287 BigEndian::read_i32(&self.bytes[16..]).into() |
281 } |
288 } |
282 |
289 |
283 pub fn link_revision(&self) -> Revision { |
290 pub fn link_revision(&self) -> UncheckedRevision { |
284 BigEndian::read_i32(&self.bytes[20..]) |
291 BigEndian::read_i32(&self.bytes[20..]).into() |
285 } |
292 } |
286 |
293 |
287 pub fn p1(&self) -> Revision { |
294 pub fn p1(&self) -> UncheckedRevision { |
288 BigEndian::read_i32(&self.bytes[24..]) |
295 BigEndian::read_i32(&self.bytes[24..]).into() |
289 } |
296 } |
290 |
297 |
291 pub fn p2(&self) -> Revision { |
298 pub fn p2(&self) -> UncheckedRevision { |
292 BigEndian::read_i32(&self.bytes[28..]) |
299 BigEndian::read_i32(&self.bytes[28..]).into() |
293 } |
300 } |
294 |
301 |
295 /// Return the hash of revision's full text. |
302 /// Return the hash of revision's full text. |
296 /// |
303 /// |
297 /// Currently, SHA-1 is used and only the first 20 bytes of this field |
304 /// Currently, SHA-1 is used and only the first 20 bytes of this field |
545 let entry = IndexEntry { |
552 let entry = IndexEntry { |
546 bytes: &bytes, |
553 bytes: &bytes, |
547 offset_override: None, |
554 offset_override: None, |
548 }; |
555 }; |
549 |
556 |
550 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1) |
557 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into()) |
551 } |
558 } |
552 |
559 |
553 #[test] |
560 #[test] |
554 fn link_revision_test() { |
561 fn link_revision_test() { |
555 let bytes = IndexEntryBuilder::new().with_link_revision(123).build(); |
562 let bytes = IndexEntryBuilder::new().with_link_revision(123).build(); |
557 let entry = IndexEntry { |
564 let entry = IndexEntry { |
558 bytes: &bytes, |
565 bytes: &bytes, |
559 offset_override: None, |
566 offset_override: None, |
560 }; |
567 }; |
561 |
568 |
562 assert_eq!(entry.link_revision(), 123); |
569 assert_eq!(entry.link_revision(), 123.into()); |
563 } |
570 } |
564 |
571 |
565 #[test] |
572 #[test] |
566 fn p1_test() { |
573 fn p1_test() { |
567 let bytes = IndexEntryBuilder::new().with_p1(123).build(); |
574 let bytes = IndexEntryBuilder::new().with_p1(123).build(); |
581 let entry = IndexEntry { |
588 let entry = IndexEntry { |
582 bytes: &bytes, |
589 bytes: &bytes, |
583 offset_override: None, |
590 offset_override: None, |
584 }; |
591 }; |
585 |
592 |
586 assert_eq!(entry.p2(), 123); |
593 assert_eq!(entry.p2(), 123.into()); |
587 } |
594 } |
588 |
595 |
589 #[test] |
596 #[test] |
590 fn node_test() { |
597 fn node_test() { |
591 let node = Node::from_hex("0123456789012345678901234567890123456789") |
598 let node = Node::from_hex("0123456789012345678901234567890123456789") |