comparison rust/hg-core/src/revlog/index.rs @ 45601:900b9b79b99c

hg-core: make `Index` owner of its bytes (D8958#inline-14994 followup 1/2) Prevent building `Index` every time it is needed. It was a bad idea anyway. When `Index::new` will return `Result` it will avoid things like `Revlog::len` returning `Result<usize>` instead of `usize`. [X] make `Index` owner of its bytes [ ] make `Index::new` return an error if `offset != bytes.len()` Differential Revision: https://phab.mercurial-scm.org/D9106
author Antoine cezar<acezar@chwitlabs.fr>
date Mon, 28 Sep 2020 15:13:51 +0200
parents da30e4b553c3
children 1cef583541c0
comparison
equal deleted inserted replaced
45600:b68b19104d16 45601:900b9b79b99c
1 use std::ops::Deref;
2
1 use byteorder::{BigEndian, ByteOrder}; 3 use byteorder::{BigEndian, ByteOrder};
2 4
3 use crate::revlog::{Revision, NULL_REVISION}; 5 use crate::revlog::{Revision, NULL_REVISION};
4 6
5 pub const INDEX_ENTRY_SIZE: usize = 64; 7 pub const INDEX_ENTRY_SIZE: usize = 64;
6 8
7 /// A Revlog index 9 /// A Revlog index
8 #[derive(Debug)] 10 pub struct Index {
9 pub struct Index<'a> { 11 bytes: Box<dyn Deref<Target = [u8]> + Send>,
10 bytes: &'a [u8],
11 /// Offsets of starts of index blocks. 12 /// Offsets of starts of index blocks.
12 /// Only needed when the index is interleaved with data. 13 /// Only needed when the index is interleaved with data.
13 offsets: Option<Vec<usize>>, 14 offsets: Option<Vec<usize>>,
14 } 15 }
15 16
16 impl<'a> Index<'a> { 17 impl Index {
17 /// Create an index from bytes. 18 /// Create an index from bytes.
18 /// Calculate the start of each entry when is_inline is true. 19 /// Calculate the start of each entry when is_inline is true.
19 pub fn new(bytes: &'a [u8], is_inline: bool) -> Self { 20 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
20 if is_inline { 21 if is_inline(&bytes) {
21 let mut offset: usize = 0; 22 let mut offset: usize = 0;
22 let mut offsets = Vec::new(); 23 let mut offsets = Vec::new();
23 24
24 while offset + INDEX_ENTRY_SIZE <= bytes.len() { 25 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
25 offsets.push(offset); 26 offsets.push(offset);
40 Self { 41 Self {
41 bytes, 42 bytes,
42 offsets: None, 43 offsets: None,
43 } 44 }
44 } 45 }
46 }
47
48 /// Value of the inline flag.
49 pub fn is_inline(&self) -> bool {
50 is_inline(&self.bytes)
51 }
52
53 /// Return a slice of bytes if `revlog` is inline. Panic if not.
54 pub fn data(&self, start: usize, end: usize) -> &[u8] {
55 if !self.is_inline() {
56 panic!("tried to access data in the index of a revlog that is not inline");
57 }
58 &self.bytes[start..end]
45 } 59 }
46 60
47 /// Return number of entries of the revlog index. 61 /// Return number of entries of the revlog index.
48 pub fn len(&self) -> usize { 62 pub fn len(&self) -> usize {
49 if let Some(offsets) = &self.offsets { 63 if let Some(offsets) = &self.offsets {
167 /// 181 ///
168 /// Currently, SHA-1 is used and only the first 20 bytes of this field 182 /// Currently, SHA-1 is used and only the first 20 bytes of this field
169 /// are used. 183 /// are used.
170 pub fn hash(&self) -> &[u8] { 184 pub fn hash(&self) -> &[u8] {
171 &self.bytes[32..52] 185 &self.bytes[32..52]
186 }
187 }
188
189 /// Value of the inline flag.
190 pub fn is_inline(index_bytes: &[u8]) -> bool {
191 match &index_bytes[0..=1] {
192 [0, 0] | [0, 2] => false,
193 _ => true,
172 } 194 }
173 } 195 }
174 196
175 #[cfg(test)] 197 #[cfg(test)]
176 mod tests { 198 mod tests {
267 bytes 289 bytes
268 } 290 }
269 } 291 }
270 292
271 #[test] 293 #[test]
294 fn is_not_inline_when_no_inline_flag_test() {
295 let bytes = IndexEntryBuilder::new()
296 .is_first(true)
297 .with_general_delta(false)
298 .with_inline(false)
299 .build();
300
301 assert_eq!(is_inline(&bytes), false)
302 }
303
304 #[test]
305 fn is_inline_when_inline_flag_test() {
306 let bytes = IndexEntryBuilder::new()
307 .is_first(true)
308 .with_general_delta(false)
309 .with_inline(true)
310 .build();
311
312 assert_eq!(is_inline(&bytes), true)
313 }
314
315 #[test]
316 fn is_inline_when_inline_and_generaldelta_flags_test() {
317 let bytes = IndexEntryBuilder::new()
318 .is_first(true)
319 .with_general_delta(true)
320 .with_inline(true)
321 .build();
322
323 assert_eq!(is_inline(&bytes), true)
324 }
325
326 #[test]
272 fn test_offset() { 327 fn test_offset() {
273 let bytes = IndexEntryBuilder::new().with_offset(1).build(); 328 let bytes = IndexEntryBuilder::new().with_offset(1).build();
274 let entry = IndexEntry { 329 let entry = IndexEntry {
275 bytes: &bytes, 330 bytes: &bytes,
276 offset_override: None, 331 offset_override: None,