##// END OF EJS Templates
hg-core: Explain offset override of first revision...
Antoine cezar -
r46165:f38adf8b default
parent child Browse files
Show More
@@ -1,330 +1,331 b''
1 1 use byteorder::{BigEndian, ByteOrder};
2 2
3 3 use crate::revlog::{Revision, NULL_REVISION};
4 4
5 5 pub const INDEX_ENTRY_SIZE: usize = 64;
6 6
7 7 /// A Revlog index
8 8 #[derive(Debug)]
9 9 pub struct Index<'a> {
10 10 bytes: &'a [u8],
11 11 /// Offsets of starts of index blocks.
12 12 /// Only needed when the index is interleaved with data.
13 13 offsets: Option<Vec<usize>>,
14 14 }
15 15
16 16 impl<'a> Index<'a> {
17 17 /// Create an index from bytes.
18 18 /// Calculate the start of each entry when is_inline is true.
19 19 pub fn new(bytes: &'a [u8], is_inline: bool) -> Self {
20 20 if is_inline {
21 21 let mut offset: usize = 0;
22 22 let mut offsets = Vec::new();
23 23
24 24 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
25 25 offsets.push(offset);
26 26 let end = offset + INDEX_ENTRY_SIZE;
27 27 let entry = IndexEntry {
28 28 bytes: &bytes[offset..end],
29 29 offset_override: None,
30 30 };
31 31
32 32 offset += INDEX_ENTRY_SIZE + entry.compressed_len();
33 33 }
34 34
35 35 Self {
36 36 bytes,
37 37 offsets: Some(offsets),
38 38 }
39 39 } else {
40 40 Self {
41 41 bytes,
42 42 offsets: None,
43 43 }
44 44 }
45 45 }
46 46
47 47 /// Return number of entries of the revlog index.
48 48 pub fn len(&self) -> usize {
49 49 if let Some(offsets) = &self.offsets {
50 50 offsets.len()
51 51 } else {
52 52 self.bytes.len() / INDEX_ENTRY_SIZE
53 53 }
54 54 }
55 55
56 56 /// Returns `true` if the `Index` has zero `entries`.
57 57 pub fn is_empty(&self) -> bool {
58 58 self.len() == 0
59 59 }
60 60
61 61 /// Return the index entry corresponding to the given revision if it
62 62 /// exists.
63 63 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
64 64 if rev == NULL_REVISION {
65 65 return None;
66 66 }
67 67 if let Some(offsets) = &self.offsets {
68 68 self.get_entry_inline(rev, offsets)
69 69 } else {
70 70 self.get_entry_separated(rev)
71 71 }
72 72 }
73 73
74 74 fn get_entry_inline(
75 75 &self,
76 76 rev: Revision,
77 77 offsets: &[usize],
78 78 ) -> Option<IndexEntry> {
79 79 let start = *offsets.get(rev as usize)?;
80 80 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
81 81 let bytes = &self.bytes[start..end];
82 82
83 83 // See IndexEntry for an explanation of this override.
84 84 let offset_override = Some(end);
85 85
86 86 Some(IndexEntry {
87 87 bytes,
88 88 offset_override,
89 89 })
90 90 }
91 91
92 92 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
93 93 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
94 94 if rev as usize >= max_rev {
95 95 return None;
96 96 }
97 97 let start = rev as usize * INDEX_ENTRY_SIZE;
98 98 let end = start + INDEX_ENTRY_SIZE;
99 99 let bytes = &self.bytes[start..end];
100 100
101 // See IndexEntry for an explanation of this override.
101 // Override the offset of the first revision as its bytes are used
102 // for the index's metadata (saving space because it is always 0)
102 103 let offset_override = match rev {
103 104 0 => Some(0),
104 105 _ => None,
105 106 };
106 107
107 108 Some(IndexEntry {
108 109 bytes,
109 110 offset_override,
110 111 })
111 112 }
112 113 }
113 114
114 115 #[derive(Debug)]
115 116 pub struct IndexEntry<'a> {
116 117 bytes: &'a [u8],
117 118 /// Allows to override the offset value of the entry.
118 119 ///
119 120 /// For interleaved index and data, the offset stored in the index
120 121 /// corresponds to the separated data offset.
121 122 /// It has to be overridden with the actual offset in the interleaved
122 123 /// index which is just after the index block.
123 124 ///
124 125 /// For separated index and data, the offset stored in the first index
125 126 /// entry is mixed with the index headers.
126 127 /// It has to be overridden with 0.
127 128 offset_override: Option<usize>,
128 129 }
129 130
130 131 impl<'a> IndexEntry<'a> {
131 132 /// Return the offset of the data if not overridden by offset_override.
132 133 pub fn offset(&self) -> usize {
133 134 if let Some(offset_override) = self.offset_override {
134 135 offset_override
135 136 } else {
136 137 let mut bytes = [0; 8];
137 138 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
138 139 BigEndian::read_u64(&bytes[..]) as usize
139 140 }
140 141 }
141 142
142 143 /// Return the compressed length of the data.
143 144 pub fn compressed_len(&self) -> usize {
144 145 BigEndian::read_u32(&self.bytes[8..=11]) as usize
145 146 }
146 147
147 148 /// Return the uncompressed length of the data.
148 149 pub fn uncompressed_len(&self) -> usize {
149 150 BigEndian::read_u32(&self.bytes[12..=15]) as usize
150 151 }
151 152
152 153 /// Return the revision upon which the data has been derived.
153 154 pub fn base_revision(&self) -> Revision {
154 155 // TODO Maybe return an Option when base_revision == rev?
155 156 // Requires to add rev to IndexEntry
156 157
157 158 BigEndian::read_i32(&self.bytes[16..])
158 159 }
159 160
160 161 pub fn p1(&self) -> Revision {
161 162 BigEndian::read_i32(&self.bytes[24..])
162 163 }
163 164
164 165 pub fn p2(&self) -> Revision {
165 166 BigEndian::read_i32(&self.bytes[28..])
166 167 }
167 168
168 169 /// Return the hash of revision's full text.
169 170 ///
170 171 /// Currently, SHA-1 is used and only the first 20 bytes of this field
171 172 /// are used.
172 173 pub fn hash(&self) -> &[u8] {
173 174 &self.bytes[32..52]
174 175 }
175 176 }
176 177
177 178 #[cfg(test)]
178 179 mod tests {
179 180 use super::*;
180 181
181 182 #[cfg(test)]
182 183 #[derive(Debug, Copy, Clone)]
183 184 pub struct IndexEntryBuilder {
184 185 is_first: bool,
185 186 is_inline: bool,
186 187 is_general_delta: bool,
187 188 version: u16,
188 189 offset: usize,
189 190 compressed_len: usize,
190 191 uncompressed_len: usize,
191 192 base_revision: Revision,
192 193 }
193 194
194 195 #[cfg(test)]
195 196 impl IndexEntryBuilder {
196 197 pub fn new() -> Self {
197 198 Self {
198 199 is_first: false,
199 200 is_inline: false,
200 201 is_general_delta: true,
201 202 version: 2,
202 203 offset: 0,
203 204 compressed_len: 0,
204 205 uncompressed_len: 0,
205 206 base_revision: 0,
206 207 }
207 208 }
208 209
209 210 pub fn is_first(&mut self, value: bool) -> &mut Self {
210 211 self.is_first = value;
211 212 self
212 213 }
213 214
214 215 pub fn with_inline(&mut self, value: bool) -> &mut Self {
215 216 self.is_inline = value;
216 217 self
217 218 }
218 219
219 220 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
220 221 self.is_general_delta = value;
221 222 self
222 223 }
223 224
224 225 pub fn with_version(&mut self, value: u16) -> &mut Self {
225 226 self.version = value;
226 227 self
227 228 }
228 229
229 230 pub fn with_offset(&mut self, value: usize) -> &mut Self {
230 231 self.offset = value;
231 232 self
232 233 }
233 234
234 235 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
235 236 self.compressed_len = value;
236 237 self
237 238 }
238 239
239 240 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
240 241 self.uncompressed_len = value;
241 242 self
242 243 }
243 244
244 245 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
245 246 self.base_revision = value;
246 247 self
247 248 }
248 249
249 250 pub fn build(&self) -> Vec<u8> {
250 251 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
251 252 if self.is_first {
252 253 bytes.extend(&match (self.is_general_delta, self.is_inline) {
253 254 (false, false) => [0u8, 0],
254 255 (false, true) => [0u8, 1],
255 256 (true, false) => [0u8, 2],
256 257 (true, true) => [0u8, 3],
257 258 });
258 259 bytes.extend(&self.version.to_be_bytes());
259 260 // Remaining offset bytes.
260 261 bytes.extend(&[0u8; 2]);
261 262 } else {
262 263 // Offset is only 6 bytes will usize is 8.
263 264 bytes.extend(&self.offset.to_be_bytes()[2..]);
264 265 }
265 266 bytes.extend(&[0u8; 2]); // Revision flags.
266 267 bytes.extend(&self.compressed_len.to_be_bytes()[4..]);
267 268 bytes.extend(&self.uncompressed_len.to_be_bytes()[4..]);
268 269 bytes.extend(&self.base_revision.to_be_bytes());
269 270 bytes
270 271 }
271 272 }
272 273
273 274 #[test]
274 275 fn test_offset() {
275 276 let bytes = IndexEntryBuilder::new().with_offset(1).build();
276 277 let entry = IndexEntry {
277 278 bytes: &bytes,
278 279 offset_override: None,
279 280 };
280 281
281 282 assert_eq!(entry.offset(), 1)
282 283 }
283 284
284 285 #[test]
285 286 fn test_with_overridden_offset() {
286 287 let bytes = IndexEntryBuilder::new().with_offset(1).build();
287 288 let entry = IndexEntry {
288 289 bytes: &bytes,
289 290 offset_override: Some(2),
290 291 };
291 292
292 293 assert_eq!(entry.offset(), 2)
293 294 }
294 295
295 296 #[test]
296 297 fn test_compressed_len() {
297 298 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
298 299 let entry = IndexEntry {
299 300 bytes: &bytes,
300 301 offset_override: None,
301 302 };
302 303
303 304 assert_eq!(entry.compressed_len(), 1)
304 305 }
305 306
306 307 #[test]
307 308 fn test_uncompressed_len() {
308 309 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
309 310 let entry = IndexEntry {
310 311 bytes: &bytes,
311 312 offset_override: None,
312 313 };
313 314
314 315 assert_eq!(entry.uncompressed_len(), 1)
315 316 }
316 317
317 318 #[test]
318 319 fn test_base_revision() {
319 320 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
320 321 let entry = IndexEntry {
321 322 bytes: &bytes,
322 323 offset_override: None,
323 324 };
324 325
325 326 assert_eq!(entry.base_revision(), 1)
326 327 }
327 328 }
328 329
329 330 #[cfg(test)]
330 331 pub use tests::IndexEntryBuilder;
General Comments 0
You need to be logged in to leave comments. Login now