Show More
@@ -17,8 +17,10 b' use super::{' | |||
|
17 | 17 | }; |
|
18 | 18 | |
|
19 | 19 | use std::fmt; |
|
20 | use std::mem; | |
|
20 | 21 | use std::ops::Deref; |
|
21 | 22 | use std::ops::Index; |
|
23 | use std::slice; | |
|
22 | 24 | |
|
23 | 25 | #[derive(Debug, PartialEq)] |
|
24 | 26 | pub enum NodeMapError { |
@@ -172,20 +174,42 b' impl From<Element> for RawElement {' | |||
|
172 | 174 | /// represented at all, because we want an immutable empty nodetree |
|
173 | 175 | /// to be valid. |
|
174 | 176 | |
|
175 |
#[derive(Clone |
|
|
176 |
pub struct Block([ |
|
|
177 | #[derive(Copy, Clone)] | |
|
178 | pub struct Block([u8; BLOCK_SIZE]); | |
|
179 | ||
|
180 | /// Not derivable for arrays of length >32 until const generics are stable | |
|
181 | impl PartialEq for Block { | |
|
182 | fn eq(&self, other: &Self) -> bool { | |
|
183 | &self.0[..] == &other.0[..] | |
|
184 | } | |
|
185 | } | |
|
186 | ||
|
187 | pub const BLOCK_SIZE: usize = 64; | |
|
177 | 188 | |
|
178 | 189 | impl Block { |
|
179 | 190 | fn new() -> Self { |
|
180 | Block([-1; 16]) | |
|
191 | // -1 in 2's complement to create an absent node | |
|
192 | let byte: u8 = 255; | |
|
193 | Block([byte; BLOCK_SIZE]) | |
|
181 | 194 | } |
|
182 | 195 | |
|
183 | 196 | fn get(&self, nybble: u8) -> Element { |
|
184 | Element::from(RawElement::from_be(self.0[nybble as usize])) | |
|
197 | let index = nybble as usize * mem::size_of::<RawElement>(); | |
|
198 | Element::from(RawElement::from_be_bytes([ | |
|
199 | self.0[index], | |
|
200 | self.0[index + 1], | |
|
201 | self.0[index + 2], | |
|
202 | self.0[index + 3], | |
|
203 | ])) | |
|
185 | 204 | } |
|
186 | 205 | |
|
187 | 206 | fn set(&mut self, nybble: u8, element: Element) { |
|
188 |
|
|
|
207 | let values = RawElement::to_be_bytes(element.into()); | |
|
208 | let index = nybble as usize * mem::size_of::<RawElement>(); | |
|
209 | self.0[index] = values[0]; | |
|
210 | self.0[index + 1] = values[1]; | |
|
211 | self.0[index + 2] = values[2]; | |
|
212 | self.0[index + 3] = values[3]; | |
|
189 | 213 | } |
|
190 | 214 | } |
|
191 | 215 | |
@@ -230,9 +254,9 b' impl Index<usize> for NodeTree {' | |||
|
230 | 254 | } |
|
231 | 255 | |
|
232 | 256 | /// Return `None` unless the `Node` for `rev` has given prefix in `index`. |
|
233 |
fn has_prefix_or_none |
|
|
257 | fn has_prefix_or_none( | |
|
234 | 258 | idx: &impl RevlogIndex, |
|
235 |
prefix: NodePrefixRef |
|
|
259 | prefix: NodePrefixRef, | |
|
236 | 260 | rev: Revision, |
|
237 | 261 | ) -> Result<Option<Revision>, NodeMapError> { |
|
238 | 262 | idx.node(rev) |
@@ -262,6 +286,67 b' impl NodeTree {' | |||
|
262 | 286 | } |
|
263 | 287 | } |
|
264 | 288 | |
|
289 | /// Create from an opaque bunch of bytes | |
|
290 | /// | |
|
291 | /// The created `NodeTreeBytes` from `buffer`, | |
|
292 | /// of which exactly `amount` bytes are used. | |
|
293 | /// | |
|
294 | /// - `buffer` could be derived from `PyBuffer` and `Mmap` objects. | |
|
295 | /// - `offset` allows for the final file format to include fixed data | |
|
296 | /// (generation number, behavioural flags) | |
|
297 | /// - `amount` is expressed in bytes, and is not automatically derived from | |
|
298 | /// `bytes`, so that a caller that manages them atomically can perform | |
|
299 | /// temporary disk serializations and still rollback easily if needed. | |
|
300 | /// First use-case for this would be to support Mercurial shell hooks. | |
|
301 | /// | |
|
302 | /// panics if `buffer` is smaller than `amount` | |
|
303 | pub fn load_bytes( | |
|
304 | bytes: Box<dyn Deref<Target = [u8]> + Send>, | |
|
305 | amount: usize, | |
|
306 | ) -> Self { | |
|
307 | NodeTree::new(Box::new(NodeTreeBytes::new(bytes, amount))) | |
|
308 | } | |
|
309 | ||
|
310 | /// Retrieve added `Block` and the original immutable data | |
|
311 | pub fn into_readonly_and_added( | |
|
312 | self, | |
|
313 | ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<Block>) { | |
|
314 | let mut vec = self.growable; | |
|
315 | let readonly = self.readonly; | |
|
316 | if readonly.last() != Some(&self.root) { | |
|
317 | vec.push(self.root); | |
|
318 | } | |
|
319 | (readonly, vec) | |
|
320 | } | |
|
321 | ||
|
322 | /// Retrieve added `Blocks` as bytes, ready to be written to persistent | |
|
323 | /// storage | |
|
324 | pub fn into_readonly_and_added_bytes( | |
|
325 | self, | |
|
326 | ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<u8>) { | |
|
327 | let (readonly, vec) = self.into_readonly_and_added(); | |
|
328 | // Prevent running `v`'s destructor so we are in complete control | |
|
329 | // of the allocation. | |
|
330 | let vec = mem::ManuallyDrop::new(vec); | |
|
331 | ||
|
332 | // Transmute the `Vec<Block>` to a `Vec<u8>`. Blocks are contiguous | |
|
333 | // bytes, so this is perfectly safe. | |
|
334 | let bytes = unsafe { | |
|
335 | // Assert that `Block` hasn't been changed and has no padding | |
|
336 | let _: [u8; 4 * BLOCK_SIZE] = | |
|
337 | std::mem::transmute([Block::new(); 4]); | |
|
338 | ||
|
339 | // /!\ Any use of `vec` after this is use-after-free. | |
|
340 | // TODO: use `into_raw_parts` once stabilized | |
|
341 | Vec::from_raw_parts( | |
|
342 | vec.as_ptr() as *mut u8, | |
|
343 | vec.len() * BLOCK_SIZE, | |
|
344 | vec.capacity() * BLOCK_SIZE, | |
|
345 | ) | |
|
346 | }; | |
|
347 | (readonly, bytes) | |
|
348 | } | |
|
349 | ||
|
265 | 350 | /// Total number of blocks |
|
266 | 351 | fn len(&self) -> usize { |
|
267 | 352 | self.readonly.len() + self.growable.len() + 1 |
@@ -410,6 +495,38 b' impl NodeTree {' | |||
|
410 | 495 | } |
|
411 | 496 | } |
|
412 | 497 | |
|
498 | pub struct NodeTreeBytes { | |
|
499 | buffer: Box<dyn Deref<Target = [u8]> + Send>, | |
|
500 | len_in_blocks: usize, | |
|
501 | } | |
|
502 | ||
|
503 | impl NodeTreeBytes { | |
|
504 | fn new( | |
|
505 | buffer: Box<dyn Deref<Target = [u8]> + Send>, | |
|
506 | amount: usize, | |
|
507 | ) -> Self { | |
|
508 | assert!(buffer.len() >= amount); | |
|
509 | let len_in_blocks = amount / BLOCK_SIZE; | |
|
510 | NodeTreeBytes { | |
|
511 | buffer, | |
|
512 | len_in_blocks, | |
|
513 | } | |
|
514 | } | |
|
515 | } | |
|
516 | ||
|
517 | impl Deref for NodeTreeBytes { | |
|
518 | type Target = [Block]; | |
|
519 | ||
|
520 | fn deref(&self) -> &[Block] { | |
|
521 | unsafe { | |
|
522 | slice::from_raw_parts( | |
|
523 | (&self.buffer).as_ptr() as *const Block, | |
|
524 | self.len_in_blocks, | |
|
525 | ) | |
|
526 | } | |
|
527 | } | |
|
528 | } | |
|
529 | ||
|
413 | 530 | struct NodeTreeVisitor<'n, 'p> { |
|
414 | 531 | nt: &'n NodeTree, |
|
415 | 532 | prefix: NodePrefixRef<'p>, |
@@ -539,12 +656,15 b' mod tests {' | |||
|
539 | 656 | |
|
540 | 657 | #[test] |
|
541 | 658 | fn test_raw_block() { |
|
542 |
let mut raw = [ |
|
|
543 | raw[0] = 0; | |
|
544 | raw[1] = RawElement::to_be(15); | |
|
545 | raw[2] = RawElement::to_be(-2); | |
|
546 | raw[3] = RawElement::to_be(-1); | |
|
547 | raw[4] = RawElement::to_be(-3); | |
|
659 | let mut raw = [255u8; 64]; | |
|
660 | ||
|
661 | let mut counter = 0; | |
|
662 | for val in [0, 15, -2, -1, -3].iter() { | |
|
663 | for byte in RawElement::to_be_bytes(*val).iter() { | |
|
664 | raw[counter] = *byte; | |
|
665 | counter += 1; | |
|
666 | } | |
|
667 | } | |
|
548 | 668 | let block = Block(raw); |
|
549 | 669 | assert_eq!(block.get(0), Element::Block(0)); |
|
550 | 670 | assert_eq!(block.get(1), Element::Block(15)); |
@@ -787,4 +907,30 b' mod tests {' | |||
|
787 | 907 | |
|
788 | 908 | Ok(()) |
|
789 | 909 | } |
|
910 | ||
|
911 | #[test] | |
|
912 | fn test_into_added_empty() { | |
|
913 | assert!(sample_nodetree().into_readonly_and_added().1.is_empty()); | |
|
914 | assert!(sample_nodetree() | |
|
915 | .into_readonly_and_added_bytes() | |
|
916 | .1 | |
|
917 | .is_empty()); | |
|
918 | } | |
|
919 | ||
|
920 | #[test] | |
|
921 | fn test_into_added_bytes() -> Result<(), NodeMapError> { | |
|
922 | let mut idx = TestNtIndex::new(); | |
|
923 | idx.insert(0, "1234")?; | |
|
924 | let mut idx = idx.commit(); | |
|
925 | idx.insert(4, "cafe")?; | |
|
926 | let (_, bytes) = idx.nt.into_readonly_and_added_bytes(); | |
|
927 | ||
|
928 | // only the root block has been changed | |
|
929 | assert_eq!(bytes.len(), BLOCK_SIZE); | |
|
930 | // big endian for -2 | |
|
931 | assert_eq!(&bytes[4..2 * 4], [255, 255, 255, 254]); | |
|
932 | // big endian for -6 | |
|
933 | assert_eq!(&bytes[12 * 4..13 * 4], [255, 255, 255, 250]); | |
|
934 | Ok(()) | |
|
935 | } | |
|
790 | 936 | } |
General Comments 0
You need to be logged in to leave comments.
Login now