542 Ok(()) |
542 Ok(()) |
543 } |
543 } |
544 recur(on_disk, root.root_nodes, &mut f) |
544 recur(on_disk, root.root_nodes, &mut f) |
545 } |
545 } |
546 |
546 |
|
547 /// Returns new data together with whether that data should be appended to the |
|
548 /// existing data file whose content is at `dirstate_map.on_disk` (true), |
|
549 /// instead of written to a new data file (false). |
547 pub(super) fn write( |
550 pub(super) fn write( |
548 dirstate_map: &mut DirstateMap, |
551 dirstate_map: &mut DirstateMap, |
549 ) -> Result<Vec<u8>, DirstateError> { |
552 can_append: bool, |
550 let root_len = std::mem::size_of::<Root>(); |
553 ) -> Result<(Vec<u8>, bool), DirstateError> { |
|
554 let append = can_append && dirstate_map.write_should_append(); |
551 |
555 |
552 // This ignores the space for paths, and for nodes without an entry. |
556 // This ignores the space for paths, and for nodes without an entry. |
553 // TODO: better estimate? Skip the `Vec` and write to a file directly? |
557 // TODO: better estimate? Skip the `Vec` and write to a file directly? |
554 let size_guess = root_len |
558 let size_guess = std::mem::size_of::<Root>() |
555 + std::mem::size_of::<Node>() |
559 + std::mem::size_of::<Node>() |
556 * dirstate_map.nodes_with_entry_count as usize; |
560 * dirstate_map.nodes_with_entry_count as usize; |
557 let mut out = Vec::with_capacity(size_guess); |
561 |
558 |
562 let mut writer = Writer { |
559 let root_nodes = |
563 dirstate_map, |
560 write_nodes(dirstate_map, dirstate_map.root.as_ref(), &mut out)?; |
564 append, |
|
565 out: Vec::with_capacity(size_guess), |
|
566 }; |
|
567 |
|
568 let root_nodes = writer.write_nodes(dirstate_map.root.as_ref())?; |
561 |
569 |
562 let root = Root { |
570 let root = Root { |
563 root_nodes, |
571 root_nodes, |
564 nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(), |
572 nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(), |
565 nodes_with_copy_source_count: dirstate_map |
573 nodes_with_copy_source_count: dirstate_map |
566 .nodes_with_copy_source_count |
574 .nodes_with_copy_source_count |
567 .into(), |
575 .into(), |
568 ignore_patterns_hash: dirstate_map.ignore_patterns_hash, |
576 ignore_patterns_hash: dirstate_map.ignore_patterns_hash, |
569 }; |
577 }; |
570 out.extend(root.as_bytes()); |
578 writer.out.extend(root.as_bytes()); |
571 Ok(out) |
579 Ok((writer.out, append)) |
572 } |
580 } |
573 |
581 |
574 fn write_nodes( |
582 struct Writer<'dmap, 'on_disk> { |
575 dirstate_map: &DirstateMap, |
583 dirstate_map: &'dmap DirstateMap<'on_disk>, |
576 nodes: dirstate_map::ChildNodesRef, |
584 append: bool, |
577 out: &mut Vec<u8>, |
585 out: Vec<u8>, |
578 ) -> Result<ChildNodes, DirstateError> { |
586 } |
579 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration |
587 |
580 // order. Sort to enable binary search in the written file. |
588 impl Writer<'_, '_> { |
581 let nodes = nodes.sorted(); |
589 fn write_nodes( |
582 let nodes_len = nodes.len(); |
590 &mut self, |
583 |
591 nodes: dirstate_map::ChildNodesRef, |
584 // First accumulate serialized nodes in a `Vec` |
592 ) -> Result<ChildNodes, DirstateError> { |
585 let mut on_disk_nodes = Vec::with_capacity(nodes_len); |
593 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration |
586 for node in nodes { |
594 // order. Sort to enable binary search in the written file. |
587 let children = write_nodes( |
595 let nodes = nodes.sorted(); |
588 dirstate_map, |
596 let nodes_len = nodes.len(); |
589 node.children(dirstate_map.on_disk)?, |
597 |
590 out, |
598 // First accumulate serialized nodes in a `Vec` |
591 )?; |
599 let mut on_disk_nodes = Vec::with_capacity(nodes_len); |
592 let full_path = node.full_path(dirstate_map.on_disk)?; |
600 for node in nodes { |
593 let full_path = write_path(full_path.as_bytes(), out); |
601 let children = |
594 let copy_source = |
602 self.write_nodes(node.children(self.dirstate_map.on_disk)?)?; |
595 if let Some(source) = node.copy_source(dirstate_map.on_disk)? { |
603 let full_path = node.full_path(self.dirstate_map.on_disk)?; |
596 write_path(source.as_bytes(), out) |
604 let full_path = self.write_path(full_path.as_bytes()); |
|
605 let copy_source = if let Some(source) = |
|
606 node.copy_source(self.dirstate_map.on_disk)? |
|
607 { |
|
608 self.write_path(source.as_bytes()) |
597 } else { |
609 } else { |
598 PathSlice { |
610 PathSlice { |
599 start: 0.into(), |
611 start: 0.into(), |
600 len: 0.into(), |
612 len: 0.into(), |
601 } |
613 } |
602 }; |
614 }; |
603 on_disk_nodes.push(match node { |
615 on_disk_nodes.push(match node { |
604 NodeRef::InMemory(path, node) => { |
616 NodeRef::InMemory(path, node) => { |
605 let (state, data) = match &node.data { |
617 let (state, data) = match &node.data { |
606 dirstate_map::NodeData::Entry(entry) => ( |
618 dirstate_map::NodeData::Entry(entry) => ( |
607 entry.state.into(), |
619 entry.state.into(), |
608 Entry { |
620 Entry { |
609 mode: entry.mode.into(), |
621 mode: entry.mode.into(), |
610 mtime: entry.mtime.into(), |
622 mtime: entry.mtime.into(), |
611 size: entry.size.into(), |
623 size: entry.size.into(), |
612 }, |
624 }, |
613 ), |
625 ), |
614 dirstate_map::NodeData::CachedDirectory { mtime } => { |
626 dirstate_map::NodeData::CachedDirectory { mtime } => { |
615 (b'd', Entry::from_timestamp(*mtime)) |
627 (b'd', Entry::from_timestamp(*mtime)) |
|
628 } |
|
629 dirstate_map::NodeData::None => ( |
|
630 b'\0', |
|
631 Entry { |
|
632 mode: 0.into(), |
|
633 mtime: 0.into(), |
|
634 size: 0.into(), |
|
635 }, |
|
636 ), |
|
637 }; |
|
638 Node { |
|
639 children, |
|
640 copy_source, |
|
641 full_path, |
|
642 base_name_start: u16::try_from(path.base_name_start()) |
|
643 // Could only panic for paths over 64 KiB |
|
644 .expect("dirstate-v2 path length overflow") |
|
645 .into(), |
|
646 descendants_with_entry_count: node |
|
647 .descendants_with_entry_count |
|
648 .into(), |
|
649 tracked_descendants_count: node |
|
650 .tracked_descendants_count |
|
651 .into(), |
|
652 state, |
|
653 data, |
616 } |
654 } |
617 dirstate_map::NodeData::None => ( |
655 } |
618 b'\0', |
656 NodeRef::OnDisk(node) => Node { |
619 Entry { |
|
620 mode: 0.into(), |
|
621 mtime: 0.into(), |
|
622 size: 0.into(), |
|
623 }, |
|
624 ), |
|
625 }; |
|
626 Node { |
|
627 children, |
657 children, |
628 copy_source, |
658 copy_source, |
629 full_path, |
659 full_path, |
630 base_name_start: u16::try_from(path.base_name_start()) |
660 ..*node |
631 // Could only panic for paths over 64 KiB |
661 }, |
632 .expect("dirstate-v2 path length overflow") |
662 }) |
633 .into(), |
663 } |
634 descendants_with_entry_count: node |
664 // … so we can write them contiguously, after writing everything else |
635 .descendants_with_entry_count |
665 // they refer to. |
636 .into(), |
666 let start = self.current_offset(); |
637 tracked_descendants_count: node |
667 let len = u32::try_from(nodes_len) |
638 .tracked_descendants_count |
668 // Could only panic with over 4 billion nodes |
639 .into(), |
669 .expect("dirstate-v2 path length overflow") |
640 state, |
670 .into(); |
641 data, |
671 self.out.extend(on_disk_nodes.as_bytes()); |
642 } |
672 Ok(ChildNodes { start, len }) |
643 } |
673 } |
644 NodeRef::OnDisk(node) => Node { |
674 |
645 children, |
675 fn current_offset(&mut self) -> Offset { |
646 copy_source, |
676 let mut offset = self.out.len(); |
647 full_path, |
677 if self.append { |
648 ..*node |
678 offset += self.dirstate_map.on_disk.len() |
649 }, |
679 } |
650 }) |
680 u32::try_from(offset) |
651 } |
681 // Could only panic for a dirstate file larger than 4 GiB |
652 // … so we can write them contiguously, after writing everything else they |
682 .expect("dirstate-v2 offset overflow") |
653 // refer to. |
683 .into() |
654 let start = current_offset(out); |
684 } |
655 let len = u32::try_from(nodes_len) |
685 |
656 // Could only panic with over 4 billion nodes |
686 fn write_path(&mut self, slice: &[u8]) -> PathSlice { |
657 .expect("dirstate-v2 path length overflow") |
687 let start = self.current_offset(); |
658 .into(); |
688 let len = u16::try_from(slice.len()) |
659 out.extend(on_disk_nodes.as_bytes()); |
689 // Could only panic for paths over 64 KiB |
660 Ok(ChildNodes { start, len }) |
690 .expect("dirstate-v2 path length overflow") |
661 } |
691 .into(); |
662 |
692 self.out.extend(slice.as_bytes()); |
663 fn current_offset(out: &Vec<u8>) -> Offset { |
693 PathSlice { start, len } |
664 u32::try_from(out.len()) |
694 } |
665 // Could only panic for a dirstate file larger than 4 GiB |
695 } |
666 .expect("dirstate-v2 offset overflow") |
|
667 .into() |
|
668 } |
|
669 |
|
670 fn write_path(slice: &[u8], out: &mut Vec<u8>) -> PathSlice { |
|
671 let start = current_offset(out); |
|
672 let len = u16::try_from(slice.len()) |
|
673 // Could only panic for paths over 64 KiB |
|
674 .expect("dirstate-v2 path length overflow") |
|
675 .into(); |
|
676 out.extend(slice.as_bytes()); |
|
677 PathSlice { start, len } |
|
678 } |
|