##// END OF EJS Templates
hg-core: return `Err` on decompression error (D8958#inline-15004 followup)...
Antoine cezar -
r46169:49765789 default
parent child Browse files
Show More
@@ -1,438 +1,445
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::fs::File;
2 use std::fs::File;
3 use std::io::Read;
3 use std::io::Read;
4 use std::ops::Deref;
4 use std::ops::Deref;
5 use std::path::Path;
5 use std::path::Path;
6
6
7 use byteorder::{BigEndian, ByteOrder};
7 use byteorder::{BigEndian, ByteOrder};
8 use crypto::digest::Digest;
8 use crypto::digest::Digest;
9 use crypto::sha1::Sha1;
9 use crypto::sha1::Sha1;
10 use flate2::read::ZlibDecoder;
10 use flate2::read::ZlibDecoder;
11 use memmap::{Mmap, MmapOptions};
11 use memmap::{Mmap, MmapOptions};
12 use micro_timer::timed;
12 use micro_timer::timed;
13 use zstd;
13 use zstd;
14
14
15 use super::index::Index;
15 use super::index::Index;
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
17 use super::patch;
17 use super::patch;
18 use crate::revlog::Revision;
18 use crate::revlog::Revision;
19
19
20 pub enum RevlogError {
20 pub enum RevlogError {
21 IoError(std::io::Error),
21 IoError(std::io::Error),
22 UnsuportedVersion(u16),
22 UnsuportedVersion(u16),
23 InvalidRevision,
23 InvalidRevision,
24 Corrupted,
24 Corrupted,
25 UnknowDataFormat(u8),
25 UnknowDataFormat(u8),
26 }
26 }
27
27
28 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
28 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
29 let file = File::open(path)?;
29 let file = File::open(path)?;
30 let mmap = unsafe { MmapOptions::new().map(&file) }?;
30 let mmap = unsafe { MmapOptions::new().map(&file) }?;
31 Ok(mmap)
31 Ok(mmap)
32 }
32 }
33
33
34 /// Read only implementation of revlog.
34 /// Read only implementation of revlog.
35 pub struct Revlog {
35 pub struct Revlog {
36 /// When index and data are not interleaved: bytes of the revlog index.
36 /// When index and data are not interleaved: bytes of the revlog index.
37 /// When index and data are interleaved: bytes of the revlog index and
37 /// When index and data are interleaved: bytes of the revlog index and
38 /// data.
38 /// data.
39 index_bytes: Box<dyn Deref<Target = [u8]> + Send>,
39 index_bytes: Box<dyn Deref<Target = [u8]> + Send>,
40 /// When index and data are not interleaved: bytes of the revlog data
40 /// When index and data are not interleaved: bytes of the revlog data
41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
42 }
42 }
43
43
44 impl Revlog {
44 impl Revlog {
45 /// Open a revlog index file.
45 /// Open a revlog index file.
46 ///
46 ///
47 /// It will also open the associated data file if index and data are not
47 /// It will also open the associated data file if index and data are not
48 /// interleaved.
48 /// interleaved.
49 #[timed]
49 #[timed]
50 pub fn open(index_path: &Path) -> Result<Self, RevlogError> {
50 pub fn open(index_path: &Path) -> Result<Self, RevlogError> {
51 let index_mmap =
51 let index_mmap =
52 mmap_open(&index_path).map_err(RevlogError::IoError)?;
52 mmap_open(&index_path).map_err(RevlogError::IoError)?;
53
53
54 let version = get_version(&index_mmap);
54 let version = get_version(&index_mmap);
55 if version != 1 {
55 if version != 1 {
56 return Err(RevlogError::UnsuportedVersion(version));
56 return Err(RevlogError::UnsuportedVersion(version));
57 }
57 }
58
58
59 let is_inline = is_inline(&index_mmap);
59 let is_inline = is_inline(&index_mmap);
60
60
61 let index_bytes = Box::new(index_mmap);
61 let index_bytes = Box::new(index_mmap);
62
62
63 // TODO load data only when needed //
63 // TODO load data only when needed //
64 // type annotation required
64 // type annotation required
65 // won't recognize Mmap as Deref<Target = [u8]>
65 // won't recognize Mmap as Deref<Target = [u8]>
66 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
66 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
67 if is_inline {
67 if is_inline {
68 None
68 None
69 } else {
69 } else {
70 let data_path = index_path.with_extension("d");
70 let data_path = index_path.with_extension("d");
71 let data_mmap =
71 let data_mmap =
72 mmap_open(&data_path).map_err(RevlogError::IoError)?;
72 mmap_open(&data_path).map_err(RevlogError::IoError)?;
73 Some(Box::new(data_mmap))
73 Some(Box::new(data_mmap))
74 };
74 };
75
75
76 Ok(Revlog {
76 Ok(Revlog {
77 index_bytes,
77 index_bytes,
78 data_bytes,
78 data_bytes,
79 })
79 })
80 }
80 }
81
81
82 /// Return number of entries of the `Revlog`.
82 /// Return number of entries of the `Revlog`.
83 pub fn len(&self) -> usize {
83 pub fn len(&self) -> usize {
84 self.index().len()
84 self.index().len()
85 }
85 }
86
86
87 /// Returns `true` if the `Revlog` has zero `entries`.
87 /// Returns `true` if the `Revlog` has zero `entries`.
88 pub fn is_empty(&self) -> bool {
88 pub fn is_empty(&self) -> bool {
89 self.index().is_empty()
89 self.index().is_empty()
90 }
90 }
91
91
92 /// Return the full data associated to a node.
92 /// Return the full data associated to a node.
93 #[timed]
93 #[timed]
94 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
94 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
95 let index = self.index();
95 let index = self.index();
96 // This is brute force. But it is fast enough for now.
96 // This is brute force. But it is fast enough for now.
97 // Optimization will come later.
97 // Optimization will come later.
98 for rev in (0..self.len() as Revision).rev() {
98 for rev in (0..self.len() as Revision).rev() {
99 let index_entry =
99 let index_entry =
100 index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
100 index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
101 if node == index_entry.hash() {
101 if node == index_entry.hash() {
102 return Ok(rev);
102 return Ok(rev);
103 }
103 }
104 }
104 }
105 Err(RevlogError::InvalidRevision)
105 Err(RevlogError::InvalidRevision)
106 }
106 }
107
107
108 /// Return the full data associated to a revision.
108 /// Return the full data associated to a revision.
109 ///
109 ///
110 /// All entries required to build the final data out of deltas will be
110 /// All entries required to build the final data out of deltas will be
111 /// retrieved as needed, and the deltas will be applied to the inital
111 /// retrieved as needed, and the deltas will be applied to the inital
112 /// snapshot to rebuild the final data.
112 /// snapshot to rebuild the final data.
113 #[timed]
113 #[timed]
114 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
114 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
115 // Todo return -> Cow
115 // Todo return -> Cow
116 let mut entry = self.get_entry(rev)?;
116 let mut entry = self.get_entry(rev)?;
117 let mut delta_chain = vec![];
117 let mut delta_chain = vec![];
118 while let Some(base_rev) = entry.base_rev {
118 while let Some(base_rev) = entry.base_rev {
119 delta_chain.push(entry);
119 delta_chain.push(entry);
120 entry = self
120 entry = self
121 .get_entry(base_rev)
121 .get_entry(base_rev)
122 .map_err(|_| RevlogError::Corrupted)?;
122 .map_err(|_| RevlogError::Corrupted)?;
123 }
123 }
124
124
125 // TODO do not look twice in the index
125 // TODO do not look twice in the index
126 let index = self.index();
126 let index = self.index();
127 let index_entry =
127 let index_entry =
128 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
128 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
129
129
130 let data: Vec<u8> = if delta_chain.is_empty() {
130 let data: Vec<u8> = if delta_chain.is_empty() {
131 entry.data()?.into()
131 entry.data()?.into()
132 } else {
132 } else {
133 Revlog::build_data_from_deltas(entry, &delta_chain)?
133 Revlog::build_data_from_deltas(entry, &delta_chain)?
134 };
134 };
135
135
136 if self.check_hash(
136 if self.check_hash(
137 index_entry.p1(),
137 index_entry.p1(),
138 index_entry.p2(),
138 index_entry.p2(),
139 index_entry.hash(),
139 index_entry.hash(),
140 &data,
140 &data,
141 ) {
141 ) {
142 Ok(data)
142 Ok(data)
143 } else {
143 } else {
144 Err(RevlogError::Corrupted)
144 Err(RevlogError::Corrupted)
145 }
145 }
146 }
146 }
147
147
148 /// Check the hash of some given data against the recorded hash.
148 /// Check the hash of some given data against the recorded hash.
149 pub fn check_hash(
149 pub fn check_hash(
150 &self,
150 &self,
151 p1: Revision,
151 p1: Revision,
152 p2: Revision,
152 p2: Revision,
153 expected: &[u8],
153 expected: &[u8],
154 data: &[u8],
154 data: &[u8],
155 ) -> bool {
155 ) -> bool {
156 let index = self.index();
156 let index = self.index();
157 let e1 = index.get_entry(p1);
157 let e1 = index.get_entry(p1);
158 let h1 = match e1 {
158 let h1 = match e1 {
159 Some(ref entry) => entry.hash(),
159 Some(ref entry) => entry.hash(),
160 None => &NULL_NODE_ID,
160 None => &NULL_NODE_ID,
161 };
161 };
162 let e2 = index.get_entry(p2);
162 let e2 = index.get_entry(p2);
163 let h2 = match e2 {
163 let h2 = match e2 {
164 Some(ref entry) => entry.hash(),
164 Some(ref entry) => entry.hash(),
165 None => &NULL_NODE_ID,
165 None => &NULL_NODE_ID,
166 };
166 };
167
167
168 hash(data, &h1, &h2).as_slice() == expected
168 hash(data, &h1, &h2).as_slice() == expected
169 }
169 }
170
170
171 /// Build the full data of a revision out its snapshot
171 /// Build the full data of a revision out its snapshot
172 /// and its deltas.
172 /// and its deltas.
173 #[timed]
173 #[timed]
174 fn build_data_from_deltas(
174 fn build_data_from_deltas(
175 snapshot: RevlogEntry,
175 snapshot: RevlogEntry,
176 deltas: &[RevlogEntry],
176 deltas: &[RevlogEntry],
177 ) -> Result<Vec<u8>, RevlogError> {
177 ) -> Result<Vec<u8>, RevlogError> {
178 let snapshot = snapshot.data()?;
178 let snapshot = snapshot.data()?;
179 let deltas = deltas
179 let deltas = deltas
180 .iter()
180 .iter()
181 .rev()
181 .rev()
182 .map(RevlogEntry::data)
182 .map(RevlogEntry::data)
183 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
183 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
184 let patches: Vec<_> =
184 let patches: Vec<_> =
185 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
185 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
186 let patch = patch::fold_patch_lists(&patches);
186 let patch = patch::fold_patch_lists(&patches);
187 Ok(patch.apply(&snapshot))
187 Ok(patch.apply(&snapshot))
188 }
188 }
189
189
190 /// Return the revlog index.
190 /// Return the revlog index.
191 pub fn index(&self) -> Index {
191 pub fn index(&self) -> Index {
192 let is_inline = self.data_bytes.is_none();
192 let is_inline = self.data_bytes.is_none();
193 Index::new(&self.index_bytes, is_inline)
193 Index::new(&self.index_bytes, is_inline)
194 }
194 }
195
195
196 /// Return the revlog data.
196 /// Return the revlog data.
197 fn data(&self) -> &[u8] {
197 fn data(&self) -> &[u8] {
198 match self.data_bytes {
198 match self.data_bytes {
199 Some(ref data_bytes) => &data_bytes,
199 Some(ref data_bytes) => &data_bytes,
200 None => &self.index_bytes,
200 None => &self.index_bytes,
201 }
201 }
202 }
202 }
203
203
204 /// Get an entry of the revlog.
204 /// Get an entry of the revlog.
205 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
205 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
206 let index = self.index();
206 let index = self.index();
207 let index_entry =
207 let index_entry =
208 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
208 index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
209 let start = index_entry.offset();
209 let start = index_entry.offset();
210 let end = start + index_entry.compressed_len();
210 let end = start + index_entry.compressed_len();
211 let entry = RevlogEntry {
211 let entry = RevlogEntry {
212 rev,
212 rev,
213 bytes: &self.data()[start..end],
213 bytes: &self.data()[start..end],
214 compressed_len: index_entry.compressed_len(),
214 compressed_len: index_entry.compressed_len(),
215 uncompressed_len: index_entry.uncompressed_len(),
215 uncompressed_len: index_entry.uncompressed_len(),
216 base_rev: if index_entry.base_revision() == rev {
216 base_rev: if index_entry.base_revision() == rev {
217 None
217 None
218 } else {
218 } else {
219 Some(index_entry.base_revision())
219 Some(index_entry.base_revision())
220 },
220 },
221 };
221 };
222 Ok(entry)
222 Ok(entry)
223 }
223 }
224 }
224 }
225
225
226 /// The revlog entry's bytes and the necessary informations to extract
226 /// The revlog entry's bytes and the necessary informations to extract
227 /// the entry's data.
227 /// the entry's data.
228 #[derive(Debug)]
228 #[derive(Debug)]
229 pub struct RevlogEntry<'a> {
229 pub struct RevlogEntry<'a> {
230 rev: Revision,
230 rev: Revision,
231 bytes: &'a [u8],
231 bytes: &'a [u8],
232 compressed_len: usize,
232 compressed_len: usize,
233 uncompressed_len: usize,
233 uncompressed_len: usize,
234 base_rev: Option<Revision>,
234 base_rev: Option<Revision>,
235 }
235 }
236
236
237 impl<'a> RevlogEntry<'a> {
237 impl<'a> RevlogEntry<'a> {
238 /// Extract the data contained in the entry.
238 /// Extract the data contained in the entry.
239 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
239 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
240 if self.bytes.is_empty() {
240 if self.bytes.is_empty() {
241 return Ok(Cow::Borrowed(&[]));
241 return Ok(Cow::Borrowed(&[]));
242 }
242 }
243 match self.bytes[0] {
243 match self.bytes[0] {
244 // Revision data is the entirety of the entry, including this
244 // Revision data is the entirety of the entry, including this
245 // header.
245 // header.
246 b'\0' => Ok(Cow::Borrowed(self.bytes)),
246 b'\0' => Ok(Cow::Borrowed(self.bytes)),
247 // Raw revision data follows.
247 // Raw revision data follows.
248 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
248 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
249 // zlib (RFC 1950) data.
249 // zlib (RFC 1950) data.
250 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data())),
250 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
251 // zstd data.
251 // zstd data.
252 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data())),
252 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
253 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
253 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
254 }
254 }
255 }
255 }
256
256
257 fn uncompressed_zlib_data(&self) -> Vec<u8> {
257 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
258 let mut decoder = ZlibDecoder::new(self.bytes);
258 let mut decoder = ZlibDecoder::new(self.bytes);
259 if self.is_delta() {
259 if self.is_delta() {
260 let mut buf = Vec::with_capacity(self.compressed_len);
260 let mut buf = Vec::with_capacity(self.compressed_len);
261 decoder.read_to_end(&mut buf).expect("corrupted zlib data");
261 decoder
262 buf
262 .read_to_end(&mut buf)
263 .or(Err(RevlogError::Corrupted))?;
264 Ok(buf)
263 } else {
265 } else {
264 let mut buf = vec![0; self.uncompressed_len];
266 let mut buf = vec![0; self.uncompressed_len];
265 decoder.read_exact(&mut buf).expect("corrupted zlib data");
267 decoder
266 buf
268 .read_exact(&mut buf)
269 .or(Err(RevlogError::Corrupted))?;
270 Ok(buf)
267 }
271 }
268 }
272 }
269
273
270 fn uncompressed_zstd_data(&self) -> Vec<u8> {
274 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
271 if self.is_delta() {
275 if self.is_delta() {
272 let mut buf = Vec::with_capacity(self.compressed_len);
276 let mut buf = Vec::with_capacity(self.compressed_len);
273 zstd::stream::copy_decode(self.bytes, &mut buf)
277 zstd::stream::copy_decode(self.bytes, &mut buf)
274 .expect("corrupted zstd data");
278 .or(Err(RevlogError::Corrupted))?;
275 buf
279 Ok(buf)
276 } else {
280 } else {
277 let mut buf = vec![0; self.uncompressed_len];
281 let mut buf = vec![0; self.uncompressed_len];
278 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
282 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
279 .expect("corrupted zstd data");
283 .or(Err(RevlogError::Corrupted))?;
280 assert_eq!(len, self.uncompressed_len, "corrupted zstd data");
284 if len != self.uncompressed_len {
281 buf
285 Err(RevlogError::Corrupted)
286 } else {
287 Ok(buf)
288 }
282 }
289 }
283 }
290 }
284
291
285 /// Tell if the entry is a snapshot or a delta
292 /// Tell if the entry is a snapshot or a delta
286 /// (influences on decompression).
293 /// (influences on decompression).
287 fn is_delta(&self) -> bool {
294 fn is_delta(&self) -> bool {
288 self.base_rev.is_some()
295 self.base_rev.is_some()
289 }
296 }
290 }
297 }
291
298
292 /// Value of the inline flag.
299 /// Value of the inline flag.
293 pub fn is_inline(index_bytes: &[u8]) -> bool {
300 pub fn is_inline(index_bytes: &[u8]) -> bool {
294 match &index_bytes[0..=1] {
301 match &index_bytes[0..=1] {
295 [0, 0] | [0, 2] => false,
302 [0, 0] | [0, 2] => false,
296 _ => true,
303 _ => true,
297 }
304 }
298 }
305 }
299
306
300 /// Format version of the revlog.
307 /// Format version of the revlog.
301 pub fn get_version(index_bytes: &[u8]) -> u16 {
308 pub fn get_version(index_bytes: &[u8]) -> u16 {
302 BigEndian::read_u16(&index_bytes[2..=3])
309 BigEndian::read_u16(&index_bytes[2..=3])
303 }
310 }
304
311
305 /// Calculate the hash of a revision given its data and its parents.
312 /// Calculate the hash of a revision given its data and its parents.
306 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
313 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
307 let mut hasher = Sha1::new();
314 let mut hasher = Sha1::new();
308 let (a, b) = (p1_hash, p2_hash);
315 let (a, b) = (p1_hash, p2_hash);
309 if a > b {
316 if a > b {
310 hasher.input(b);
317 hasher.input(b);
311 hasher.input(a);
318 hasher.input(a);
312 } else {
319 } else {
313 hasher.input(a);
320 hasher.input(a);
314 hasher.input(b);
321 hasher.input(b);
315 }
322 }
316 hasher.input(data);
323 hasher.input(data);
317 let mut hash = vec![0; NODE_BYTES_LENGTH];
324 let mut hash = vec![0; NODE_BYTES_LENGTH];
318 hasher.result(&mut hash);
325 hasher.result(&mut hash);
319 hash
326 hash
320 }
327 }
321
328
322 #[cfg(test)]
329 #[cfg(test)]
323 mod tests {
330 mod tests {
324 use super::*;
331 use super::*;
325
332
326 use super::super::index::IndexEntryBuilder;
333 use super::super::index::IndexEntryBuilder;
327
334
328 #[cfg(test)]
335 #[cfg(test)]
329 pub struct RevlogBuilder {
336 pub struct RevlogBuilder {
330 version: u16,
337 version: u16,
331 is_general_delta: bool,
338 is_general_delta: bool,
332 is_inline: bool,
339 is_inline: bool,
333 offset: usize,
340 offset: usize,
334 index: Vec<Vec<u8>>,
341 index: Vec<Vec<u8>>,
335 data: Vec<Vec<u8>>,
342 data: Vec<Vec<u8>>,
336 }
343 }
337
344
338 #[cfg(test)]
345 #[cfg(test)]
339 impl RevlogBuilder {
346 impl RevlogBuilder {
340 pub fn new() -> Self {
347 pub fn new() -> Self {
341 Self {
348 Self {
342 version: 2,
349 version: 2,
343 is_inline: false,
350 is_inline: false,
344 is_general_delta: true,
351 is_general_delta: true,
345 offset: 0,
352 offset: 0,
346 index: vec![],
353 index: vec![],
347 data: vec![],
354 data: vec![],
348 }
355 }
349 }
356 }
350
357
351 pub fn with_inline(&mut self, value: bool) -> &mut Self {
358 pub fn with_inline(&mut self, value: bool) -> &mut Self {
352 self.is_inline = value;
359 self.is_inline = value;
353 self
360 self
354 }
361 }
355
362
356 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
363 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
357 self.is_general_delta = value;
364 self.is_general_delta = value;
358 self
365 self
359 }
366 }
360
367
361 pub fn with_version(&mut self, value: u16) -> &mut Self {
368 pub fn with_version(&mut self, value: u16) -> &mut Self {
362 self.version = value;
369 self.version = value;
363 self
370 self
364 }
371 }
365
372
366 pub fn push(
373 pub fn push(
367 &mut self,
374 &mut self,
368 mut index: IndexEntryBuilder,
375 mut index: IndexEntryBuilder,
369 data: Vec<u8>,
376 data: Vec<u8>,
370 ) -> &mut Self {
377 ) -> &mut Self {
371 if self.index.is_empty() {
378 if self.index.is_empty() {
372 index.is_first(true);
379 index.is_first(true);
373 index.with_general_delta(self.is_general_delta);
380 index.with_general_delta(self.is_general_delta);
374 index.with_inline(self.is_inline);
381 index.with_inline(self.is_inline);
375 index.with_version(self.version);
382 index.with_version(self.version);
376 } else {
383 } else {
377 index.with_offset(self.offset);
384 index.with_offset(self.offset);
378 }
385 }
379 self.index.push(index.build());
386 self.index.push(index.build());
380 self.offset += data.len();
387 self.offset += data.len();
381 self.data.push(data);
388 self.data.push(data);
382 self
389 self
383 }
390 }
384
391
385 pub fn build_inline(&self) -> Vec<u8> {
392 pub fn build_inline(&self) -> Vec<u8> {
386 let mut bytes =
393 let mut bytes =
387 Vec::with_capacity(self.index.len() + self.data.len());
394 Vec::with_capacity(self.index.len() + self.data.len());
388 for (index, data) in self.index.iter().zip(self.data.iter()) {
395 for (index, data) in self.index.iter().zip(self.data.iter()) {
389 bytes.extend(index);
396 bytes.extend(index);
390 bytes.extend(data);
397 bytes.extend(data);
391 }
398 }
392 bytes
399 bytes
393 }
400 }
394 }
401 }
395
402
396 #[test]
403 #[test]
397 fn is_not_inline_when_no_inline_flag_test() {
404 fn is_not_inline_when_no_inline_flag_test() {
398 let bytes = RevlogBuilder::new()
405 let bytes = RevlogBuilder::new()
399 .with_general_delta(false)
406 .with_general_delta(false)
400 .with_inline(false)
407 .with_inline(false)
401 .push(IndexEntryBuilder::new(), vec![])
408 .push(IndexEntryBuilder::new(), vec![])
402 .build_inline();
409 .build_inline();
403
410
404 assert_eq!(is_inline(&bytes), false)
411 assert_eq!(is_inline(&bytes), false)
405 }
412 }
406
413
407 #[test]
414 #[test]
408 fn is_inline_when_inline_flag_test() {
415 fn is_inline_when_inline_flag_test() {
409 let bytes = RevlogBuilder::new()
416 let bytes = RevlogBuilder::new()
410 .with_general_delta(false)
417 .with_general_delta(false)
411 .with_inline(true)
418 .with_inline(true)
412 .push(IndexEntryBuilder::new(), vec![])
419 .push(IndexEntryBuilder::new(), vec![])
413 .build_inline();
420 .build_inline();
414
421
415 assert_eq!(is_inline(&bytes), true)
422 assert_eq!(is_inline(&bytes), true)
416 }
423 }
417
424
418 #[test]
425 #[test]
419 fn is_inline_when_inline_and_generaldelta_flags_test() {
426 fn is_inline_when_inline_and_generaldelta_flags_test() {
420 let bytes = RevlogBuilder::new()
427 let bytes = RevlogBuilder::new()
421 .with_general_delta(true)
428 .with_general_delta(true)
422 .with_inline(true)
429 .with_inline(true)
423 .push(IndexEntryBuilder::new(), vec![])
430 .push(IndexEntryBuilder::new(), vec![])
424 .build_inline();
431 .build_inline();
425
432
426 assert_eq!(is_inline(&bytes), true)
433 assert_eq!(is_inline(&bytes), true)
427 }
434 }
428
435
429 #[test]
436 #[test]
430 fn version_test() {
437 fn version_test() {
431 let bytes = RevlogBuilder::new()
438 let bytes = RevlogBuilder::new()
432 .with_version(1)
439 .with_version(1)
433 .push(IndexEntryBuilder::new(), vec![])
440 .push(IndexEntryBuilder::new(), vec![])
434 .build_inline();
441 .build_inline();
435
442
436 assert_eq!(get_version(&bytes), 1)
443 assert_eq!(get_version(&bytes), 1)
437 }
444 }
438 }
445 }
General Comments 0
You need to be logged in to leave comments. Login now