##// END OF EJS Templates
rust-revlog: make `IndexEntryBuilder` build a whole entry...
Martin von Zweigbergk -
r49984:2a9a55ff default
parent child Browse files
Show More
@@ -1,528 +1,616 b''
1 use std::convert::TryInto;
1 use std::convert::TryInto;
2 use std::ops::Deref;
2 use std::ops::Deref;
3
3
4 use byteorder::{BigEndian, ByteOrder};
4 use byteorder::{BigEndian, ByteOrder};
5
5
6 use crate::errors::HgError;
6 use crate::errors::HgError;
7 use crate::revlog::node::Node;
7 use crate::revlog::node::Node;
8 use crate::revlog::{Revision, NULL_REVISION};
8 use crate::revlog::{Revision, NULL_REVISION};
9
9
10 pub const INDEX_ENTRY_SIZE: usize = 64;
10 pub const INDEX_ENTRY_SIZE: usize = 64;
11
11
12 pub struct IndexHeader {
12 pub struct IndexHeader {
13 header_bytes: [u8; 4],
13 header_bytes: [u8; 4],
14 }
14 }
15
15
16 #[derive(Copy, Clone)]
16 #[derive(Copy, Clone)]
17 pub struct IndexHeaderFlags {
17 pub struct IndexHeaderFlags {
18 flags: u16,
18 flags: u16,
19 }
19 }
20
20
21 /// Corresponds to the high bits of `_format_flags` in python
21 /// Corresponds to the high bits of `_format_flags` in python
22 impl IndexHeaderFlags {
22 impl IndexHeaderFlags {
23 /// Corresponds to FLAG_INLINE_DATA in python
23 /// Corresponds to FLAG_INLINE_DATA in python
24 pub fn is_inline(self) -> bool {
24 pub fn is_inline(self) -> bool {
25 return self.flags & 1 != 0;
25 return self.flags & 1 != 0;
26 }
26 }
27 /// Corresponds to FLAG_GENERALDELTA in python
27 /// Corresponds to FLAG_GENERALDELTA in python
28 pub fn uses_generaldelta(self) -> bool {
28 pub fn uses_generaldelta(self) -> bool {
29 return self.flags & 2 != 0;
29 return self.flags & 2 != 0;
30 }
30 }
31 }
31 }
32
32
33 /// Corresponds to the INDEX_HEADER structure,
33 /// Corresponds to the INDEX_HEADER structure,
34 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
34 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
35 impl IndexHeader {
35 impl IndexHeader {
36 fn format_flags(&self) -> IndexHeaderFlags {
36 fn format_flags(&self) -> IndexHeaderFlags {
37 // No "unknown flags" check here, unlike in python. Maybe there should
37 // No "unknown flags" check here, unlike in python. Maybe there should
38 // be.
38 // be.
39 return IndexHeaderFlags {
39 return IndexHeaderFlags {
40 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
40 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
41 };
41 };
42 }
42 }
43
43
44 /// The only revlog version currently supported by rhg.
44 /// The only revlog version currently supported by rhg.
45 const REVLOGV1: u16 = 1;
45 const REVLOGV1: u16 = 1;
46
46
47 /// Corresponds to `_format_version` in Python.
47 /// Corresponds to `_format_version` in Python.
48 fn format_version(&self) -> u16 {
48 fn format_version(&self) -> u16 {
49 return BigEndian::read_u16(&self.header_bytes[2..4]);
49 return BigEndian::read_u16(&self.header_bytes[2..4]);
50 }
50 }
51
51
52 const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
52 const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
53 // We treat an empty file as a valid index with no entries.
53 // We treat an empty file as a valid index with no entries.
54 // Here we make an arbitrary choice of what we assume the format of the
54 // Here we make an arbitrary choice of what we assume the format of the
55 // index to be (V1, using generaldelta).
55 // index to be (V1, using generaldelta).
56 // This doesn't matter too much, since we're only doing read-only
56 // This doesn't matter too much, since we're only doing read-only
57 // access. but the value corresponds to the `new_header` variable in
57 // access. but the value corresponds to the `new_header` variable in
58 // `revlog.py`, `_loadindex`
58 // `revlog.py`, `_loadindex`
59 header_bytes: [0, 3, 0, 1],
59 header_bytes: [0, 3, 0, 1],
60 };
60 };
61
61
62 fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> {
62 fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> {
63 if index_bytes.len() == 0 {
63 if index_bytes.len() == 0 {
64 return Ok(IndexHeader::EMPTY_INDEX_HEADER);
64 return Ok(IndexHeader::EMPTY_INDEX_HEADER);
65 }
65 }
66 if index_bytes.len() < 4 {
66 if index_bytes.len() < 4 {
67 return Err(HgError::corrupted(
67 return Err(HgError::corrupted(
68 "corrupted revlog: can't read the index format header",
68 "corrupted revlog: can't read the index format header",
69 ));
69 ));
70 }
70 }
71 return Ok(IndexHeader {
71 return Ok(IndexHeader {
72 header_bytes: {
72 header_bytes: {
73 let bytes: [u8; 4] =
73 let bytes: [u8; 4] =
74 index_bytes[0..4].try_into().expect("impossible");
74 index_bytes[0..4].try_into().expect("impossible");
75 bytes
75 bytes
76 },
76 },
77 });
77 });
78 }
78 }
79 }
79 }
80
80
81 /// A Revlog index
81 /// A Revlog index
82 pub struct Index {
82 pub struct Index {
83 bytes: Box<dyn Deref<Target = [u8]> + Send>,
83 bytes: Box<dyn Deref<Target = [u8]> + Send>,
84 /// Offsets of starts of index blocks.
84 /// Offsets of starts of index blocks.
85 /// Only needed when the index is interleaved with data.
85 /// Only needed when the index is interleaved with data.
86 offsets: Option<Vec<usize>>,
86 offsets: Option<Vec<usize>>,
87 uses_generaldelta: bool,
87 uses_generaldelta: bool,
88 }
88 }
89
89
90 impl Index {
90 impl Index {
91 /// Create an index from bytes.
91 /// Create an index from bytes.
92 /// Calculate the start of each entry when is_inline is true.
92 /// Calculate the start of each entry when is_inline is true.
93 pub fn new(
93 pub fn new(
94 bytes: Box<dyn Deref<Target = [u8]> + Send>,
94 bytes: Box<dyn Deref<Target = [u8]> + Send>,
95 ) -> Result<Self, HgError> {
95 ) -> Result<Self, HgError> {
96 let header = IndexHeader::parse(bytes.as_ref())?;
96 let header = IndexHeader::parse(bytes.as_ref())?;
97
97
98 if header.format_version() != IndexHeader::REVLOGV1 {
98 if header.format_version() != IndexHeader::REVLOGV1 {
99 // A proper new version should have had a repo/store
99 // A proper new version should have had a repo/store
100 // requirement.
100 // requirement.
101 return Err(HgError::corrupted("unsupported revlog version"));
101 return Err(HgError::corrupted("unsupported revlog version"));
102 }
102 }
103
103
104 // This is only correct because we know version is REVLOGV1.
104 // This is only correct because we know version is REVLOGV1.
105 // In v2 we always use generaldelta, while in v0 we never use
105 // In v2 we always use generaldelta, while in v0 we never use
106 // generaldelta. Similar for [is_inline] (it's only used in v1).
106 // generaldelta. Similar for [is_inline] (it's only used in v1).
107 let uses_generaldelta = header.format_flags().uses_generaldelta();
107 let uses_generaldelta = header.format_flags().uses_generaldelta();
108
108
109 if header.format_flags().is_inline() {
109 if header.format_flags().is_inline() {
110 let mut offset: usize = 0;
110 let mut offset: usize = 0;
111 let mut offsets = Vec::new();
111 let mut offsets = Vec::new();
112
112
113 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
113 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
114 offsets.push(offset);
114 offsets.push(offset);
115 let end = offset + INDEX_ENTRY_SIZE;
115 let end = offset + INDEX_ENTRY_SIZE;
116 let entry = IndexEntry {
116 let entry = IndexEntry {
117 bytes: &bytes[offset..end],
117 bytes: &bytes[offset..end],
118 offset_override: None,
118 offset_override: None,
119 };
119 };
120
120
121 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
121 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
122 }
122 }
123
123
124 if offset == bytes.len() {
124 if offset == bytes.len() {
125 Ok(Self {
125 Ok(Self {
126 bytes,
126 bytes,
127 offsets: Some(offsets),
127 offsets: Some(offsets),
128 uses_generaldelta,
128 uses_generaldelta,
129 })
129 })
130 } else {
130 } else {
131 Err(HgError::corrupted("unexpected inline revlog length")
131 Err(HgError::corrupted("unexpected inline revlog length")
132 .into())
132 .into())
133 }
133 }
134 } else {
134 } else {
135 Ok(Self {
135 Ok(Self {
136 bytes,
136 bytes,
137 offsets: None,
137 offsets: None,
138 uses_generaldelta,
138 uses_generaldelta,
139 })
139 })
140 }
140 }
141 }
141 }
142
142
143 pub fn uses_generaldelta(&self) -> bool {
143 pub fn uses_generaldelta(&self) -> bool {
144 self.uses_generaldelta
144 self.uses_generaldelta
145 }
145 }
146
146
147 /// Value of the inline flag.
147 /// Value of the inline flag.
148 pub fn is_inline(&self) -> bool {
148 pub fn is_inline(&self) -> bool {
149 self.offsets.is_some()
149 self.offsets.is_some()
150 }
150 }
151
151
152 /// Return a slice of bytes if `revlog` is inline. Panic if not.
152 /// Return a slice of bytes if `revlog` is inline. Panic if not.
153 pub fn data(&self, start: usize, end: usize) -> &[u8] {
153 pub fn data(&self, start: usize, end: usize) -> &[u8] {
154 if !self.is_inline() {
154 if !self.is_inline() {
155 panic!("tried to access data in the index of a revlog that is not inline");
155 panic!("tried to access data in the index of a revlog that is not inline");
156 }
156 }
157 &self.bytes[start..end]
157 &self.bytes[start..end]
158 }
158 }
159
159
160 /// Return number of entries of the revlog index.
160 /// Return number of entries of the revlog index.
161 pub fn len(&self) -> usize {
161 pub fn len(&self) -> usize {
162 if let Some(offsets) = &self.offsets {
162 if let Some(offsets) = &self.offsets {
163 offsets.len()
163 offsets.len()
164 } else {
164 } else {
165 self.bytes.len() / INDEX_ENTRY_SIZE
165 self.bytes.len() / INDEX_ENTRY_SIZE
166 }
166 }
167 }
167 }
168
168
169 /// Returns `true` if the `Index` has zero `entries`.
169 /// Returns `true` if the `Index` has zero `entries`.
170 pub fn is_empty(&self) -> bool {
170 pub fn is_empty(&self) -> bool {
171 self.len() == 0
171 self.len() == 0
172 }
172 }
173
173
174 /// Return the index entry corresponding to the given revision if it
174 /// Return the index entry corresponding to the given revision if it
175 /// exists.
175 /// exists.
176 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
176 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
177 if rev == NULL_REVISION {
177 if rev == NULL_REVISION {
178 return None;
178 return None;
179 }
179 }
180 if let Some(offsets) = &self.offsets {
180 if let Some(offsets) = &self.offsets {
181 self.get_entry_inline(rev, offsets)
181 self.get_entry_inline(rev, offsets)
182 } else {
182 } else {
183 self.get_entry_separated(rev)
183 self.get_entry_separated(rev)
184 }
184 }
185 }
185 }
186
186
187 fn get_entry_inline(
187 fn get_entry_inline(
188 &self,
188 &self,
189 rev: Revision,
189 rev: Revision,
190 offsets: &[usize],
190 offsets: &[usize],
191 ) -> Option<IndexEntry> {
191 ) -> Option<IndexEntry> {
192 let start = *offsets.get(rev as usize)?;
192 let start = *offsets.get(rev as usize)?;
193 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
193 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
194 let bytes = &self.bytes[start..end];
194 let bytes = &self.bytes[start..end];
195
195
196 // See IndexEntry for an explanation of this override.
196 // See IndexEntry for an explanation of this override.
197 let offset_override = Some(end);
197 let offset_override = Some(end);
198
198
199 Some(IndexEntry {
199 Some(IndexEntry {
200 bytes,
200 bytes,
201 offset_override,
201 offset_override,
202 })
202 })
203 }
203 }
204
204
205 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
205 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
206 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
206 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
207 if rev as usize >= max_rev {
207 if rev as usize >= max_rev {
208 return None;
208 return None;
209 }
209 }
210 let start = rev as usize * INDEX_ENTRY_SIZE;
210 let start = rev as usize * INDEX_ENTRY_SIZE;
211 let end = start + INDEX_ENTRY_SIZE;
211 let end = start + INDEX_ENTRY_SIZE;
212 let bytes = &self.bytes[start..end];
212 let bytes = &self.bytes[start..end];
213
213
214 // Override the offset of the first revision as its bytes are used
214 // Override the offset of the first revision as its bytes are used
215 // for the index's metadata (saving space because it is always 0)
215 // for the index's metadata (saving space because it is always 0)
216 let offset_override = if rev == 0 { Some(0) } else { None };
216 let offset_override = if rev == 0 { Some(0) } else { None };
217
217
218 Some(IndexEntry {
218 Some(IndexEntry {
219 bytes,
219 bytes,
220 offset_override,
220 offset_override,
221 })
221 })
222 }
222 }
223 }
223 }
224
224
225 impl super::RevlogIndex for Index {
225 impl super::RevlogIndex for Index {
226 fn len(&self) -> usize {
226 fn len(&self) -> usize {
227 self.len()
227 self.len()
228 }
228 }
229
229
230 fn node(&self, rev: Revision) -> Option<&Node> {
230 fn node(&self, rev: Revision) -> Option<&Node> {
231 self.get_entry(rev).map(|entry| entry.hash())
231 self.get_entry(rev).map(|entry| entry.hash())
232 }
232 }
233 }
233 }
234
234
235 #[derive(Debug)]
235 #[derive(Debug)]
236 pub struct IndexEntry<'a> {
236 pub struct IndexEntry<'a> {
237 bytes: &'a [u8],
237 bytes: &'a [u8],
238 /// Allows to override the offset value of the entry.
238 /// Allows to override the offset value of the entry.
239 ///
239 ///
240 /// For interleaved index and data, the offset stored in the index
240 /// For interleaved index and data, the offset stored in the index
241 /// corresponds to the separated data offset.
241 /// corresponds to the separated data offset.
242 /// It has to be overridden with the actual offset in the interleaved
242 /// It has to be overridden with the actual offset in the interleaved
243 /// index which is just after the index block.
243 /// index which is just after the index block.
244 ///
244 ///
245 /// For separated index and data, the offset stored in the first index
245 /// For separated index and data, the offset stored in the first index
246 /// entry is mixed with the index headers.
246 /// entry is mixed with the index headers.
247 /// It has to be overridden with 0.
247 /// It has to be overridden with 0.
248 offset_override: Option<usize>,
248 offset_override: Option<usize>,
249 }
249 }
250
250
251 impl<'a> IndexEntry<'a> {
251 impl<'a> IndexEntry<'a> {
252 /// Return the offset of the data.
252 /// Return the offset of the data.
253 pub fn offset(&self) -> usize {
253 pub fn offset(&self) -> usize {
254 if let Some(offset_override) = self.offset_override {
254 if let Some(offset_override) = self.offset_override {
255 offset_override
255 offset_override
256 } else {
256 } else {
257 let mut bytes = [0; 8];
257 let mut bytes = [0; 8];
258 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
258 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
259 BigEndian::read_u64(&bytes[..]) as usize
259 BigEndian::read_u64(&bytes[..]) as usize
260 }
260 }
261 }
261 }
262
262
263 pub fn flags(&self) -> u16 {
263 pub fn flags(&self) -> u16 {
264 BigEndian::read_u16(&self.bytes[6..=7])
264 BigEndian::read_u16(&self.bytes[6..=7])
265 }
265 }
266
266
267 /// Return the compressed length of the data.
267 /// Return the compressed length of the data.
268 pub fn compressed_len(&self) -> u32 {
268 pub fn compressed_len(&self) -> u32 {
269 BigEndian::read_u32(&self.bytes[8..=11])
269 BigEndian::read_u32(&self.bytes[8..=11])
270 }
270 }
271
271
272 /// Return the uncompressed length of the data.
272 /// Return the uncompressed length of the data.
273 pub fn uncompressed_len(&self) -> i32 {
273 pub fn uncompressed_len(&self) -> i32 {
274 BigEndian::read_i32(&self.bytes[12..=15])
274 BigEndian::read_i32(&self.bytes[12..=15])
275 }
275 }
276
276
277 /// Return the revision upon which the data has been derived.
277 /// Return the revision upon which the data has been derived.
278 pub fn base_revision_or_base_of_delta_chain(&self) -> Revision {
278 pub fn base_revision_or_base_of_delta_chain(&self) -> Revision {
279 // TODO Maybe return an Option when base_revision == rev?
279 // TODO Maybe return an Option when base_revision == rev?
280 // Requires to add rev to IndexEntry
280 // Requires to add rev to IndexEntry
281
281
282 BigEndian::read_i32(&self.bytes[16..])
282 BigEndian::read_i32(&self.bytes[16..])
283 }
283 }
284
284
285 pub fn link_revision(&self) -> Revision {
286 BigEndian::read_i32(&self.bytes[20..])
287 }
288
285 pub fn p1(&self) -> Revision {
289 pub fn p1(&self) -> Revision {
286 BigEndian::read_i32(&self.bytes[24..])
290 BigEndian::read_i32(&self.bytes[24..])
287 }
291 }
288
292
289 pub fn p2(&self) -> Revision {
293 pub fn p2(&self) -> Revision {
290 BigEndian::read_i32(&self.bytes[28..])
294 BigEndian::read_i32(&self.bytes[28..])
291 }
295 }
292
296
293 /// Return the hash of revision's full text.
297 /// Return the hash of revision's full text.
294 ///
298 ///
295 /// Currently, SHA-1 is used and only the first 20 bytes of this field
299 /// Currently, SHA-1 is used and only the first 20 bytes of this field
296 /// are used.
300 /// are used.
297 pub fn hash(&self) -> &'a Node {
301 pub fn hash(&self) -> &'a Node {
298 (&self.bytes[32..52]).try_into().unwrap()
302 (&self.bytes[32..52]).try_into().unwrap()
299 }
303 }
300 }
304 }
301
305
302 #[cfg(test)]
306 #[cfg(test)]
303 mod tests {
307 mod tests {
304 use super::*;
308 use super::*;
309 use crate::node::NULL_NODE;
305
310
306 #[cfg(test)]
311 #[cfg(test)]
307 #[derive(Debug, Copy, Clone)]
312 #[derive(Debug, Copy, Clone)]
308 pub struct IndexEntryBuilder {
313 pub struct IndexEntryBuilder {
309 is_first: bool,
314 is_first: bool,
310 is_inline: bool,
315 is_inline: bool,
311 is_general_delta: bool,
316 is_general_delta: bool,
312 version: u16,
317 version: u16,
313 offset: usize,
318 offset: usize,
314 compressed_len: usize,
319 compressed_len: usize,
315 uncompressed_len: usize,
320 uncompressed_len: usize,
316 base_revision_or_base_of_delta_chain: Revision,
321 base_revision_or_base_of_delta_chain: Revision,
322 link_revision: Revision,
323 p1: Revision,
324 p2: Revision,
325 node: Node,
317 }
326 }
318
327
319 #[cfg(test)]
328 #[cfg(test)]
320 impl IndexEntryBuilder {
329 impl IndexEntryBuilder {
321 pub fn new() -> Self {
330 pub fn new() -> Self {
322 Self {
331 Self {
323 is_first: false,
332 is_first: false,
324 is_inline: false,
333 is_inline: false,
325 is_general_delta: true,
334 is_general_delta: true,
326 version: 1,
335 version: 1,
327 offset: 0,
336 offset: 0,
328 compressed_len: 0,
337 compressed_len: 0,
329 uncompressed_len: 0,
338 uncompressed_len: 0,
330 base_revision_or_base_of_delta_chain: 0,
339 base_revision_or_base_of_delta_chain: 0,
340 link_revision: 0,
341 p1: NULL_REVISION,
342 p2: NULL_REVISION,
343 node: NULL_NODE,
331 }
344 }
332 }
345 }
333
346
334 pub fn is_first(&mut self, value: bool) -> &mut Self {
347 pub fn is_first(&mut self, value: bool) -> &mut Self {
335 self.is_first = value;
348 self.is_first = value;
336 self
349 self
337 }
350 }
338
351
339 pub fn with_inline(&mut self, value: bool) -> &mut Self {
352 pub fn with_inline(&mut self, value: bool) -> &mut Self {
340 self.is_inline = value;
353 self.is_inline = value;
341 self
354 self
342 }
355 }
343
356
344 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
357 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
345 self.is_general_delta = value;
358 self.is_general_delta = value;
346 self
359 self
347 }
360 }
348
361
349 pub fn with_version(&mut self, value: u16) -> &mut Self {
362 pub fn with_version(&mut self, value: u16) -> &mut Self {
350 self.version = value;
363 self.version = value;
351 self
364 self
352 }
365 }
353
366
354 pub fn with_offset(&mut self, value: usize) -> &mut Self {
367 pub fn with_offset(&mut self, value: usize) -> &mut Self {
355 self.offset = value;
368 self.offset = value;
356 self
369 self
357 }
370 }
358
371
359 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
372 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
360 self.compressed_len = value;
373 self.compressed_len = value;
361 self
374 self
362 }
375 }
363
376
364 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
377 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
365 self.uncompressed_len = value;
378 self.uncompressed_len = value;
366 self
379 self
367 }
380 }
368
381
369 pub fn with_base_revision_or_base_of_delta_chain(
382 pub fn with_base_revision_or_base_of_delta_chain(
370 &mut self,
383 &mut self,
371 value: Revision,
384 value: Revision,
372 ) -> &mut Self {
385 ) -> &mut Self {
373 self.base_revision_or_base_of_delta_chain = value;
386 self.base_revision_or_base_of_delta_chain = value;
374 self
387 self
375 }
388 }
376
389
390 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
391 self.link_revision = value;
392 self
393 }
394
395 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
396 self.p1 = value;
397 self
398 }
399
400 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
401 self.p2 = value;
402 self
403 }
404
405 pub fn with_node(&mut self, value: Node) -> &mut Self {
406 self.node = value;
407 self
408 }
409
377 pub fn build(&self) -> Vec<u8> {
410 pub fn build(&self) -> Vec<u8> {
378 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
411 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
379 if self.is_first {
412 if self.is_first {
380 bytes.extend(&match (self.is_general_delta, self.is_inline) {
413 bytes.extend(&match (self.is_general_delta, self.is_inline) {
381 (false, false) => [0u8, 0],
414 (false, false) => [0u8, 0],
382 (false, true) => [0u8, 1],
415 (false, true) => [0u8, 1],
383 (true, false) => [0u8, 2],
416 (true, false) => [0u8, 2],
384 (true, true) => [0u8, 3],
417 (true, true) => [0u8, 3],
385 });
418 });
386 bytes.extend(&self.version.to_be_bytes());
419 bytes.extend(&self.version.to_be_bytes());
387 // Remaining offset bytes.
420 // Remaining offset bytes.
388 bytes.extend(&[0u8; 2]);
421 bytes.extend(&[0u8; 2]);
389 } else {
422 } else {
390 // Offset stored on 48 bits (6 bytes)
423 // Offset stored on 48 bits (6 bytes)
391 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
424 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
392 }
425 }
393 bytes.extend(&[0u8; 2]); // Revision flags.
426 bytes.extend(&[0u8; 2]); // Revision flags.
394 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
427 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
395 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
428 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
396 bytes.extend(
429 bytes.extend(
397 &self.base_revision_or_base_of_delta_chain.to_be_bytes(),
430 &self.base_revision_or_base_of_delta_chain.to_be_bytes(),
398 );
431 );
432 bytes.extend(&self.link_revision.to_be_bytes());
433 bytes.extend(&self.p1.to_be_bytes());
434 bytes.extend(&self.p2.to_be_bytes());
435 bytes.extend(self.node.as_bytes());
436 bytes.extend(vec![0u8; 12]);
399 bytes
437 bytes
400 }
438 }
401 }
439 }
402
440
403 pub fn is_inline(index_bytes: &[u8]) -> bool {
441 pub fn is_inline(index_bytes: &[u8]) -> bool {
404 IndexHeader::parse(index_bytes)
442 IndexHeader::parse(index_bytes)
405 .expect("too short")
443 .expect("too short")
406 .format_flags()
444 .format_flags()
407 .is_inline()
445 .is_inline()
408 }
446 }
409
447
410 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
448 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
411 IndexHeader::parse(index_bytes)
449 IndexHeader::parse(index_bytes)
412 .expect("too short")
450 .expect("too short")
413 .format_flags()
451 .format_flags()
414 .uses_generaldelta()
452 .uses_generaldelta()
415 }
453 }
416
454
417 pub fn get_version(index_bytes: &[u8]) -> u16 {
455 pub fn get_version(index_bytes: &[u8]) -> u16 {
418 IndexHeader::parse(index_bytes)
456 IndexHeader::parse(index_bytes)
419 .expect("too short")
457 .expect("too short")
420 .format_version()
458 .format_version()
421 }
459 }
422
460
423 #[test]
461 #[test]
424 fn flags_when_no_inline_flag_test() {
462 fn flags_when_no_inline_flag_test() {
425 let bytes = IndexEntryBuilder::new()
463 let bytes = IndexEntryBuilder::new()
426 .is_first(true)
464 .is_first(true)
427 .with_general_delta(false)
465 .with_general_delta(false)
428 .with_inline(false)
466 .with_inline(false)
429 .build();
467 .build();
430
468
431 assert_eq!(is_inline(&bytes), false);
469 assert_eq!(is_inline(&bytes), false);
432 assert_eq!(uses_generaldelta(&bytes), false);
470 assert_eq!(uses_generaldelta(&bytes), false);
433 }
471 }
434
472
435 #[test]
473 #[test]
436 fn flags_when_inline_flag_test() {
474 fn flags_when_inline_flag_test() {
437 let bytes = IndexEntryBuilder::new()
475 let bytes = IndexEntryBuilder::new()
438 .is_first(true)
476 .is_first(true)
439 .with_general_delta(false)
477 .with_general_delta(false)
440 .with_inline(true)
478 .with_inline(true)
441 .build();
479 .build();
442
480
443 assert_eq!(is_inline(&bytes), true);
481 assert_eq!(is_inline(&bytes), true);
444 assert_eq!(uses_generaldelta(&bytes), false);
482 assert_eq!(uses_generaldelta(&bytes), false);
445 }
483 }
446
484
447 #[test]
485 #[test]
448 fn flags_when_inline_and_generaldelta_flags_test() {
486 fn flags_when_inline_and_generaldelta_flags_test() {
449 let bytes = IndexEntryBuilder::new()
487 let bytes = IndexEntryBuilder::new()
450 .is_first(true)
488 .is_first(true)
451 .with_general_delta(true)
489 .with_general_delta(true)
452 .with_inline(true)
490 .with_inline(true)
453 .build();
491 .build();
454
492
455 assert_eq!(is_inline(&bytes), true);
493 assert_eq!(is_inline(&bytes), true);
456 assert_eq!(uses_generaldelta(&bytes), true);
494 assert_eq!(uses_generaldelta(&bytes), true);
457 }
495 }
458
496
459 #[test]
497 #[test]
460 fn test_offset() {
498 fn test_offset() {
461 let bytes = IndexEntryBuilder::new().with_offset(1).build();
499 let bytes = IndexEntryBuilder::new().with_offset(1).build();
462 let entry = IndexEntry {
500 let entry = IndexEntry {
463 bytes: &bytes,
501 bytes: &bytes,
464 offset_override: None,
502 offset_override: None,
465 };
503 };
466
504
467 assert_eq!(entry.offset(), 1)
505 assert_eq!(entry.offset(), 1)
468 }
506 }
469
507
470 #[test]
508 #[test]
471 fn test_with_overridden_offset() {
509 fn test_with_overridden_offset() {
472 let bytes = IndexEntryBuilder::new().with_offset(1).build();
510 let bytes = IndexEntryBuilder::new().with_offset(1).build();
473 let entry = IndexEntry {
511 let entry = IndexEntry {
474 bytes: &bytes,
512 bytes: &bytes,
475 offset_override: Some(2),
513 offset_override: Some(2),
476 };
514 };
477
515
478 assert_eq!(entry.offset(), 2)
516 assert_eq!(entry.offset(), 2)
479 }
517 }
480
518
481 #[test]
519 #[test]
482 fn test_compressed_len() {
520 fn test_compressed_len() {
483 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
521 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
484 let entry = IndexEntry {
522 let entry = IndexEntry {
485 bytes: &bytes,
523 bytes: &bytes,
486 offset_override: None,
524 offset_override: None,
487 };
525 };
488
526
489 assert_eq!(entry.compressed_len(), 1)
527 assert_eq!(entry.compressed_len(), 1)
490 }
528 }
491
529
492 #[test]
530 #[test]
493 fn test_uncompressed_len() {
531 fn test_uncompressed_len() {
494 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
532 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
495 let entry = IndexEntry {
533 let entry = IndexEntry {
496 bytes: &bytes,
534 bytes: &bytes,
497 offset_override: None,
535 offset_override: None,
498 };
536 };
499
537
500 assert_eq!(entry.uncompressed_len(), 1)
538 assert_eq!(entry.uncompressed_len(), 1)
501 }
539 }
502
540
503 #[test]
541 #[test]
504 fn test_base_revision_or_base_of_delta_chain() {
542 fn test_base_revision_or_base_of_delta_chain() {
505 let bytes = IndexEntryBuilder::new()
543 let bytes = IndexEntryBuilder::new()
506 .with_base_revision_or_base_of_delta_chain(1)
544 .with_base_revision_or_base_of_delta_chain(1)
507 .build();
545 .build();
508 let entry = IndexEntry {
546 let entry = IndexEntry {
509 bytes: &bytes,
547 bytes: &bytes,
510 offset_override: None,
548 offset_override: None,
511 };
549 };
512
550
513 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1)
551 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1)
514 }
552 }
515
553
516 #[test]
554 #[test]
555 fn link_revision_test() {
556 let bytes = IndexEntryBuilder::new().with_link_revision(123).build();
557
558 let entry = IndexEntry {
559 bytes: &bytes,
560 offset_override: None,
561 };
562
563 assert_eq!(entry.link_revision(), 123);
564 }
565
566 #[test]
567 fn p1_test() {
568 let bytes = IndexEntryBuilder::new().with_p1(123).build();
569
570 let entry = IndexEntry {
571 bytes: &bytes,
572 offset_override: None,
573 };
574
575 assert_eq!(entry.p1(), 123);
576 }
577
578 #[test]
579 fn p2_test() {
580 let bytes = IndexEntryBuilder::new().with_p2(123).build();
581
582 let entry = IndexEntry {
583 bytes: &bytes,
584 offset_override: None,
585 };
586
587 assert_eq!(entry.p2(), 123);
588 }
589
590 #[test]
591 fn node_test() {
592 let node = Node::from_hex("0123456789012345678901234567890123456789")
593 .unwrap();
594 let bytes = IndexEntryBuilder::new().with_node(node).build();
595
596 let entry = IndexEntry {
597 bytes: &bytes,
598 offset_override: None,
599 };
600
601 assert_eq!(*entry.hash(), node);
602 }
603
604 #[test]
517 fn version_test() {
605 fn version_test() {
518 let bytes = IndexEntryBuilder::new()
606 let bytes = IndexEntryBuilder::new()
519 .is_first(true)
607 .is_first(true)
520 .with_version(2)
608 .with_version(2)
521 .build();
609 .build();
522
610
523 assert_eq!(get_version(&bytes), 2)
611 assert_eq!(get_version(&bytes), 2)
524 }
612 }
525 }
613 }
526
614
527 #[cfg(test)]
615 #[cfg(test)]
528 pub use tests::IndexEntryBuilder;
616 pub use tests::IndexEntryBuilder;
General Comments 0
You need to be logged in to leave comments. Login now