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