588 impl Writer<'_, '_> { |
588 impl Writer<'_, '_> { |
589 fn write_nodes( |
589 fn write_nodes( |
590 &mut self, |
590 &mut self, |
591 nodes: dirstate_map::ChildNodesRef, |
591 nodes: dirstate_map::ChildNodesRef, |
592 ) -> Result<ChildNodes, DirstateError> { |
592 ) -> Result<ChildNodes, DirstateError> { |
593 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration |
593 // Reuse already-written nodes if possible |
594 // order. Sort to enable binary search in the written file. |
594 if self.append { |
|
595 if let dirstate_map::ChildNodesRef::OnDisk(nodes_slice) = nodes { |
|
596 let start = self.offset_of(nodes_slice); |
|
597 let len = child_nodes_len_from_usize(nodes_slice.len()); |
|
598 return Ok(ChildNodes { start, len }); |
|
599 } |
|
600 } |
|
601 |
|
602 // `dirstate_map::ChildNodes::InMemory` contains a `HashMap` which has |
|
603 // undefined iteration order. Sort to enable binary search in the |
|
604 // written file. |
595 let nodes = nodes.sorted(); |
605 let nodes = nodes.sorted(); |
596 let nodes_len = nodes.len(); |
606 let nodes_len = nodes.len(); |
597 |
607 |
598 // First accumulate serialized nodes in a `Vec` |
608 // First accumulate serialized nodes in a `Vec` |
599 let mut on_disk_nodes = Vec::with_capacity(nodes_len); |
609 let mut on_disk_nodes = Vec::with_capacity(nodes_len); |
662 }) |
672 }) |
663 } |
673 } |
664 // … so we can write them contiguously, after writing everything else |
674 // … so we can write them contiguously, after writing everything else |
665 // they refer to. |
675 // they refer to. |
666 let start = self.current_offset(); |
676 let start = self.current_offset(); |
667 let len = u32::try_from(nodes_len) |
677 let len = child_nodes_len_from_usize(nodes_len); |
668 // Could only panic with over 4 billion nodes |
|
669 .expect("dirstate-v2 path length overflow") |
|
670 .into(); |
|
671 self.out.extend(on_disk_nodes.as_bytes()); |
678 self.out.extend(on_disk_nodes.as_bytes()); |
672 Ok(ChildNodes { start, len }) |
679 Ok(ChildNodes { start, len }) |
|
680 } |
|
681 |
|
682 /// Takes a slice of items within `on_disk` and returns its offset for the |
|
683 /// start of `on_disk`. |
|
684 /// |
|
685 /// Panics if the given slice is not within `on_disk`. |
|
686 fn offset_of<T>(&self, slice: &[T]) -> Offset |
|
687 where |
|
688 T: BytesCast, |
|
689 { |
|
690 fn address_range(slice: &[u8]) -> std::ops::RangeInclusive<usize> { |
|
691 let start = slice.as_ptr() as usize; |
|
692 let end = start + slice.len(); |
|
693 start..=end |
|
694 } |
|
695 let slice_addresses = address_range(slice.as_bytes()); |
|
696 let on_disk_addresses = address_range(self.dirstate_map.on_disk); |
|
697 assert!(on_disk_addresses.contains(slice_addresses.start())); |
|
698 assert!(on_disk_addresses.contains(slice_addresses.end())); |
|
699 let offset = slice_addresses.start() - on_disk_addresses.start(); |
|
700 offset_from_usize(offset) |
673 } |
701 } |
674 |
702 |
675 fn current_offset(&mut self) -> Offset { |
703 fn current_offset(&mut self) -> Offset { |
676 let mut offset = self.out.len(); |
704 let mut offset = self.out.len(); |
677 if self.append { |
705 if self.append { |
678 offset += self.dirstate_map.on_disk.len() |
706 offset += self.dirstate_map.on_disk.len() |
679 } |
707 } |
680 u32::try_from(offset) |
708 offset_from_usize(offset) |
681 // Could only panic for a dirstate file larger than 4 GiB |
|
682 .expect("dirstate-v2 offset overflow") |
|
683 .into() |
|
684 } |
709 } |
685 |
710 |
686 fn write_path(&mut self, slice: &[u8]) -> PathSlice { |
711 fn write_path(&mut self, slice: &[u8]) -> PathSlice { |
687 let start = self.current_offset(); |
712 let start = self.current_offset(); |
688 let len = u16::try_from(slice.len()) |
713 let len = path_len_from_usize(slice.len()); |
689 // Could only panic for paths over 64 KiB |
|
690 .expect("dirstate-v2 path length overflow") |
|
691 .into(); |
|
692 self.out.extend(slice.as_bytes()); |
714 self.out.extend(slice.as_bytes()); |
693 PathSlice { start, len } |
715 PathSlice { start, len } |
694 } |
716 } |
695 } |
717 } |
|
718 |
|
719 fn offset_from_usize(x: usize) -> Offset { |
|
720 u32::try_from(x) |
|
721 // Could only panic for a dirstate file larger than 4 GiB |
|
722 .expect("dirstate-v2 offset overflow") |
|
723 .into() |
|
724 } |
|
725 |
|
726 fn child_nodes_len_from_usize(x: usize) -> Size { |
|
727 u32::try_from(x) |
|
728 // Could only panic with over 4 billion nodes |
|
729 .expect("dirstate-v2 slice length overflow") |
|
730 .into() |
|
731 } |
|
732 |
|
733 fn path_len_from_usize(x: usize) -> PathSize { |
|
734 u16::try_from(x) |
|
735 // Could only panic for paths over 64 KiB |
|
736 .expect("dirstate-v2 path length overflow") |
|
737 .into() |
|
738 } |