##// END OF EJS Templates
hg-core: make `Index` owner of its bytes (D8958#inline-14994 followup 1/2)...
Antoine cezar -
r46175:900b9b79 default
parent child Browse files
Show More
@@ -1,3 +1,5 b''
1 use std::ops::Deref;
2
1 3 use byteorder::{BigEndian, ByteOrder};
2 4
3 5 use crate::revlog::{Revision, NULL_REVISION};
@@ -5,19 +7,18 b' use crate::revlog::{Revision, NULL_REVIS'
5 7 pub const INDEX_ENTRY_SIZE: usize = 64;
6 8
7 9 /// A Revlog index
8 #[derive(Debug)]
9 pub struct Index<'a> {
10 bytes: &'a [u8],
10 pub struct Index {
11 bytes: Box<dyn Deref<Target = [u8]> + Send>,
11 12 /// Offsets of starts of index blocks.
12 13 /// Only needed when the index is interleaved with data.
13 14 offsets: Option<Vec<usize>>,
14 15 }
15 16
16 impl<'a> Index<'a> {
17 impl Index {
17 18 /// Create an index from bytes.
18 19 /// Calculate the start of each entry when is_inline is true.
19 pub fn new(bytes: &'a [u8], is_inline: bool) -> Self {
20 if is_inline {
20 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
21 if is_inline(&bytes) {
21 22 let mut offset: usize = 0;
22 23 let mut offsets = Vec::new();
23 24
@@ -44,6 +45,19 b" impl<'a> Index<'a> {"
44 45 }
45 46 }
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]
59 }
60
47 61 /// Return number of entries of the revlog index.
48 62 pub fn len(&self) -> usize {
49 63 if let Some(offsets) = &self.offsets {
@@ -172,6 +186,14 b" impl<'a> IndexEntry<'a> {"
172 186 }
173 187 }
174 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,
194 }
195 }
196
175 197 #[cfg(test)]
176 198 mod tests {
177 199 use super::*;
@@ -269,6 +291,39 b' mod tests {'
269 291 }
270 292
271 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 327 fn test_offset() {
273 328 let bytes = IndexEntryBuilder::new().with_offset(1).build();
274 329 let entry = IndexEntry {
@@ -36,7 +36,7 b' pub struct Revlog {'
36 36 /// When index and data are not interleaved: bytes of the revlog index.
37 37 /// When index and data are interleaved: bytes of the revlog index and
38 38 /// data.
39 index_bytes: Box<dyn Deref<Target = [u8]> + Send>,
39 index: Index,
40 40 /// When index and data are not interleaved: bytes of the revlog data
41 41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
42 42 }
@@ -56,15 +56,13 b' impl Revlog {'
56 56 return Err(RevlogError::UnsuportedVersion(version));
57 57 }
58 58
59 let is_inline = is_inline(&index_mmap);
60
61 let index_bytes = Box::new(index_mmap);
59 let index = Index::new(Box::new(index_mmap));
62 60
63 61 // TODO load data only when needed //
64 62 // type annotation required
65 63 // won't recognize Mmap as Deref<Target = [u8]>
66 64 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
67 if is_inline {
65 if index.is_inline() {
68 66 None
69 67 } else {
70 68 let data_path = index_path.with_extension("d");
@@ -73,31 +71,27 b' impl Revlog {'
73 71 Some(Box::new(data_mmap))
74 72 };
75 73
76 Ok(Revlog {
77 index_bytes,
78 data_bytes,
79 })
74 Ok(Revlog { index, data_bytes })
80 75 }
81 76
82 77 /// Return number of entries of the `Revlog`.
83 78 pub fn len(&self) -> usize {
84 self.index().len()
79 self.index.len()
85 80 }
86 81
87 82 /// Returns `true` if the `Revlog` has zero `entries`.
88 83 pub fn is_empty(&self) -> bool {
89 self.index().is_empty()
84 self.index.is_empty()
90 85 }
91 86
92 87 /// Return the full data associated to a node.
93 88 #[timed]
94 89 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
95 let index = self.index();
96 90 // This is brute force. But it is fast enough for now.
97 91 // Optimization will come later.
98 92 for rev in (0..self.len() as Revision).rev() {
99 93 let index_entry =
100 index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
94 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
101 95 if node == index_entry.hash() {
102 96 return Ok(rev);
103 97 }
@@ -123,9 +117,10 b' impl Revlog {'
123 117 }
124 118
125 119 // TODO do not look twice in the index
126 let index = self.index();
127 let index_entry =
128 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
120 let index_entry = self
121 .index
122 .get_entry(rev)
123 .ok_or(RevlogError::InvalidRevision)?;
129 124
130 125 let data: Vec<u8> = if delta_chain.is_empty() {
131 126 entry.data()?.into()
@@ -153,13 +148,12 b' impl Revlog {'
153 148 expected: &[u8],
154 149 data: &[u8],
155 150 ) -> bool {
156 let index = self.index();
157 let e1 = index.get_entry(p1);
151 let e1 = self.index.get_entry(p1);
158 152 let h1 = match e1 {
159 153 Some(ref entry) => entry.hash(),
160 154 None => &NULL_NODE_ID,
161 155 };
162 let e2 = index.get_entry(p2);
156 let e2 = self.index.get_entry(p2);
163 157 let h2 = match e2 {
164 158 Some(ref entry) => entry.hash(),
165 159 None => &NULL_NODE_ID,
@@ -187,30 +181,32 b' impl Revlog {'
187 181 Ok(patch.apply(&snapshot))
188 182 }
189 183
190 /// Return the revlog index.
191 pub fn index(&self) -> Index {
192 let is_inline = self.data_bytes.is_none();
193 Index::new(&self.index_bytes, is_inline)
194 }
195
196 184 /// Return the revlog data.
197 185 fn data(&self) -> &[u8] {
198 186 match self.data_bytes {
199 187 Some(ref data_bytes) => &data_bytes,
200 None => &self.index_bytes,
188 None => panic!(
189 "forgot to load the data or trying to access inline data"
190 ),
201 191 }
202 192 }
203 193
204 194 /// Get an entry of the revlog.
205 195 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
206 let index = self.index();
207 let index_entry =
208 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
196 let index_entry = self
197 .index
198 .get_entry(rev)
199 .ok_or(RevlogError::InvalidRevision)?;
209 200 let start = index_entry.offset();
210 201 let end = start + index_entry.compressed_len();
202 let data = if self.index.is_inline() {
203 self.index.data(start, end)
204 } else {
205 &self.data()[start..end]
206 };
211 207 let entry = RevlogEntry {
212 208 rev,
213 bytes: &self.data()[start..end],
209 bytes: data,
214 210 compressed_len: index_entry.compressed_len(),
215 211 uncompressed_len: index_entry.uncompressed_len(),
216 212 base_rev: if index_entry.base_revision() == rev {
@@ -296,14 +292,6 b" impl<'a> RevlogEntry<'a> {"
296 292 }
297 293 }
298 294
299 /// Value of the inline flag.
300 pub fn is_inline(index_bytes: &[u8]) -> bool {
301 match &index_bytes[0..=1] {
302 [0, 0] | [0, 2] => false,
303 _ => true,
304 }
305 }
306
307 295 /// Format version of the revlog.
308 296 pub fn get_version(index_bytes: &[u8]) -> u16 {
309 297 BigEndian::read_u16(&index_bytes[2..=3])
@@ -332,113 +320,12 b' mod tests {'
332 320
333 321 use super::super::index::IndexEntryBuilder;
334 322
335 #[cfg(test)]
336 pub struct RevlogBuilder {
337 version: u16,
338 is_general_delta: bool,
339 is_inline: bool,
340 offset: usize,
341 index: Vec<Vec<u8>>,
342 data: Vec<Vec<u8>>,
343 }
344
345 #[cfg(test)]
346 impl RevlogBuilder {
347 pub fn new() -> Self {
348 Self {
349 version: 2,
350 is_inline: false,
351 is_general_delta: true,
352 offset: 0,
353 index: vec![],
354 data: vec![],
355 }
356 }
357
358 pub fn with_inline(&mut self, value: bool) -> &mut Self {
359 self.is_inline = value;
360 self
361 }
362
363 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
364 self.is_general_delta = value;
365 self
366 }
367
368 pub fn with_version(&mut self, value: u16) -> &mut Self {
369 self.version = value;
370 self
371 }
372
373 pub fn push(
374 &mut self,
375 mut index: IndexEntryBuilder,
376 data: Vec<u8>,
377 ) -> &mut Self {
378 if self.index.is_empty() {
379 index.is_first(true);
380 index.with_general_delta(self.is_general_delta);
381 index.with_inline(self.is_inline);
382 index.with_version(self.version);
383 } else {
384 index.with_offset(self.offset);
385 }
386 self.index.push(index.build());
387 self.offset += data.len();
388 self.data.push(data);
389 self
390 }
391
392 pub fn build_inline(&self) -> Vec<u8> {
393 let mut bytes =
394 Vec::with_capacity(self.index.len() + self.data.len());
395 for (index, data) in self.index.iter().zip(self.data.iter()) {
396 bytes.extend(index);
397 bytes.extend(data);
398 }
399 bytes
400 }
401 }
402
403 #[test]
404 fn is_not_inline_when_no_inline_flag_test() {
405 let bytes = RevlogBuilder::new()
406 .with_general_delta(false)
407 .with_inline(false)
408 .push(IndexEntryBuilder::new(), vec![])
409 .build_inline();
410
411 assert_eq!(is_inline(&bytes), false)
412 }
413
414 #[test]
415 fn is_inline_when_inline_flag_test() {
416 let bytes = RevlogBuilder::new()
417 .with_general_delta(false)
418 .with_inline(true)
419 .push(IndexEntryBuilder::new(), vec![])
420 .build_inline();
421
422 assert_eq!(is_inline(&bytes), true)
423 }
424
425 #[test]
426 fn is_inline_when_inline_and_generaldelta_flags_test() {
427 let bytes = RevlogBuilder::new()
428 .with_general_delta(true)
429 .with_inline(true)
430 .push(IndexEntryBuilder::new(), vec![])
431 .build_inline();
432
433 assert_eq!(is_inline(&bytes), true)
434 }
435
436 323 #[test]
437 324 fn version_test() {
438 let bytes = RevlogBuilder::new()
325 let bytes = IndexEntryBuilder::new()
326 .is_first(true)
439 327 .with_version(1)
440 .push(IndexEntryBuilder::new(), vec![])
441 .build_inline();
328 .build();
442 329
443 330 assert_eq!(get_version(&bytes), 1)
444 331 }
General Comments 0
You need to be logged in to leave comments. Login now