##// END OF EJS Templates
rust-index: add missing special case for null rev...
Raphaël Gomès -
r52102:e9d47e2f default
parent child Browse files
Show More
@@ -1,938 +1,941 b''
1 use std::fmt::Debug;
1 use std::fmt::Debug;
2 use std::ops::Deref;
2 use std::ops::Deref;
3 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
3 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
4
4
5 use byteorder::{BigEndian, ByteOrder};
5 use byteorder::{BigEndian, ByteOrder};
6 use bytes_cast::{unaligned, BytesCast};
6 use bytes_cast::{unaligned, BytesCast};
7
7
8 use super::REVIDX_KNOWN_FLAGS;
8 use super::REVIDX_KNOWN_FLAGS;
9 use crate::errors::HgError;
9 use crate::errors::HgError;
10 use crate::node::{NODE_BYTES_LENGTH, STORED_NODE_ID_BYTES};
10 use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES};
11 use crate::revlog::node::Node;
11 use crate::revlog::node::Node;
12 use crate::revlog::{Revision, NULL_REVISION};
12 use crate::revlog::{Revision, NULL_REVISION};
13 use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision};
13 use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision};
14
14
15 pub const INDEX_ENTRY_SIZE: usize = 64;
15 pub const INDEX_ENTRY_SIZE: usize = 64;
16 pub const COMPRESSION_MODE_INLINE: u8 = 2;
16 pub const COMPRESSION_MODE_INLINE: u8 = 2;
17
17
18 pub struct IndexHeader {
18 pub struct IndexHeader {
19 pub(super) header_bytes: [u8; 4],
19 pub(super) header_bytes: [u8; 4],
20 }
20 }
21
21
22 #[derive(Copy, Clone)]
22 #[derive(Copy, Clone)]
23 pub struct IndexHeaderFlags {
23 pub struct IndexHeaderFlags {
24 flags: u16,
24 flags: u16,
25 }
25 }
26
26
27 /// Corresponds to the high bits of `_format_flags` in python
27 /// Corresponds to the high bits of `_format_flags` in python
28 impl IndexHeaderFlags {
28 impl IndexHeaderFlags {
29 /// Corresponds to FLAG_INLINE_DATA in python
29 /// Corresponds to FLAG_INLINE_DATA in python
30 pub fn is_inline(self) -> bool {
30 pub fn is_inline(self) -> bool {
31 self.flags & 1 != 0
31 self.flags & 1 != 0
32 }
32 }
33 /// Corresponds to FLAG_GENERALDELTA in python
33 /// Corresponds to FLAG_GENERALDELTA in python
34 pub fn uses_generaldelta(self) -> bool {
34 pub fn uses_generaldelta(self) -> bool {
35 self.flags & 2 != 0
35 self.flags & 2 != 0
36 }
36 }
37 }
37 }
38
38
39 /// Corresponds to the INDEX_HEADER structure,
39 /// Corresponds to the INDEX_HEADER structure,
40 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
40 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
41 impl IndexHeader {
41 impl IndexHeader {
42 fn format_flags(&self) -> IndexHeaderFlags {
42 fn format_flags(&self) -> IndexHeaderFlags {
43 // No "unknown flags" check here, unlike in python. Maybe there should
43 // No "unknown flags" check here, unlike in python. Maybe there should
44 // be.
44 // be.
45 IndexHeaderFlags {
45 IndexHeaderFlags {
46 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
46 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
47 }
47 }
48 }
48 }
49
49
50 /// The only revlog version currently supported by rhg.
50 /// The only revlog version currently supported by rhg.
51 const REVLOGV1: u16 = 1;
51 const REVLOGV1: u16 = 1;
52
52
53 /// Corresponds to `_format_version` in Python.
53 /// Corresponds to `_format_version` in Python.
54 fn format_version(&self) -> u16 {
54 fn format_version(&self) -> u16 {
55 BigEndian::read_u16(&self.header_bytes[2..4])
55 BigEndian::read_u16(&self.header_bytes[2..4])
56 }
56 }
57
57
58 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
58 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
59 if index_bytes.is_empty() {
59 if index_bytes.is_empty() {
60 return Ok(None);
60 return Ok(None);
61 }
61 }
62 if index_bytes.len() < 4 {
62 if index_bytes.len() < 4 {
63 return Err(HgError::corrupted(
63 return Err(HgError::corrupted(
64 "corrupted revlog: can't read the index format header",
64 "corrupted revlog: can't read the index format header",
65 ));
65 ));
66 }
66 }
67 Ok(Some(IndexHeader {
67 Ok(Some(IndexHeader {
68 header_bytes: {
68 header_bytes: {
69 let bytes: [u8; 4] =
69 let bytes: [u8; 4] =
70 index_bytes[0..4].try_into().expect("impossible");
70 index_bytes[0..4].try_into().expect("impossible");
71 bytes
71 bytes
72 },
72 },
73 }))
73 }))
74 }
74 }
75 }
75 }
76
76
77 /// Abstracts the access to the index bytes since they can be spread between
77 /// Abstracts the access to the index bytes since they can be spread between
78 /// the immutable (bytes) part and the mutable (added) part if any appends
78 /// the immutable (bytes) part and the mutable (added) part if any appends
79 /// happened. This makes it transparent for the callers.
79 /// happened. This makes it transparent for the callers.
80 struct IndexData {
80 struct IndexData {
81 /// Immutable bytes, most likely taken from disk
81 /// Immutable bytes, most likely taken from disk
82 bytes: Box<dyn Deref<Target = [u8]> + Send>,
82 bytes: Box<dyn Deref<Target = [u8]> + Send>,
83 /// Used when stripping index contents, keeps track of the start of the
83 /// Used when stripping index contents, keeps track of the start of the
84 /// first stripped revision, which is used to give a slice of the
84 /// first stripped revision, which is used to give a slice of the
85 /// `bytes` field.
85 /// `bytes` field.
86 truncation: Option<usize>,
86 truncation: Option<usize>,
87 /// Bytes that were added after reading the index
87 /// Bytes that were added after reading the index
88 added: Vec<u8>,
88 added: Vec<u8>,
89 }
89 }
90
90
91 impl IndexData {
91 impl IndexData {
92 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
92 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
93 Self {
93 Self {
94 bytes,
94 bytes,
95 truncation: None,
95 truncation: None,
96 added: vec![],
96 added: vec![],
97 }
97 }
98 }
98 }
99
99
100 pub fn len(&self) -> usize {
100 pub fn len(&self) -> usize {
101 match self.truncation {
101 match self.truncation {
102 Some(truncation) => truncation + self.added.len(),
102 Some(truncation) => truncation + self.added.len(),
103 None => self.bytes.len() + self.added.len(),
103 None => self.bytes.len() + self.added.len(),
104 }
104 }
105 }
105 }
106
106
107 fn remove(
107 fn remove(
108 &mut self,
108 &mut self,
109 rev: Revision,
109 rev: Revision,
110 offsets: Option<&[usize]>,
110 offsets: Option<&[usize]>,
111 ) -> Result<(), RevlogError> {
111 ) -> Result<(), RevlogError> {
112 let rev = rev.0 as usize;
112 let rev = rev.0 as usize;
113 let truncation = if let Some(offsets) = offsets {
113 let truncation = if let Some(offsets) = offsets {
114 offsets[rev]
114 offsets[rev]
115 } else {
115 } else {
116 rev * INDEX_ENTRY_SIZE
116 rev * INDEX_ENTRY_SIZE
117 };
117 };
118 if truncation < self.bytes.len() {
118 if truncation < self.bytes.len() {
119 self.truncation = Some(truncation);
119 self.truncation = Some(truncation);
120 self.added.clear();
120 self.added.clear();
121 } else {
121 } else {
122 self.added.truncate(truncation - self.bytes.len());
122 self.added.truncate(truncation - self.bytes.len());
123 }
123 }
124 Ok(())
124 Ok(())
125 }
125 }
126
126
127 fn is_new(&self) -> bool {
127 fn is_new(&self) -> bool {
128 self.bytes.is_empty()
128 self.bytes.is_empty()
129 }
129 }
130 }
130 }
131
131
132 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
132 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
133 type Output = [u8];
133 type Output = [u8];
134
134
135 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
135 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
136 let start = index.start;
136 let start = index.start;
137 let end = index.end;
137 let end = index.end;
138 let immutable_len = match self.truncation {
138 let immutable_len = match self.truncation {
139 Some(truncation) => truncation,
139 Some(truncation) => truncation,
140 None => self.bytes.len(),
140 None => self.bytes.len(),
141 };
141 };
142 if start < immutable_len {
142 if start < immutable_len {
143 if end > immutable_len {
143 if end > immutable_len {
144 panic!("index data cannot span existing and added ranges");
144 panic!("index data cannot span existing and added ranges");
145 }
145 }
146 &self.bytes[index]
146 &self.bytes[index]
147 } else {
147 } else {
148 &self.added[start - immutable_len..end - immutable_len]
148 &self.added[start - immutable_len..end - immutable_len]
149 }
149 }
150 }
150 }
151 }
151 }
152
152
153 #[derive(Debug, PartialEq, Eq)]
153 #[derive(Debug, PartialEq, Eq)]
154 pub struct RevisionDataParams {
154 pub struct RevisionDataParams {
155 pub flags: u16,
155 pub flags: u16,
156 pub data_offset: u64,
156 pub data_offset: u64,
157 pub data_compressed_length: i32,
157 pub data_compressed_length: i32,
158 pub data_uncompressed_length: i32,
158 pub data_uncompressed_length: i32,
159 pub data_delta_base: i32,
159 pub data_delta_base: i32,
160 pub link_rev: i32,
160 pub link_rev: i32,
161 pub parent_rev_1: i32,
161 pub parent_rev_1: i32,
162 pub parent_rev_2: i32,
162 pub parent_rev_2: i32,
163 pub node_id: [u8; NODE_BYTES_LENGTH],
163 pub node_id: [u8; NODE_BYTES_LENGTH],
164 pub _sidedata_offset: u64,
164 pub _sidedata_offset: u64,
165 pub _sidedata_compressed_length: i32,
165 pub _sidedata_compressed_length: i32,
166 pub data_compression_mode: u8,
166 pub data_compression_mode: u8,
167 pub _sidedata_compression_mode: u8,
167 pub _sidedata_compression_mode: u8,
168 pub _rank: i32,
168 pub _rank: i32,
169 }
169 }
170
170
171 impl Default for RevisionDataParams {
171 impl Default for RevisionDataParams {
172 fn default() -> Self {
172 fn default() -> Self {
173 Self {
173 Self {
174 flags: 0,
174 flags: 0,
175 data_offset: 0,
175 data_offset: 0,
176 data_compressed_length: 0,
176 data_compressed_length: 0,
177 data_uncompressed_length: 0,
177 data_uncompressed_length: 0,
178 data_delta_base: -1,
178 data_delta_base: -1,
179 link_rev: -1,
179 link_rev: -1,
180 parent_rev_1: -1,
180 parent_rev_1: -1,
181 parent_rev_2: -1,
181 parent_rev_2: -1,
182 node_id: [0; NODE_BYTES_LENGTH],
182 node_id: [0; NODE_BYTES_LENGTH],
183 _sidedata_offset: 0,
183 _sidedata_offset: 0,
184 _sidedata_compressed_length: 0,
184 _sidedata_compressed_length: 0,
185 data_compression_mode: COMPRESSION_MODE_INLINE,
185 data_compression_mode: COMPRESSION_MODE_INLINE,
186 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
186 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
187 _rank: -1,
187 _rank: -1,
188 }
188 }
189 }
189 }
190 }
190 }
191
191
192 #[derive(BytesCast)]
192 #[derive(BytesCast)]
193 #[repr(C)]
193 #[repr(C)]
194 pub struct RevisionDataV1 {
194 pub struct RevisionDataV1 {
195 data_offset_or_flags: unaligned::U64Be,
195 data_offset_or_flags: unaligned::U64Be,
196 data_compressed_length: unaligned::I32Be,
196 data_compressed_length: unaligned::I32Be,
197 data_uncompressed_length: unaligned::I32Be,
197 data_uncompressed_length: unaligned::I32Be,
198 data_delta_base: unaligned::I32Be,
198 data_delta_base: unaligned::I32Be,
199 link_rev: unaligned::I32Be,
199 link_rev: unaligned::I32Be,
200 parent_rev_1: unaligned::I32Be,
200 parent_rev_1: unaligned::I32Be,
201 parent_rev_2: unaligned::I32Be,
201 parent_rev_2: unaligned::I32Be,
202 node_id: [u8; STORED_NODE_ID_BYTES],
202 node_id: [u8; STORED_NODE_ID_BYTES],
203 }
203 }
204
204
205 fn _static_assert_size_of_revision_data_v1() {
205 fn _static_assert_size_of_revision_data_v1() {
206 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
206 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
207 }
207 }
208
208
209 impl RevisionDataParams {
209 impl RevisionDataParams {
210 pub fn validate(&self) -> Result<(), RevlogError> {
210 pub fn validate(&self) -> Result<(), RevlogError> {
211 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
211 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
212 return Err(RevlogError::corrupted(format!(
212 return Err(RevlogError::corrupted(format!(
213 "unknown revlog index flags: {}",
213 "unknown revlog index flags: {}",
214 self.flags
214 self.flags
215 )));
215 )));
216 }
216 }
217 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
217 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
218 return Err(RevlogError::corrupted(format!(
218 return Err(RevlogError::corrupted(format!(
219 "invalid data compression mode: {}",
219 "invalid data compression mode: {}",
220 self.data_compression_mode
220 self.data_compression_mode
221 )));
221 )));
222 }
222 }
223 // FIXME isn't this only for v2 or changelog v2?
223 // FIXME isn't this only for v2 or changelog v2?
224 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
224 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
225 return Err(RevlogError::corrupted(format!(
225 return Err(RevlogError::corrupted(format!(
226 "invalid sidedata compression mode: {}",
226 "invalid sidedata compression mode: {}",
227 self._sidedata_compression_mode
227 self._sidedata_compression_mode
228 )));
228 )));
229 }
229 }
230 Ok(())
230 Ok(())
231 }
231 }
232
232
233 pub fn into_v1(self) -> RevisionDataV1 {
233 pub fn into_v1(self) -> RevisionDataV1 {
234 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
234 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
235 let mut node_id = [0; STORED_NODE_ID_BYTES];
235 let mut node_id = [0; STORED_NODE_ID_BYTES];
236 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
236 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
237 RevisionDataV1 {
237 RevisionDataV1 {
238 data_offset_or_flags: data_offset_or_flags.into(),
238 data_offset_or_flags: data_offset_or_flags.into(),
239 data_compressed_length: self.data_compressed_length.into(),
239 data_compressed_length: self.data_compressed_length.into(),
240 data_uncompressed_length: self.data_uncompressed_length.into(),
240 data_uncompressed_length: self.data_uncompressed_length.into(),
241 data_delta_base: self.data_delta_base.into(),
241 data_delta_base: self.data_delta_base.into(),
242 link_rev: self.link_rev.into(),
242 link_rev: self.link_rev.into(),
243 parent_rev_1: self.parent_rev_1.into(),
243 parent_rev_1: self.parent_rev_1.into(),
244 parent_rev_2: self.parent_rev_2.into(),
244 parent_rev_2: self.parent_rev_2.into(),
245 node_id,
245 node_id,
246 }
246 }
247 }
247 }
248 }
248 }
249
249
250 /// A Revlog index
250 /// A Revlog index
251 pub struct Index {
251 pub struct Index {
252 bytes: IndexData,
252 bytes: IndexData,
253 /// Offsets of starts of index blocks.
253 /// Offsets of starts of index blocks.
254 /// Only needed when the index is interleaved with data.
254 /// Only needed when the index is interleaved with data.
255 offsets: RwLock<Option<Vec<usize>>>,
255 offsets: RwLock<Option<Vec<usize>>>,
256 uses_generaldelta: bool,
256 uses_generaldelta: bool,
257 is_inline: bool,
257 is_inline: bool,
258 }
258 }
259
259
260 impl Debug for Index {
260 impl Debug for Index {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 f.debug_struct("Index")
262 f.debug_struct("Index")
263 .field("offsets", &self.offsets)
263 .field("offsets", &self.offsets)
264 .field("uses_generaldelta", &self.uses_generaldelta)
264 .field("uses_generaldelta", &self.uses_generaldelta)
265 .finish()
265 .finish()
266 }
266 }
267 }
267 }
268
268
269 impl Graph for Index {
269 impl Graph for Index {
270 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
270 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
271 let err = || GraphError::ParentOutOfRange(rev);
271 let err = || GraphError::ParentOutOfRange(rev);
272 match self.get_entry(rev) {
272 match self.get_entry(rev) {
273 Some(entry) => {
273 Some(entry) => {
274 // The C implementation checks that the parents are valid
274 // The C implementation checks that the parents are valid
275 // before returning
275 // before returning
276 Ok([
276 Ok([
277 self.check_revision(entry.p1()).ok_or_else(err)?,
277 self.check_revision(entry.p1()).ok_or_else(err)?,
278 self.check_revision(entry.p2()).ok_or_else(err)?,
278 self.check_revision(entry.p2()).ok_or_else(err)?,
279 ])
279 ])
280 }
280 }
281 None => Ok([NULL_REVISION, NULL_REVISION]),
281 None => Ok([NULL_REVISION, NULL_REVISION]),
282 }
282 }
283 }
283 }
284 }
284 }
285
285
286 impl Index {
286 impl Index {
287 /// Create an index from bytes.
287 /// Create an index from bytes.
288 /// Calculate the start of each entry when is_inline is true.
288 /// Calculate the start of each entry when is_inline is true.
289 pub fn new(
289 pub fn new(
290 bytes: Box<dyn Deref<Target = [u8]> + Send>,
290 bytes: Box<dyn Deref<Target = [u8]> + Send>,
291 default_header: IndexHeader,
291 default_header: IndexHeader,
292 ) -> Result<Self, HgError> {
292 ) -> Result<Self, HgError> {
293 let header =
293 let header =
294 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
294 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
295
295
296 if header.format_version() != IndexHeader::REVLOGV1 {
296 if header.format_version() != IndexHeader::REVLOGV1 {
297 // A proper new version should have had a repo/store
297 // A proper new version should have had a repo/store
298 // requirement.
298 // requirement.
299 return Err(HgError::corrupted("unsupported revlog version"));
299 return Err(HgError::corrupted("unsupported revlog version"));
300 }
300 }
301
301
302 // This is only correct because we know version is REVLOGV1.
302 // This is only correct because we know version is REVLOGV1.
303 // In v2 we always use generaldelta, while in v0 we never use
303 // In v2 we always use generaldelta, while in v0 we never use
304 // generaldelta. Similar for [is_inline] (it's only used in v1).
304 // generaldelta. Similar for [is_inline] (it's only used in v1).
305 let uses_generaldelta = header.format_flags().uses_generaldelta();
305 let uses_generaldelta = header.format_flags().uses_generaldelta();
306
306
307 if header.format_flags().is_inline() {
307 if header.format_flags().is_inline() {
308 let mut offset: usize = 0;
308 let mut offset: usize = 0;
309 let mut offsets = Vec::new();
309 let mut offsets = Vec::new();
310
310
311 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
311 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
312 offsets.push(offset);
312 offsets.push(offset);
313 let end = offset + INDEX_ENTRY_SIZE;
313 let end = offset + INDEX_ENTRY_SIZE;
314 let entry = IndexEntry {
314 let entry = IndexEntry {
315 bytes: &bytes[offset..end],
315 bytes: &bytes[offset..end],
316 offset_override: None,
316 offset_override: None,
317 };
317 };
318
318
319 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
319 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
320 }
320 }
321
321
322 if offset == bytes.len() {
322 if offset == bytes.len() {
323 Ok(Self {
323 Ok(Self {
324 bytes: IndexData::new(bytes),
324 bytes: IndexData::new(bytes),
325 offsets: RwLock::new(Some(offsets)),
325 offsets: RwLock::new(Some(offsets)),
326 uses_generaldelta,
326 uses_generaldelta,
327 is_inline: true,
327 is_inline: true,
328 })
328 })
329 } else {
329 } else {
330 Err(HgError::corrupted("unexpected inline revlog length"))
330 Err(HgError::corrupted("unexpected inline revlog length"))
331 }
331 }
332 } else {
332 } else {
333 Ok(Self {
333 Ok(Self {
334 bytes: IndexData::new(bytes),
334 bytes: IndexData::new(bytes),
335 offsets: RwLock::new(None),
335 offsets: RwLock::new(None),
336 uses_generaldelta,
336 uses_generaldelta,
337 is_inline: false,
337 is_inline: false,
338 })
338 })
339 }
339 }
340 }
340 }
341
341
342 pub fn uses_generaldelta(&self) -> bool {
342 pub fn uses_generaldelta(&self) -> bool {
343 self.uses_generaldelta
343 self.uses_generaldelta
344 }
344 }
345
345
346 /// Value of the inline flag.
346 /// Value of the inline flag.
347 pub fn is_inline(&self) -> bool {
347 pub fn is_inline(&self) -> bool {
348 self.is_inline
348 self.is_inline
349 }
349 }
350
350
351 /// Return a slice of bytes if `revlog` is inline. Panic if not.
351 /// Return a slice of bytes if `revlog` is inline. Panic if not.
352 pub fn data(&self, start: usize, end: usize) -> &[u8] {
352 pub fn data(&self, start: usize, end: usize) -> &[u8] {
353 if !self.is_inline() {
353 if !self.is_inline() {
354 panic!("tried to access data in the index of a revlog that is not inline");
354 panic!("tried to access data in the index of a revlog that is not inline");
355 }
355 }
356 &self.bytes[start..end]
356 &self.bytes[start..end]
357 }
357 }
358
358
359 /// Return number of entries of the revlog index.
359 /// Return number of entries of the revlog index.
360 pub fn len(&self) -> usize {
360 pub fn len(&self) -> usize {
361 if let Some(offsets) = &*self.get_offsets() {
361 if let Some(offsets) = &*self.get_offsets() {
362 offsets.len()
362 offsets.len()
363 } else {
363 } else {
364 self.bytes.len() / INDEX_ENTRY_SIZE
364 self.bytes.len() / INDEX_ENTRY_SIZE
365 }
365 }
366 }
366 }
367
367
368 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
368 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
369 if self.is_inline() {
369 if self.is_inline() {
370 {
370 {
371 // Wrap in a block to drop the read guard
371 // Wrap in a block to drop the read guard
372 // TODO perf?
372 // TODO perf?
373 let mut offsets = self.offsets.write().unwrap();
373 let mut offsets = self.offsets.write().unwrap();
374 if offsets.is_none() {
374 if offsets.is_none() {
375 offsets.replace(inline_scan(&self.bytes.bytes).1);
375 offsets.replace(inline_scan(&self.bytes.bytes).1);
376 }
376 }
377 }
377 }
378 }
378 }
379 self.offsets.read().unwrap()
379 self.offsets.read().unwrap()
380 }
380 }
381
381
382 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
382 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
383 let mut offsets = self.offsets.write().unwrap();
383 let mut offsets = self.offsets.write().unwrap();
384 if self.is_inline() && offsets.is_none() {
384 if self.is_inline() && offsets.is_none() {
385 offsets.replace(inline_scan(&self.bytes.bytes).1);
385 offsets.replace(inline_scan(&self.bytes.bytes).1);
386 }
386 }
387 offsets
387 offsets
388 }
388 }
389
389
390 /// Returns `true` if the `Index` has zero `entries`.
390 /// Returns `true` if the `Index` has zero `entries`.
391 pub fn is_empty(&self) -> bool {
391 pub fn is_empty(&self) -> bool {
392 self.len() == 0
392 self.len() == 0
393 }
393 }
394
394
395 /// Return the index entry corresponding to the given revision or `None`
395 /// Return the index entry corresponding to the given revision or `None`
396 /// for [`NULL_REVISION`]
396 /// for [`NULL_REVISION`]
397 ///
397 ///
398 /// The specified revision being of the checked type, it always exists
398 /// The specified revision being of the checked type, it always exists
399 /// if it was validated by this index.
399 /// if it was validated by this index.
400 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
400 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
401 if rev == NULL_REVISION {
401 if rev == NULL_REVISION {
402 return None;
402 return None;
403 }
403 }
404 Some(if let Some(offsets) = &*self.get_offsets() {
404 Some(if let Some(offsets) = &*self.get_offsets() {
405 self.get_entry_inline(rev, offsets.as_ref())
405 self.get_entry_inline(rev, offsets.as_ref())
406 } else {
406 } else {
407 self.get_entry_separated(rev)
407 self.get_entry_separated(rev)
408 })
408 })
409 }
409 }
410
410
411 /// Return the binary content of the index entry for the given revision
411 /// Return the binary content of the index entry for the given revision
412 ///
412 ///
413 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
413 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
414 /// returned.
414 /// returned.
415 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
415 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
416 self.get_entry(rev).map(|e| {
416 self.get_entry(rev).map(|e| {
417 let bytes = e.as_bytes();
417 let bytes = e.as_bytes();
418 if rev.0 == 0 {
418 if rev.0 == 0 {
419 &bytes[4..]
419 &bytes[4..]
420 } else {
420 } else {
421 bytes
421 bytes
422 }
422 }
423 })
423 })
424 }
424 }
425
425
426 pub fn entry_as_params(
426 pub fn entry_as_params(
427 &self,
427 &self,
428 rev: UncheckedRevision,
428 rev: UncheckedRevision,
429 ) -> Option<RevisionDataParams> {
429 ) -> Option<RevisionDataParams> {
430 let rev = self.check_revision(rev)?;
430 let rev = self.check_revision(rev)?;
431 self.get_entry(rev).map(|e| RevisionDataParams {
431 self.get_entry(rev).map(|e| RevisionDataParams {
432 flags: e.flags(),
432 flags: e.flags(),
433 data_offset: if rev.0 == 0 && !self.bytes.is_new() {
433 data_offset: if rev.0 == 0 && !self.bytes.is_new() {
434 e.flags() as u64
434 e.flags() as u64
435 } else {
435 } else {
436 e.raw_offset()
436 e.raw_offset()
437 },
437 },
438 data_compressed_length: e.compressed_len().try_into().unwrap(),
438 data_compressed_length: e.compressed_len().try_into().unwrap(),
439 data_uncompressed_length: e.uncompressed_len(),
439 data_uncompressed_length: e.uncompressed_len(),
440 data_delta_base: e.base_revision_or_base_of_delta_chain().0,
440 data_delta_base: e.base_revision_or_base_of_delta_chain().0,
441 link_rev: e.link_revision().0,
441 link_rev: e.link_revision().0,
442 parent_rev_1: e.p1().0,
442 parent_rev_1: e.p1().0,
443 parent_rev_2: e.p2().0,
443 parent_rev_2: e.p2().0,
444 node_id: e.hash().as_bytes().try_into().unwrap(),
444 node_id: e.hash().as_bytes().try_into().unwrap(),
445 ..Default::default()
445 ..Default::default()
446 })
446 })
447 }
447 }
448
448
449 fn get_entry_inline(
449 fn get_entry_inline(
450 &self,
450 &self,
451 rev: Revision,
451 rev: Revision,
452 offsets: &[usize],
452 offsets: &[usize],
453 ) -> IndexEntry {
453 ) -> IndexEntry {
454 let start = offsets[rev.0 as usize];
454 let start = offsets[rev.0 as usize];
455 let end = start + INDEX_ENTRY_SIZE;
455 let end = start + INDEX_ENTRY_SIZE;
456 let bytes = &self.bytes[start..end];
456 let bytes = &self.bytes[start..end];
457
457
458 // See IndexEntry for an explanation of this override.
458 // See IndexEntry for an explanation of this override.
459 let offset_override = Some(end);
459 let offset_override = Some(end);
460
460
461 IndexEntry {
461 IndexEntry {
462 bytes,
462 bytes,
463 offset_override,
463 offset_override,
464 }
464 }
465 }
465 }
466
466
467 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
467 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
468 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
468 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
469 let end = start + INDEX_ENTRY_SIZE;
469 let end = start + INDEX_ENTRY_SIZE;
470 let bytes = &self.bytes[start..end];
470 let bytes = &self.bytes[start..end];
471
471
472 // Override the offset of the first revision as its bytes are used
472 // Override the offset of the first revision as its bytes are used
473 // for the index's metadata (saving space because it is always 0)
473 // for the index's metadata (saving space because it is always 0)
474 let offset_override = if rev == Revision(0) { Some(0) } else { None };
474 let offset_override = if rev == Revision(0) { Some(0) } else { None };
475
475
476 IndexEntry {
476 IndexEntry {
477 bytes,
477 bytes,
478 offset_override,
478 offset_override,
479 }
479 }
480 }
480 }
481
481
482 /// TODO move this to the trait probably, along with other things
482 /// TODO move this to the trait probably, along with other things
483 pub fn append(
483 pub fn append(
484 &mut self,
484 &mut self,
485 revision_data: RevisionDataParams,
485 revision_data: RevisionDataParams,
486 ) -> Result<(), RevlogError> {
486 ) -> Result<(), RevlogError> {
487 revision_data.validate()?;
487 revision_data.validate()?;
488 let new_offset = self.bytes.len();
488 let new_offset = self.bytes.len();
489 if let Some(offsets) = &mut *self.get_offsets_mut() {
489 if let Some(offsets) = &mut *self.get_offsets_mut() {
490 offsets.push(new_offset)
490 offsets.push(new_offset)
491 }
491 }
492 self.bytes.added.extend(revision_data.into_v1().as_bytes());
492 self.bytes.added.extend(revision_data.into_v1().as_bytes());
493 Ok(())
493 Ok(())
494 }
494 }
495
495
496 pub fn pack_header(&self, header: i32) -> [u8; 4] {
496 pub fn pack_header(&self, header: i32) -> [u8; 4] {
497 header.to_be_bytes()
497 header.to_be_bytes()
498 }
498 }
499
499
500 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
500 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
501 let offsets = self.get_offsets().clone();
501 let offsets = self.get_offsets().clone();
502 self.bytes.remove(rev, offsets.as_deref())?;
502 self.bytes.remove(rev, offsets.as_deref())?;
503 if let Some(offsets) = &mut *self.get_offsets_mut() {
503 if let Some(offsets) = &mut *self.get_offsets_mut() {
504 offsets.truncate(rev.0 as usize)
504 offsets.truncate(rev.0 as usize)
505 }
505 }
506 Ok(())
506 Ok(())
507 }
507 }
508
508
509 pub fn clear_caches(&mut self) {
509 pub fn clear_caches(&mut self) {
510 // We need to get the 'inline' value from Python at init and use this
510 // We need to get the 'inline' value from Python at init and use this
511 // instead of offsets to determine whether we're inline since we might
511 // instead of offsets to determine whether we're inline since we might
512 // clear caches. This implies re-populating the offsets on-demand.
512 // clear caches. This implies re-populating the offsets on-demand.
513 self.offsets = RwLock::new(None);
513 self.offsets = RwLock::new(None);
514 }
514 }
515 }
515 }
516
516
517 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
517 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
518 let mut offset: usize = 0;
518 let mut offset: usize = 0;
519 let mut offsets = Vec::new();
519 let mut offsets = Vec::new();
520
520
521 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
521 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
522 offsets.push(offset);
522 offsets.push(offset);
523 let end = offset + INDEX_ENTRY_SIZE;
523 let end = offset + INDEX_ENTRY_SIZE;
524 let entry = IndexEntry {
524 let entry = IndexEntry {
525 bytes: &bytes[offset..end],
525 bytes: &bytes[offset..end],
526 offset_override: None,
526 offset_override: None,
527 };
527 };
528
528
529 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
529 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
530 }
530 }
531 (offset, offsets)
531 (offset, offsets)
532 }
532 }
533
533
534 impl super::RevlogIndex for Index {
534 impl super::RevlogIndex for Index {
535 fn len(&self) -> usize {
535 fn len(&self) -> usize {
536 self.len()
536 self.len()
537 }
537 }
538
538
539 fn node(&self, rev: Revision) -> Option<&Node> {
539 fn node(&self, rev: Revision) -> Option<&Node> {
540 if rev == NULL_REVISION {
541 return Some(&NULL_NODE);
542 }
540 self.get_entry(rev).map(|entry| entry.hash())
543 self.get_entry(rev).map(|entry| entry.hash())
541 }
544 }
542 }
545 }
543
546
544 #[derive(Debug)]
547 #[derive(Debug)]
545 pub struct IndexEntry<'a> {
548 pub struct IndexEntry<'a> {
546 bytes: &'a [u8],
549 bytes: &'a [u8],
547 /// Allows to override the offset value of the entry.
550 /// Allows to override the offset value of the entry.
548 ///
551 ///
549 /// For interleaved index and data, the offset stored in the index
552 /// For interleaved index and data, the offset stored in the index
550 /// corresponds to the separated data offset.
553 /// corresponds to the separated data offset.
551 /// It has to be overridden with the actual offset in the interleaved
554 /// It has to be overridden with the actual offset in the interleaved
552 /// index which is just after the index block.
555 /// index which is just after the index block.
553 ///
556 ///
554 /// For separated index and data, the offset stored in the first index
557 /// For separated index and data, the offset stored in the first index
555 /// entry is mixed with the index headers.
558 /// entry is mixed with the index headers.
556 /// It has to be overridden with 0.
559 /// It has to be overridden with 0.
557 offset_override: Option<usize>,
560 offset_override: Option<usize>,
558 }
561 }
559
562
560 impl<'a> IndexEntry<'a> {
563 impl<'a> IndexEntry<'a> {
561 /// Return the offset of the data.
564 /// Return the offset of the data.
562 pub fn offset(&self) -> usize {
565 pub fn offset(&self) -> usize {
563 if let Some(offset_override) = self.offset_override {
566 if let Some(offset_override) = self.offset_override {
564 offset_override
567 offset_override
565 } else {
568 } else {
566 let mut bytes = [0; 8];
569 let mut bytes = [0; 8];
567 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
570 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
568 BigEndian::read_u64(&bytes[..]) as usize
571 BigEndian::read_u64(&bytes[..]) as usize
569 }
572 }
570 }
573 }
571 pub fn raw_offset(&self) -> u64 {
574 pub fn raw_offset(&self) -> u64 {
572 BigEndian::read_u64(&self.bytes[0..8])
575 BigEndian::read_u64(&self.bytes[0..8])
573 }
576 }
574
577
575 pub fn flags(&self) -> u16 {
578 pub fn flags(&self) -> u16 {
576 BigEndian::read_u16(&self.bytes[6..=7])
579 BigEndian::read_u16(&self.bytes[6..=7])
577 }
580 }
578
581
579 /// Return the compressed length of the data.
582 /// Return the compressed length of the data.
580 pub fn compressed_len(&self) -> u32 {
583 pub fn compressed_len(&self) -> u32 {
581 BigEndian::read_u32(&self.bytes[8..=11])
584 BigEndian::read_u32(&self.bytes[8..=11])
582 }
585 }
583
586
584 /// Return the uncompressed length of the data.
587 /// Return the uncompressed length of the data.
585 pub fn uncompressed_len(&self) -> i32 {
588 pub fn uncompressed_len(&self) -> i32 {
586 BigEndian::read_i32(&self.bytes[12..=15])
589 BigEndian::read_i32(&self.bytes[12..=15])
587 }
590 }
588
591
589 /// Return the revision upon which the data has been derived.
592 /// Return the revision upon which the data has been derived.
590 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
593 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
591 // TODO Maybe return an Option when base_revision == rev?
594 // TODO Maybe return an Option when base_revision == rev?
592 // Requires to add rev to IndexEntry
595 // Requires to add rev to IndexEntry
593
596
594 BigEndian::read_i32(&self.bytes[16..]).into()
597 BigEndian::read_i32(&self.bytes[16..]).into()
595 }
598 }
596
599
597 pub fn link_revision(&self) -> UncheckedRevision {
600 pub fn link_revision(&self) -> UncheckedRevision {
598 BigEndian::read_i32(&self.bytes[20..]).into()
601 BigEndian::read_i32(&self.bytes[20..]).into()
599 }
602 }
600
603
601 pub fn p1(&self) -> UncheckedRevision {
604 pub fn p1(&self) -> UncheckedRevision {
602 BigEndian::read_i32(&self.bytes[24..]).into()
605 BigEndian::read_i32(&self.bytes[24..]).into()
603 }
606 }
604
607
605 pub fn p2(&self) -> UncheckedRevision {
608 pub fn p2(&self) -> UncheckedRevision {
606 BigEndian::read_i32(&self.bytes[28..]).into()
609 BigEndian::read_i32(&self.bytes[28..]).into()
607 }
610 }
608
611
609 /// Return the hash of revision's full text.
612 /// Return the hash of revision's full text.
610 ///
613 ///
611 /// Currently, SHA-1 is used and only the first 20 bytes of this field
614 /// Currently, SHA-1 is used and only the first 20 bytes of this field
612 /// are used.
615 /// are used.
613 pub fn hash(&self) -> &'a Node {
616 pub fn hash(&self) -> &'a Node {
614 (&self.bytes[32..52]).try_into().unwrap()
617 (&self.bytes[32..52]).try_into().unwrap()
615 }
618 }
616
619
617 pub fn as_bytes(&self) -> &'a [u8] {
620 pub fn as_bytes(&self) -> &'a [u8] {
618 self.bytes
621 self.bytes
619 }
622 }
620 }
623 }
621
624
622 #[cfg(test)]
625 #[cfg(test)]
623 mod tests {
626 mod tests {
624 use super::*;
627 use super::*;
625 use crate::node::NULL_NODE;
628 use crate::node::NULL_NODE;
626
629
627 #[cfg(test)]
630 #[cfg(test)]
628 #[derive(Debug, Copy, Clone)]
631 #[derive(Debug, Copy, Clone)]
629 pub struct IndexEntryBuilder {
632 pub struct IndexEntryBuilder {
630 is_first: bool,
633 is_first: bool,
631 is_inline: bool,
634 is_inline: bool,
632 is_general_delta: bool,
635 is_general_delta: bool,
633 version: u16,
636 version: u16,
634 offset: usize,
637 offset: usize,
635 compressed_len: usize,
638 compressed_len: usize,
636 uncompressed_len: usize,
639 uncompressed_len: usize,
637 base_revision_or_base_of_delta_chain: Revision,
640 base_revision_or_base_of_delta_chain: Revision,
638 link_revision: Revision,
641 link_revision: Revision,
639 p1: Revision,
642 p1: Revision,
640 p2: Revision,
643 p2: Revision,
641 node: Node,
644 node: Node,
642 }
645 }
643
646
644 #[cfg(test)]
647 #[cfg(test)]
645 impl IndexEntryBuilder {
648 impl IndexEntryBuilder {
646 #[allow(clippy::new_without_default)]
649 #[allow(clippy::new_without_default)]
647 pub fn new() -> Self {
650 pub fn new() -> Self {
648 Self {
651 Self {
649 is_first: false,
652 is_first: false,
650 is_inline: false,
653 is_inline: false,
651 is_general_delta: true,
654 is_general_delta: true,
652 version: 1,
655 version: 1,
653 offset: 0,
656 offset: 0,
654 compressed_len: 0,
657 compressed_len: 0,
655 uncompressed_len: 0,
658 uncompressed_len: 0,
656 base_revision_or_base_of_delta_chain: Revision(0),
659 base_revision_or_base_of_delta_chain: Revision(0),
657 link_revision: Revision(0),
660 link_revision: Revision(0),
658 p1: NULL_REVISION,
661 p1: NULL_REVISION,
659 p2: NULL_REVISION,
662 p2: NULL_REVISION,
660 node: NULL_NODE,
663 node: NULL_NODE,
661 }
664 }
662 }
665 }
663
666
664 pub fn is_first(&mut self, value: bool) -> &mut Self {
667 pub fn is_first(&mut self, value: bool) -> &mut Self {
665 self.is_first = value;
668 self.is_first = value;
666 self
669 self
667 }
670 }
668
671
669 pub fn with_inline(&mut self, value: bool) -> &mut Self {
672 pub fn with_inline(&mut self, value: bool) -> &mut Self {
670 self.is_inline = value;
673 self.is_inline = value;
671 self
674 self
672 }
675 }
673
676
674 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
677 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
675 self.is_general_delta = value;
678 self.is_general_delta = value;
676 self
679 self
677 }
680 }
678
681
679 pub fn with_version(&mut self, value: u16) -> &mut Self {
682 pub fn with_version(&mut self, value: u16) -> &mut Self {
680 self.version = value;
683 self.version = value;
681 self
684 self
682 }
685 }
683
686
684 pub fn with_offset(&mut self, value: usize) -> &mut Self {
687 pub fn with_offset(&mut self, value: usize) -> &mut Self {
685 self.offset = value;
688 self.offset = value;
686 self
689 self
687 }
690 }
688
691
689 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
692 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
690 self.compressed_len = value;
693 self.compressed_len = value;
691 self
694 self
692 }
695 }
693
696
694 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
697 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
695 self.uncompressed_len = value;
698 self.uncompressed_len = value;
696 self
699 self
697 }
700 }
698
701
699 pub fn with_base_revision_or_base_of_delta_chain(
702 pub fn with_base_revision_or_base_of_delta_chain(
700 &mut self,
703 &mut self,
701 value: Revision,
704 value: Revision,
702 ) -> &mut Self {
705 ) -> &mut Self {
703 self.base_revision_or_base_of_delta_chain = value;
706 self.base_revision_or_base_of_delta_chain = value;
704 self
707 self
705 }
708 }
706
709
707 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
710 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
708 self.link_revision = value;
711 self.link_revision = value;
709 self
712 self
710 }
713 }
711
714
712 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
715 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
713 self.p1 = value;
716 self.p1 = value;
714 self
717 self
715 }
718 }
716
719
717 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
720 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
718 self.p2 = value;
721 self.p2 = value;
719 self
722 self
720 }
723 }
721
724
722 pub fn with_node(&mut self, value: Node) -> &mut Self {
725 pub fn with_node(&mut self, value: Node) -> &mut Self {
723 self.node = value;
726 self.node = value;
724 self
727 self
725 }
728 }
726
729
727 pub fn build(&self) -> Vec<u8> {
730 pub fn build(&self) -> Vec<u8> {
728 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
731 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
729 if self.is_first {
732 if self.is_first {
730 bytes.extend(&match (self.is_general_delta, self.is_inline) {
733 bytes.extend(&match (self.is_general_delta, self.is_inline) {
731 (false, false) => [0u8, 0],
734 (false, false) => [0u8, 0],
732 (false, true) => [0u8, 1],
735 (false, true) => [0u8, 1],
733 (true, false) => [0u8, 2],
736 (true, false) => [0u8, 2],
734 (true, true) => [0u8, 3],
737 (true, true) => [0u8, 3],
735 });
738 });
736 bytes.extend(&self.version.to_be_bytes());
739 bytes.extend(&self.version.to_be_bytes());
737 // Remaining offset bytes.
740 // Remaining offset bytes.
738 bytes.extend(&[0u8; 2]);
741 bytes.extend(&[0u8; 2]);
739 } else {
742 } else {
740 // Offset stored on 48 bits (6 bytes)
743 // Offset stored on 48 bits (6 bytes)
741 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
744 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
742 }
745 }
743 bytes.extend(&[0u8; 2]); // Revision flags.
746 bytes.extend(&[0u8; 2]); // Revision flags.
744 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
747 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
745 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
748 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
746 bytes.extend(
749 bytes.extend(
747 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
750 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
748 );
751 );
749 bytes.extend(&self.link_revision.0.to_be_bytes());
752 bytes.extend(&self.link_revision.0.to_be_bytes());
750 bytes.extend(&self.p1.0.to_be_bytes());
753 bytes.extend(&self.p1.0.to_be_bytes());
751 bytes.extend(&self.p2.0.to_be_bytes());
754 bytes.extend(&self.p2.0.to_be_bytes());
752 bytes.extend(self.node.as_bytes());
755 bytes.extend(self.node.as_bytes());
753 bytes.extend(vec![0u8; 12]);
756 bytes.extend(vec![0u8; 12]);
754 bytes
757 bytes
755 }
758 }
756 }
759 }
757
760
758 pub fn is_inline(index_bytes: &[u8]) -> bool {
761 pub fn is_inline(index_bytes: &[u8]) -> bool {
759 IndexHeader::parse(index_bytes)
762 IndexHeader::parse(index_bytes)
760 .expect("too short")
763 .expect("too short")
761 .unwrap()
764 .unwrap()
762 .format_flags()
765 .format_flags()
763 .is_inline()
766 .is_inline()
764 }
767 }
765
768
766 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
769 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
767 IndexHeader::parse(index_bytes)
770 IndexHeader::parse(index_bytes)
768 .expect("too short")
771 .expect("too short")
769 .unwrap()
772 .unwrap()
770 .format_flags()
773 .format_flags()
771 .uses_generaldelta()
774 .uses_generaldelta()
772 }
775 }
773
776
774 pub fn get_version(index_bytes: &[u8]) -> u16 {
777 pub fn get_version(index_bytes: &[u8]) -> u16 {
775 IndexHeader::parse(index_bytes)
778 IndexHeader::parse(index_bytes)
776 .expect("too short")
779 .expect("too short")
777 .unwrap()
780 .unwrap()
778 .format_version()
781 .format_version()
779 }
782 }
780
783
781 #[test]
784 #[test]
782 fn flags_when_no_inline_flag_test() {
785 fn flags_when_no_inline_flag_test() {
783 let bytes = IndexEntryBuilder::new()
786 let bytes = IndexEntryBuilder::new()
784 .is_first(true)
787 .is_first(true)
785 .with_general_delta(false)
788 .with_general_delta(false)
786 .with_inline(false)
789 .with_inline(false)
787 .build();
790 .build();
788
791
789 assert!(!is_inline(&bytes));
792 assert!(!is_inline(&bytes));
790 assert!(!uses_generaldelta(&bytes));
793 assert!(!uses_generaldelta(&bytes));
791 }
794 }
792
795
793 #[test]
796 #[test]
794 fn flags_when_inline_flag_test() {
797 fn flags_when_inline_flag_test() {
795 let bytes = IndexEntryBuilder::new()
798 let bytes = IndexEntryBuilder::new()
796 .is_first(true)
799 .is_first(true)
797 .with_general_delta(false)
800 .with_general_delta(false)
798 .with_inline(true)
801 .with_inline(true)
799 .build();
802 .build();
800
803
801 assert!(is_inline(&bytes));
804 assert!(is_inline(&bytes));
802 assert!(!uses_generaldelta(&bytes));
805 assert!(!uses_generaldelta(&bytes));
803 }
806 }
804
807
805 #[test]
808 #[test]
806 fn flags_when_inline_and_generaldelta_flags_test() {
809 fn flags_when_inline_and_generaldelta_flags_test() {
807 let bytes = IndexEntryBuilder::new()
810 let bytes = IndexEntryBuilder::new()
808 .is_first(true)
811 .is_first(true)
809 .with_general_delta(true)
812 .with_general_delta(true)
810 .with_inline(true)
813 .with_inline(true)
811 .build();
814 .build();
812
815
813 assert!(is_inline(&bytes));
816 assert!(is_inline(&bytes));
814 assert!(uses_generaldelta(&bytes));
817 assert!(uses_generaldelta(&bytes));
815 }
818 }
816
819
817 #[test]
820 #[test]
818 fn test_offset() {
821 fn test_offset() {
819 let bytes = IndexEntryBuilder::new().with_offset(1).build();
822 let bytes = IndexEntryBuilder::new().with_offset(1).build();
820 let entry = IndexEntry {
823 let entry = IndexEntry {
821 bytes: &bytes,
824 bytes: &bytes,
822 offset_override: None,
825 offset_override: None,
823 };
826 };
824
827
825 assert_eq!(entry.offset(), 1)
828 assert_eq!(entry.offset(), 1)
826 }
829 }
827
830
828 #[test]
831 #[test]
829 fn test_with_overridden_offset() {
832 fn test_with_overridden_offset() {
830 let bytes = IndexEntryBuilder::new().with_offset(1).build();
833 let bytes = IndexEntryBuilder::new().with_offset(1).build();
831 let entry = IndexEntry {
834 let entry = IndexEntry {
832 bytes: &bytes,
835 bytes: &bytes,
833 offset_override: Some(2),
836 offset_override: Some(2),
834 };
837 };
835
838
836 assert_eq!(entry.offset(), 2)
839 assert_eq!(entry.offset(), 2)
837 }
840 }
838
841
839 #[test]
842 #[test]
840 fn test_compressed_len() {
843 fn test_compressed_len() {
841 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
844 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
842 let entry = IndexEntry {
845 let entry = IndexEntry {
843 bytes: &bytes,
846 bytes: &bytes,
844 offset_override: None,
847 offset_override: None,
845 };
848 };
846
849
847 assert_eq!(entry.compressed_len(), 1)
850 assert_eq!(entry.compressed_len(), 1)
848 }
851 }
849
852
850 #[test]
853 #[test]
851 fn test_uncompressed_len() {
854 fn test_uncompressed_len() {
852 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
855 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
853 let entry = IndexEntry {
856 let entry = IndexEntry {
854 bytes: &bytes,
857 bytes: &bytes,
855 offset_override: None,
858 offset_override: None,
856 };
859 };
857
860
858 assert_eq!(entry.uncompressed_len(), 1)
861 assert_eq!(entry.uncompressed_len(), 1)
859 }
862 }
860
863
861 #[test]
864 #[test]
862 fn test_base_revision_or_base_of_delta_chain() {
865 fn test_base_revision_or_base_of_delta_chain() {
863 let bytes = IndexEntryBuilder::new()
866 let bytes = IndexEntryBuilder::new()
864 .with_base_revision_or_base_of_delta_chain(Revision(1))
867 .with_base_revision_or_base_of_delta_chain(Revision(1))
865 .build();
868 .build();
866 let entry = IndexEntry {
869 let entry = IndexEntry {
867 bytes: &bytes,
870 bytes: &bytes,
868 offset_override: None,
871 offset_override: None,
869 };
872 };
870
873
871 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
874 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
872 }
875 }
873
876
874 #[test]
877 #[test]
875 fn link_revision_test() {
878 fn link_revision_test() {
876 let bytes = IndexEntryBuilder::new()
879 let bytes = IndexEntryBuilder::new()
877 .with_link_revision(Revision(123))
880 .with_link_revision(Revision(123))
878 .build();
881 .build();
879
882
880 let entry = IndexEntry {
883 let entry = IndexEntry {
881 bytes: &bytes,
884 bytes: &bytes,
882 offset_override: None,
885 offset_override: None,
883 };
886 };
884
887
885 assert_eq!(entry.link_revision(), 123.into());
888 assert_eq!(entry.link_revision(), 123.into());
886 }
889 }
887
890
888 #[test]
891 #[test]
889 fn p1_test() {
892 fn p1_test() {
890 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
893 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
891
894
892 let entry = IndexEntry {
895 let entry = IndexEntry {
893 bytes: &bytes,
896 bytes: &bytes,
894 offset_override: None,
897 offset_override: None,
895 };
898 };
896
899
897 assert_eq!(entry.p1(), 123.into());
900 assert_eq!(entry.p1(), 123.into());
898 }
901 }
899
902
900 #[test]
903 #[test]
901 fn p2_test() {
904 fn p2_test() {
902 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
905 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
903
906
904 let entry = IndexEntry {
907 let entry = IndexEntry {
905 bytes: &bytes,
908 bytes: &bytes,
906 offset_override: None,
909 offset_override: None,
907 };
910 };
908
911
909 assert_eq!(entry.p2(), 123.into());
912 assert_eq!(entry.p2(), 123.into());
910 }
913 }
911
914
912 #[test]
915 #[test]
913 fn node_test() {
916 fn node_test() {
914 let node = Node::from_hex("0123456789012345678901234567890123456789")
917 let node = Node::from_hex("0123456789012345678901234567890123456789")
915 .unwrap();
918 .unwrap();
916 let bytes = IndexEntryBuilder::new().with_node(node).build();
919 let bytes = IndexEntryBuilder::new().with_node(node).build();
917
920
918 let entry = IndexEntry {
921 let entry = IndexEntry {
919 bytes: &bytes,
922 bytes: &bytes,
920 offset_override: None,
923 offset_override: None,
921 };
924 };
922
925
923 assert_eq!(*entry.hash(), node);
926 assert_eq!(*entry.hash(), node);
924 }
927 }
925
928
926 #[test]
929 #[test]
927 fn version_test() {
930 fn version_test() {
928 let bytes = IndexEntryBuilder::new()
931 let bytes = IndexEntryBuilder::new()
929 .is_first(true)
932 .is_first(true)
930 .with_version(2)
933 .with_version(2)
931 .build();
934 .build();
932
935
933 assert_eq!(get_version(&bytes), 2)
936 assert_eq!(get_version(&bytes), 2)
934 }
937 }
935 }
938 }
936
939
937 #[cfg(test)]
940 #[cfg(test)]
938 pub use tests::IndexEntryBuilder;
941 pub use tests::IndexEntryBuilder;
General Comments 0
You need to be logged in to leave comments. Login now