Show More
@@ -3,6 +3,12 b'' | |||||
3 | version = 3 |
|
3 | version = 3 | |
4 |
|
4 | |||
5 | [[package]] |
|
5 | [[package]] | |
|
6 | name = "Inflector" | |||
|
7 | version = "0.11.4" | |||
|
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
9 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" | |||
|
10 | ||||
|
11 | [[package]] | |||
6 | name = "adler" |
|
12 | name = "adler" | |
7 | version = "0.2.3" |
|
13 | version = "0.2.3" | |
8 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
14 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -18,6 +24,12 b' dependencies = [' | |||||
18 | ] |
|
24 | ] | |
19 |
|
25 | |||
20 | [[package]] |
|
26 | [[package]] | |
|
27 | name = "aliasable" | |||
|
28 | version = "0.1.3" | |||
|
29 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
30 | checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" | |||
|
31 | ||||
|
32 | [[package]] | |||
21 | name = "ansi_term" |
|
33 | name = "ansi_term" | |
22 | version = "0.11.0" |
|
34 | version = "0.11.0" | |
23 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
35 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -407,6 +419,7 b' dependencies = [' | |||||
407 | "log", |
|
419 | "log", | |
408 | "memmap2", |
|
420 | "memmap2", | |
409 | "micro-timer", |
|
421 | "micro-timer", | |
|
422 | "ouroboros", | |||
410 | "pretty_assertions", |
|
423 | "pretty_assertions", | |
411 | "rand 0.8.4", |
|
424 | "rand 0.8.4", | |
412 | "rand_distr", |
|
425 | "rand_distr", | |
@@ -415,7 +428,6 b' dependencies = [' | |||||
415 | "regex", |
|
428 | "regex", | |
416 | "same-file", |
|
429 | "same-file", | |
417 | "sha-1", |
|
430 | "sha-1", | |
418 | "stable_deref_trait", |
|
|||
419 | "tempfile", |
|
431 | "tempfile", | |
420 | "twox-hash", |
|
432 | "twox-hash", | |
421 | "zstd", |
|
433 | "zstd", | |
@@ -623,6 +635,30 b' source = "registry+https://github.com/ru' | |||||
623 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" |
|
635 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" | |
624 |
|
636 | |||
625 | [[package]] |
|
637 | [[package]] | |
|
638 | name = "ouroboros" | |||
|
639 | version = "0.15.0" | |||
|
640 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
641 | checksum = "9f31a3b678685b150cba82b702dcdc5e155893f63610cf388d30cd988d4ca2bf" | |||
|
642 | dependencies = [ | |||
|
643 | "aliasable", | |||
|
644 | "ouroboros_macro", | |||
|
645 | "stable_deref_trait", | |||
|
646 | ] | |||
|
647 | ||||
|
648 | [[package]] | |||
|
649 | name = "ouroboros_macro" | |||
|
650 | version = "0.15.0" | |||
|
651 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
652 | checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" | |||
|
653 | dependencies = [ | |||
|
654 | "Inflector", | |||
|
655 | "proc-macro-error", | |||
|
656 | "proc-macro2", | |||
|
657 | "quote", | |||
|
658 | "syn", | |||
|
659 | ] | |||
|
660 | ||||
|
661 | [[package]] | |||
626 | name = "output_vt100" |
|
662 | name = "output_vt100" | |
627 | version = "0.1.2" |
|
663 | version = "0.1.2" | |
628 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
664 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -662,6 +698,30 b' dependencies = [' | |||||
662 | ] |
|
698 | ] | |
663 |
|
699 | |||
664 | [[package]] |
|
700 | [[package]] | |
|
701 | name = "proc-macro-error" | |||
|
702 | version = "1.0.4" | |||
|
703 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
704 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | |||
|
705 | dependencies = [ | |||
|
706 | "proc-macro-error-attr", | |||
|
707 | "proc-macro2", | |||
|
708 | "quote", | |||
|
709 | "syn", | |||
|
710 | "version_check", | |||
|
711 | ] | |||
|
712 | ||||
|
713 | [[package]] | |||
|
714 | name = "proc-macro-error-attr" | |||
|
715 | version = "1.0.4" | |||
|
716 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
717 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | |||
|
718 | dependencies = [ | |||
|
719 | "proc-macro2", | |||
|
720 | "quote", | |||
|
721 | "version_check", | |||
|
722 | ] | |||
|
723 | ||||
|
724 | [[package]] | |||
665 | name = "proc-macro2" |
|
725 | name = "proc-macro2" | |
666 | version = "1.0.24" |
|
726 | version = "1.0.24" | |
667 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
727 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -18,6 +18,7 b' im-rc = "15.0.*"' | |||||
18 | itertools = "0.9" |
|
18 | itertools = "0.9" | |
19 | lazy_static = "1.4.0" |
|
19 | lazy_static = "1.4.0" | |
20 | libc = "0.2" |
|
20 | libc = "0.2" | |
|
21 | ouroboros = "0.15.0" | |||
21 | rand = "0.8.4" |
|
22 | rand = "0.8.4" | |
22 | rand_pcg = "0.3.1" |
|
23 | rand_pcg = "0.3.1" | |
23 | rand_distr = "0.4.2" |
|
24 | rand_distr = "0.4.2" | |
@@ -26,7 +27,6 b' regex = "1.3.9"' | |||||
26 | sha-1 = "0.9.6" |
|
27 | sha-1 = "0.9.6" | |
27 | twox-hash = "1.5.0" |
|
28 | twox-hash = "1.5.0" | |
28 | same-file = "1.0.6" |
|
29 | same-file = "1.0.6" | |
29 | stable_deref_trait = "1.2.0" |
|
|||
30 | tempfile = "3.1.0" |
|
30 | tempfile = "3.1.0" | |
31 | crossbeam-channel = "0.4" |
|
31 | crossbeam-channel = "0.4" | |
32 | micro-timer = "0.3.0" |
|
32 | micro-timer = "0.3.0" |
@@ -136,8 +136,6 b' pub enum StatusError {' | |||||
136 | DirstateV2ParseError(DirstateV2ParseError), |
|
136 | DirstateV2ParseError(DirstateV2ParseError), | |
137 | } |
|
137 | } | |
138 |
|
138 | |||
139 | pub type StatusResult<T> = Result<T, StatusError>; |
|
|||
140 |
|
||||
141 | impl fmt::Display for StatusError { |
|
139 | impl fmt::Display for StatusError { | |
142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
140 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
143 | match self { |
|
141 | match self { |
@@ -725,10 +725,11 b' where' | |||||
725 |
|
725 | |||
726 | impl OwningDirstateMap { |
|
726 | impl OwningDirstateMap { | |
727 | pub fn clear(&mut self) { |
|
727 | pub fn clear(&mut self) { | |
728 |
|
|
728 | self.with_dmap_mut(|map| { | |
729 | map.root = Default::default(); |
|
729 | map.root = Default::default(); | |
730 | map.nodes_with_entry_count = 0; |
|
730 | map.nodes_with_entry_count = 0; | |
731 | map.nodes_with_copy_source_count = 0; |
|
731 | map.nodes_with_copy_source_count = 0; | |
|
732 | }); | |||
732 | } |
|
733 | } | |
733 |
|
734 | |||
734 | pub fn set_entry( |
|
735 | pub fn set_entry( | |
@@ -736,9 +737,10 b' impl OwningDirstateMap {' | |||||
736 | filename: &HgPath, |
|
737 | filename: &HgPath, | |
737 | entry: DirstateEntry, |
|
738 | entry: DirstateEntry, | |
738 | ) -> Result<(), DirstateV2ParseError> { |
|
739 | ) -> Result<(), DirstateV2ParseError> { | |
739 |
|
|
740 | self.with_dmap_mut(|map| { | |
740 | map.get_or_insert(&filename)?.data = NodeData::Entry(entry); |
|
741 | map.get_or_insert(&filename)?.data = NodeData::Entry(entry); | |
741 | Ok(()) |
|
742 | Ok(()) | |
|
743 | }) | |||
742 | } |
|
744 | } | |
743 |
|
745 | |||
744 | pub fn add_file( |
|
746 | pub fn add_file( | |
@@ -747,8 +749,9 b' impl OwningDirstateMap {' | |||||
747 | entry: DirstateEntry, |
|
749 | entry: DirstateEntry, | |
748 | ) -> Result<(), DirstateError> { |
|
750 | ) -> Result<(), DirstateError> { | |
749 | let old_state = self.get(filename)?.map(|e| e.state()); |
|
751 | let old_state = self.get(filename)?.map(|e| e.state()); | |
750 |
|
|
752 | self.with_dmap_mut(|map| { | |
751 | Ok(map.add_or_remove_file(filename, old_state, entry)?) |
|
753 | Ok(map.add_or_remove_file(filename, old_state, entry)?) | |
|
754 | }) | |||
752 | } |
|
755 | } | |
753 |
|
756 | |||
754 | pub fn remove_file( |
|
757 | pub fn remove_file( | |
@@ -779,9 +782,10 b' impl OwningDirstateMap {' | |||||
779 | if size == 0 { |
|
782 | if size == 0 { | |
780 | self.copy_map_remove(filename)?; |
|
783 | self.copy_map_remove(filename)?; | |
781 | } |
|
784 | } | |
782 |
|
|
785 | self.with_dmap_mut(|map| { | |
783 | let entry = DirstateEntry::new_removed(size); |
|
786 | let entry = DirstateEntry::new_removed(size); | |
784 | Ok(map.add_or_remove_file(filename, old_state, entry)?) |
|
787 | Ok(map.add_or_remove_file(filename, old_state, entry)?) | |
|
788 | }) | |||
785 | } |
|
789 | } | |
786 |
|
790 | |||
787 | pub fn drop_entry_and_copy_source( |
|
791 | pub fn drop_entry_and_copy_source( | |
@@ -791,7 +795,6 b' impl OwningDirstateMap {' | |||||
791 | let was_tracked = self |
|
795 | let was_tracked = self | |
792 | .get(filename)? |
|
796 | .get(filename)? | |
793 | .map_or(false, |e| e.state().is_tracked()); |
|
797 | .map_or(false, |e| e.state().is_tracked()); | |
794 | let map = self.get_map_mut(); |
|
|||
795 | struct Dropped { |
|
798 | struct Dropped { | |
796 | was_tracked: bool, |
|
799 | was_tracked: bool, | |
797 | had_entry: bool, |
|
800 | had_entry: bool, | |
@@ -878,6 +881,7 b' impl OwningDirstateMap {' | |||||
878 | Ok(Some((dropped, remove))) |
|
881 | Ok(Some((dropped, remove))) | |
879 | } |
|
882 | } | |
880 |
|
883 | |||
|
884 | self.with_dmap_mut(|map| { | |||
881 | if let Some((dropped, _removed)) = recur( |
|
885 | if let Some((dropped, _removed)) = recur( | |
882 | map.on_disk, |
|
886 | map.on_disk, | |
883 | &mut map.unreachable_bytes, |
|
887 | &mut map.unreachable_bytes, | |
@@ -894,13 +898,14 b' impl OwningDirstateMap {' | |||||
894 | debug_assert!(!was_tracked); |
|
898 | debug_assert!(!was_tracked); | |
895 | } |
|
899 | } | |
896 | Ok(()) |
|
900 | Ok(()) | |
|
901 | }) | |||
897 | } |
|
902 | } | |
898 |
|
903 | |||
899 | pub fn has_tracked_dir( |
|
904 | pub fn has_tracked_dir( | |
900 | &mut self, |
|
905 | &mut self, | |
901 | directory: &HgPath, |
|
906 | directory: &HgPath, | |
902 | ) -> Result<bool, DirstateError> { |
|
907 | ) -> Result<bool, DirstateError> { | |
903 |
|
|
908 | self.with_dmap_mut(|map| { | |
904 | if let Some(node) = map.get_node(directory)? { |
|
909 | if let Some(node) = map.get_node(directory)? { | |
905 | // A node without a `DirstateEntry` was created to hold child |
|
910 | // A node without a `DirstateEntry` was created to hold child | |
906 | // nodes, and is therefore a directory. |
|
911 | // nodes, and is therefore a directory. | |
@@ -909,13 +914,14 b' impl OwningDirstateMap {' | |||||
909 | } else { |
|
914 | } else { | |
910 | Ok(false) |
|
915 | Ok(false) | |
911 | } |
|
916 | } | |
|
917 | }) | |||
912 | } |
|
918 | } | |
913 |
|
919 | |||
914 | pub fn has_dir( |
|
920 | pub fn has_dir( | |
915 | &mut self, |
|
921 | &mut self, | |
916 | directory: &HgPath, |
|
922 | directory: &HgPath, | |
917 | ) -> Result<bool, DirstateError> { |
|
923 | ) -> Result<bool, DirstateError> { | |
918 |
|
|
924 | self.with_dmap_mut(|map| { | |
919 | if let Some(node) = map.get_node(directory)? { |
|
925 | if let Some(node) = map.get_node(directory)? { | |
920 | // A node without a `DirstateEntry` was created to hold child |
|
926 | // A node without a `DirstateEntry` was created to hold child | |
921 | // nodes, and is therefore a directory. |
|
927 | // nodes, and is therefore a directory. | |
@@ -924,6 +930,7 b' impl OwningDirstateMap {' | |||||
924 | } else { |
|
930 | } else { | |
925 | Ok(false) |
|
931 | Ok(false) | |
926 | } |
|
932 | } | |
|
933 | }) | |||
927 | } |
|
934 | } | |
928 |
|
935 | |||
929 | #[timed] |
|
936 | #[timed] | |
@@ -975,16 +982,29 b' impl OwningDirstateMap {' | |||||
975 | on_disk::write(map, can_append) |
|
982 | on_disk::write(map, can_append) | |
976 | } |
|
983 | } | |
977 |
|
984 | |||
978 | pub fn status<'a>( |
|
985 | /// `callback` allows the caller to process and do something with the | |
979 | &'a mut self, |
|
986 | /// results of the status. This is needed to do so efficiently (i.e. | |
980 | matcher: &'a (dyn Matcher + Sync), |
|
987 | /// without cloning the `DirstateStatus` object with its paths) because | |
|
988 | /// we need to borrow from `Self`. | |||
|
989 | pub fn with_status<R>( | |||
|
990 | &mut self, | |||
|
991 | matcher: &(dyn Matcher + Sync), | |||
981 | root_dir: PathBuf, |
|
992 | root_dir: PathBuf, | |
982 | ignore_files: Vec<PathBuf>, |
|
993 | ignore_files: Vec<PathBuf>, | |
983 | options: StatusOptions, |
|
994 | options: StatusOptions, | |
984 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> |
|
995 | callback: impl for<'r> FnOnce( | |
985 | { |
|
996 | Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>, | |
986 | let map = self.get_map_mut(); |
|
997 | ) -> R, | |
987 | super::status::status(map, matcher, root_dir, ignore_files, options) |
|
998 | ) -> R { | |
|
999 | self.with_dmap_mut(|map| { | |||
|
1000 | callback(super::status::status( | |||
|
1001 | map, | |||
|
1002 | matcher, | |||
|
1003 | root_dir, | |||
|
1004 | ignore_files, | |||
|
1005 | options, | |||
|
1006 | )) | |||
|
1007 | }) | |||
988 | } |
|
1008 | } | |
989 |
|
1009 | |||
990 | pub fn copy_map_len(&self) -> usize { |
|
1010 | pub fn copy_map_len(&self) -> usize { | |
@@ -1032,7 +1052,7 b' impl OwningDirstateMap {' | |||||
1032 | &mut self, |
|
1052 | &mut self, | |
1033 | key: &HgPath, |
|
1053 | key: &HgPath, | |
1034 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
|
1054 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | |
1035 |
|
|
1055 | self.with_dmap_mut(|map| { | |
1036 | let count = &mut map.nodes_with_copy_source_count; |
|
1056 | let count = &mut map.nodes_with_copy_source_count; | |
1037 | let unreachable_bytes = &mut map.unreachable_bytes; |
|
1057 | let unreachable_bytes = &mut map.unreachable_bytes; | |
1038 | Ok(DirstateMap::get_node_mut( |
|
1058 | Ok(DirstateMap::get_node_mut( | |
@@ -1048,6 +1068,7 b' impl OwningDirstateMap {' | |||||
1048 | } |
|
1068 | } | |
1049 | node.copy_source.take().map(Cow::into_owned) |
|
1069 | node.copy_source.take().map(Cow::into_owned) | |
1050 | })) |
|
1070 | })) | |
|
1071 | }) | |||
1051 | } |
|
1072 | } | |
1052 |
|
1073 | |||
1053 | pub fn copy_map_insert( |
|
1074 | pub fn copy_map_insert( | |
@@ -1055,7 +1076,7 b' impl OwningDirstateMap {' | |||||
1055 | key: HgPathBuf, |
|
1076 | key: HgPathBuf, | |
1056 | value: HgPathBuf, |
|
1077 | value: HgPathBuf, | |
1057 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
|
1078 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | |
1058 |
|
|
1079 | self.with_dmap_mut(|map| { | |
1059 | let node = DirstateMap::get_or_insert_node( |
|
1080 | let node = DirstateMap::get_or_insert_node( | |
1060 | map.on_disk, |
|
1081 | map.on_disk, | |
1061 | &mut map.unreachable_bytes, |
|
1082 | &mut map.unreachable_bytes, | |
@@ -1068,6 +1089,7 b' impl OwningDirstateMap {' | |||||
1068 | map.nodes_with_copy_source_count += 1 |
|
1089 | map.nodes_with_copy_source_count += 1 | |
1069 | } |
|
1090 | } | |
1070 | Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) |
|
1091 | Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) | |
|
1092 | }) | |||
1071 | } |
|
1093 | } | |
1072 |
|
1094 | |||
1073 | pub fn len(&self) -> usize { |
|
1095 | pub fn len(&self) -> usize { | |
@@ -1115,7 +1137,7 b' impl OwningDirstateMap {' | |||||
1115 | >, |
|
1137 | >, | |
1116 | DirstateError, |
|
1138 | DirstateError, | |
1117 | > { |
|
1139 | > { | |
1118 |
let map = self.get_map |
|
1140 | let map = self.get_map(); | |
1119 | let on_disk = map.on_disk; |
|
1141 | let on_disk = map.on_disk; | |
1120 | Ok(Box::new(filter_map_results( |
|
1142 | Ok(Box::new(filter_map_results( | |
1121 | map.iter_nodes(), |
|
1143 | map.iter_nodes(), |
@@ -1,142 +1,89 b'' | |||||
|
1 | use crate::{DirstateError, DirstateParents}; | |||
|
2 | ||||
1 | use super::dirstate_map::DirstateMap; |
|
3 | use super::dirstate_map::DirstateMap; | |
2 | use stable_deref_trait::StableDeref; |
|
|||
3 | use std::ops::Deref; |
|
4 | use std::ops::Deref; | |
4 |
|
5 | |||
5 | /* |
|
6 | use ouroboros::self_referencing; | |
6 | // /!\ This is unsound and can cause use after free. It will be fixed in the |
|
|||
7 | // next patch |
|
|||
8 |
|
||||
9 | // If we change `value` from its current use of `HgPathBuf` to `&HgPath`, |
|
|||
10 | // nothing here tells that `value` will outlive `OwningDirstateMap` |
|
|||
11 | pub fn copy_map_insert<'a,'owned>( |
|
|||
12 | &'owned mut self, |
|
|||
13 | key: &HgPath, |
|
|||
14 | value: &'a HgPath, |
|
|||
15 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
|
|||
16 | // `'local` is smaller than `'a` here |
|
|||
17 | let map: &'local mut DirstateMap<'local> = self.get_map_mut(); |
|
|||
18 | let node: &'local mut Node<'local> = DirstateMap::get_or_insert_node( |
|
|||
19 | map.on_disk, |
|
|||
20 | &mut map.unreachable_bytes, |
|
|||
21 | &mut map.root, |
|
|||
22 | &key, |
|
|||
23 | WithBasename::to_cow_owned, |
|
|||
24 | |_ancestor| {}, |
|
|||
25 | )?; |
|
|||
26 | if node.copy_source.is_none() { |
|
|||
27 | map.nodes_with_copy_source_count += 1 |
|
|||
28 | } |
|
|||
29 | Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) |
|
|||
30 | // and right here ----------^^^^^^^^^^^^ |
|
|||
31 | // we are storing `&'a HgPath` in `Node<'local>` which is possible |
|
|||
32 | // because to the compiler, `'a` is longer than ``local`. |
|
|||
33 | // It is wrong because nothing proves that `&'a HgPath` will outlive `self`. |
|
|||
34 | } |
|
|||
35 |
|
||||
36 | // All of this is caused by the wrong cast of the DirstateMap pointer that |
|
|||
37 | // fakes the lifetime of `DirstateMap` and ensures the compiler that it lives |
|
|||
38 | // as long as `on_disk`, which is only true for its immutable data. |
|
|||
39 | // This will be fixed in the next commit. |
|
|||
40 | */ |
|
|||
41 |
|
7 | |||
42 | /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it |
|
8 | /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it | |
43 | /// borrows. |
|
9 | /// borrows. | |
44 | /// |
|
10 | #[self_referencing] | |
45 | /// This is similar to [`OwningRef`] which is more limited because it |
|
|||
46 | /// represents exactly one `&T` reference next to the value it borrows, as |
|
|||
47 | /// opposed to a struct that may contain an arbitrary number of references in |
|
|||
48 | /// arbitrarily-nested data structures. |
|
|||
49 | /// |
|
|||
50 | /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html |
|
|||
51 | pub struct OwningDirstateMap { |
|
11 | pub struct OwningDirstateMap { | |
52 | /// Owned handle to a bytes buffer with a stable address. |
|
|||
53 | /// |
|
|||
54 | /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>. |
|
|||
55 | on_disk: Box<dyn Deref<Target = [u8]> + Send>, |
|
12 | on_disk: Box<dyn Deref<Target = [u8]> + Send>, | |
56 |
|
13 | #[borrows(on_disk)] | ||
57 | /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the |
|
14 | #[covariant] | |
58 | /// language cannot represent a lifetime referencing a sibling field. |
|
15 | map: DirstateMap<'this>, | |
59 | /// This is not quite a self-referencial struct (moving this struct is not |
|
|||
60 | /// a problem as it doesn’t change the address of the bytes buffer owned |
|
|||
61 | /// by `on_disk`) but touches similar borrow-checker limitations. |
|
|||
62 | ptr: *mut (), |
|
|||
63 | } |
|
16 | } | |
64 |
|
17 | |||
65 | impl OwningDirstateMap { |
|
18 | impl OwningDirstateMap { | |
66 | pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self |
|
19 | pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self | |
67 | where |
|
20 | where | |
68 |
OnDisk: Deref<Target = [u8]> + |
|
21 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |
69 | { |
|
22 | { | |
70 | let on_disk = Box::new(on_disk); |
|
23 | let on_disk = Box::new(on_disk); | |
71 | let bytes: &'_ [u8] = &on_disk; |
|
|||
72 | let map = DirstateMap::empty(bytes); |
|
|||
73 |
|
24 | |||
74 | // Like in `bytes` above, this `'_` lifetime parameter borrows from |
|
25 | OwningDirstateMapBuilder { | |
75 | // the bytes buffer owned by `on_disk`. |
|
26 | on_disk, | |
76 | let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map)); |
|
27 | map_builder: |bytes| DirstateMap::empty(&bytes), | |
77 |
|
28 | } | ||
78 | // Erase the pointed type entirely in order to erase the lifetime. |
|
29 | .build() | |
79 | let ptr: *mut () = ptr.cast(); |
|
|||
80 |
|
||||
81 | Self { on_disk, ptr } |
|
|||
82 | } |
|
30 | } | |
83 |
|
31 | |||
84 | pub fn get_pair_mut<'a>( |
|
32 | pub fn new_v1<OnDisk>( | |
85 | &'a mut self, |
|
33 | on_disk: OnDisk, | |
86 | ) -> (&'a [u8], &'a mut DirstateMap<'a>) { |
|
34 | ) -> Result<(Self, DirstateParents), DirstateError> | |
87 | // SAFETY: We cast the type-erased pointer back to the same type it had |
|
35 | where | |
88 | // in `new`, except with a different lifetime parameter. This time we |
|
36 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |
89 | // connect the lifetime to that of `self`. This cast is valid because |
|
37 | { | |
90 | // `self` owns the same `on_disk` whose buffer `DirstateMap` |
|
38 | let on_disk = Box::new(on_disk); | |
91 | // references. That buffer has a stable memory address because our |
|
39 | let mut parents = DirstateParents::NULL; | |
92 | // `Self::new_empty` counstructor requires `StableDeref`. |
|
40 | ||
93 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); |
|
41 | Ok(( | |
94 | // SAFETY: we dereference that pointer, connecting the lifetime of the |
|
42 | OwningDirstateMapTryBuilder { | |
95 | // new `&mut` to that of `self`. This is valid because the |
|
43 | on_disk, | |
96 | // raw pointer is to a boxed value, and `self` owns that box. |
|
44 | map_builder: |bytes| { | |
97 | (&self.on_disk, unsafe { &mut *ptr }) |
|
45 | DirstateMap::new_v1(&bytes).map(|(dmap, p)| { | |
|
46 | parents = p.unwrap_or(DirstateParents::NULL); | |||
|
47 | dmap | |||
|
48 | }) | |||
|
49 | }, | |||
98 | } |
|
50 | } | |
99 |
|
51 | .try_build()?, | ||
100 | pub fn get_map_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> { |
|
52 | parents, | |
101 | self.get_pair_mut().1 |
|
53 | )) | |
102 | } |
|
54 | } | |
103 |
|
55 | |||
104 | pub fn get_map<'a>(&'a self) -> &'a DirstateMap<'a> { |
|
56 | pub fn new_v2<OnDisk>( | |
105 | // SAFETY: same reasoning as in `get_pair_mut` above. |
|
57 | on_disk: OnDisk, | |
106 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); |
|
58 | data_size: usize, | |
107 | unsafe { &*ptr } |
|
59 | metadata: &[u8], | |
|
60 | ) -> Result<Self, DirstateError> | |||
|
61 | where | |||
|
62 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |||
|
63 | { | |||
|
64 | let on_disk = Box::new(on_disk); | |||
|
65 | ||||
|
66 | OwningDirstateMapTryBuilder { | |||
|
67 | on_disk, | |||
|
68 | map_builder: |bytes| { | |||
|
69 | DirstateMap::new_v2(&bytes, data_size, metadata) | |||
|
70 | }, | |||
108 | } |
|
71 | } | |
109 |
|
72 | .try_build() | ||
110 | pub fn on_disk<'a>(&'a self) -> &'a [u8] { |
|
|||
111 | &self.on_disk |
|
|||
112 | } |
|
|||
113 | } |
|
73 | } | |
114 |
|
74 | |||
115 | impl Drop for OwningDirstateMap { |
|
75 | pub fn with_dmap_mut<R>( | |
116 |
|
|
76 | &mut self, | |
117 | // Silence a "field is never read" warning, and demonstrate that this |
|
77 | f: impl FnOnce(&mut DirstateMap) -> R, | |
118 | // value is still alive. |
|
78 | ) -> R { | |
119 | let _: &Box<dyn Deref<Target = [u8]> + Send> = &self.on_disk; |
|
79 | self.with_map_mut(f) | |
120 | // SAFETY: this cast is the same as in `get_mut`, and is valid for the |
|
80 | } | |
121 | // same reason. `self.on_disk` still exists at this point, drop glue |
|
81 | ||
122 | // will drop it implicitly after this `drop` method returns. |
|
82 | pub fn get_map(&self) -> &DirstateMap { | |
123 | let ptr: *mut DirstateMap<'_> = self.ptr.cast(); |
|
83 | self.borrow_map() | |
124 | // SAFETY: `Box::from_raw` takes ownership of the box away from `self`. |
|
84 | } | |
125 | // This is fine because drop glue does nothing for `*mut ()` and we’re |
|
85 | ||
126 | // in `drop`, so `get` and `get_mut` cannot be called again. |
|
86 | pub fn on_disk(&self) -> &[u8] { | |
127 | unsafe { drop(Box::from_raw(ptr)) } |
|
87 | self.borrow_on_disk() | |
128 | } |
|
88 | } | |
129 | } |
|
89 | } | |
130 |
|
||||
131 | fn _static_assert_is_send<T: Send>() {} |
|
|||
132 |
|
||||
133 | fn _static_assert_fields_are_send() { |
|
|||
134 | _static_assert_is_send::<Box<DirstateMap<'_>>>(); |
|
|||
135 | } |
|
|||
136 |
|
||||
137 | // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because |
|
|||
138 | // thread-safety of raw pointers is unknown in the general case. However this |
|
|||
139 | // particular raw pointer represents a `Box<DirstateMap<'on_disk>>` that we |
|
|||
140 | // own. Since that `Box` is `Send` as shown in above, it is sound to mark |
|
|||
141 | // this struct as `Send` too. |
|
|||
142 | unsafe impl Send for OwningDirstateMap {} |
|
@@ -40,13 +40,14 b' use std::time::SystemTime;' | |||||
40 | /// exists in one of the two trees, depending on information requested by |
|
40 | /// exists in one of the two trees, depending on information requested by | |
41 | /// `options` we may need to traverse the remaining subtree. |
|
41 | /// `options` we may need to traverse the remaining subtree. | |
42 | #[timed] |
|
42 | #[timed] | |
43 |
pub fn status<' |
|
43 | pub fn status<'dirstate>( | |
44 |
dmap: &' |
|
44 | dmap: &'dirstate mut DirstateMap, | |
45 | matcher: &(dyn Matcher + Sync), |
|
45 | matcher: &(dyn Matcher + Sync), | |
46 | root_dir: PathBuf, |
|
46 | root_dir: PathBuf, | |
47 | ignore_files: Vec<PathBuf>, |
|
47 | ignore_files: Vec<PathBuf>, | |
48 | options: StatusOptions, |
|
48 | options: StatusOptions, | |
49 |
) -> Result<(DirstateStatus<' |
|
49 | ) -> Result<(DirstateStatus<'dirstate>, Vec<PatternFileWarning>), StatusError> | |
|
50 | { | |||
50 | // Force the global rayon threadpool to not exceed 16 concurrent threads. |
|
51 | // Force the global rayon threadpool to not exceed 16 concurrent threads. | |
51 | // This is a stop-gap measure until we figure out why using more than 16 |
|
52 | // This is a stop-gap measure until we figure out why using more than 16 | |
52 | // threads makes `status` slower for each additional thread. |
|
53 | // threads makes `status` slower for each additional thread. |
@@ -1,7 +1,6 b'' | |||||
1 | use crate::changelog::Changelog; |
|
1 | use crate::changelog::Changelog; | |
2 | use crate::config::{Config, ConfigError, ConfigParseError}; |
|
2 | use crate::config::{Config, ConfigError, ConfigParseError}; | |
3 | use crate::dirstate::DirstateParents; |
|
3 | use crate::dirstate::DirstateParents; | |
4 | use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
|||
5 | use crate::dirstate_tree::on_disk::Docket as DirstateDocket; |
|
4 | use crate::dirstate_tree::on_disk::Docket as DirstateDocket; | |
6 | use crate::dirstate_tree::owning::OwningDirstateMap; |
|
5 | use crate::dirstate_tree::owning::OwningDirstateMap; | |
7 | use crate::errors::HgResultExt; |
|
6 | use crate::errors::HgResultExt; | |
@@ -340,25 +339,19 b' impl Repo {' | |||||
340 | .set(Some(docket.uuid.to_owned())); |
|
339 | .set(Some(docket.uuid.to_owned())); | |
341 | let data_size = docket.data_size(); |
|
340 | let data_size = docket.data_size(); | |
342 | let metadata = docket.tree_metadata(); |
|
341 | let metadata = docket.tree_metadata(); | |
343 |
|
|
342 | if let Some(data_mmap) = self | |
344 | .hg_vfs() |
|
343 | .hg_vfs() | |
345 | .mmap_open(docket.data_filename()) |
|
344 | .mmap_open(docket.data_filename()) | |
346 | .io_not_found_as_none()? |
|
345 | .io_not_found_as_none()? | |
347 | { |
|
346 | { | |
348 |
OwningDirstateMap::new_ |
|
347 | OwningDirstateMap::new_v2(data_mmap, data_size, metadata) | |
349 | } else { |
|
348 | } else { | |
350 |
OwningDirstateMap::new_ |
|
349 | OwningDirstateMap::new_v2(Vec::new(), data_size, metadata) | |
351 |
} |
|
350 | } | |
352 | let (on_disk, placeholder) = map.get_pair_mut(); |
|
|||
353 | *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?; |
|
|||
354 | Ok(map) |
|
|||
355 | } else { |
|
351 | } else { | |
356 | let mut map = OwningDirstateMap::new_empty(dirstate_file_contents); |
|
352 | let (map, parents) = | |
357 | let (on_disk, placeholder) = map.get_pair_mut(); |
|
353 | OwningDirstateMap::new_v1(dirstate_file_contents)?; | |
358 | let (inner, parents) = DirstateMap::new_v1(on_disk)?; |
|
354 | self.dirstate_parents.set(parents); | |
359 | self.dirstate_parents |
|
|||
360 | .set(parents.unwrap_or(DirstateParents::NULL)); |
|
|||
361 | *placeholder = inner; |
|
|||
362 | Ok(map) |
|
355 | Ok(map) | |
363 | } |
|
356 | } | |
364 | } |
|
357 | } |
@@ -23,7 +23,6 b' use crate::{' | |||||
23 | }; |
|
23 | }; | |
24 | use hg::{ |
|
24 | use hg::{ | |
25 | dirstate::StateMapIter, |
|
25 | dirstate::StateMapIter, | |
26 | dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap, |
|
|||
27 | dirstate_tree::on_disk::DirstateV2ParseError, |
|
26 | dirstate_tree::on_disk::DirstateV2ParseError, | |
28 | dirstate_tree::owning::OwningDirstateMap, |
|
27 | dirstate_tree::owning::OwningDirstateMap, | |
29 | revlog::Node, |
|
28 | revlog::Node, | |
@@ -53,18 +52,12 b' py_class!(pub class DirstateMap |py| {' | |||||
53 | on_disk: PyBytes, |
|
52 | on_disk: PyBytes, | |
54 | ) -> PyResult<PyObject> { |
|
53 | ) -> PyResult<PyObject> { | |
55 | let on_disk = PyBytesDeref::new(py, on_disk); |
|
54 | let on_disk = PyBytesDeref::new(py, on_disk); | |
56 |
let |
|
55 | let (map, parents) = OwningDirstateMap::new_v1(on_disk) | |
57 | let (on_disk, map_placeholder) = map.get_pair_mut(); |
|
|||
58 |
|
||||
59 | let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk) |
|
|||
60 | .map_err(|e| dirstate_error(py, e))?; |
|
56 | .map_err(|e| dirstate_error(py, e))?; | |
61 | *map_placeholder = actual_map; |
|
|||
62 | let map = Self::create_instance(py, map)?; |
|
57 | let map = Self::create_instance(py, map)?; | |
63 | let parents = parents.map(|p| { |
|
58 | let p1 = PyBytes::new(py, parents.p1.as_bytes()); | |
64 |
|
|
59 | let p2 = PyBytes::new(py, parents.p2.as_bytes()); | |
65 | let p2 = PyBytes::new(py, p.p2.as_bytes()); |
|
60 | let parents = (p1, p2); | |
66 | (p1, p2) |
|
|||
67 | }); |
|
|||
68 | Ok((map, parents).to_py_object(py).into_object()) |
|
61 | Ok((map, parents).to_py_object(py).into_object()) | |
69 | } |
|
62 | } | |
70 |
|
63 | |||
@@ -79,9 +72,7 b' py_class!(pub class DirstateMap |py| {' | |||||
79 | PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e)) |
|
72 | PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e)) | |
80 | }; |
|
73 | }; | |
81 | let on_disk = PyBytesDeref::new(py, on_disk); |
|
74 | let on_disk = PyBytesDeref::new(py, on_disk); | |
82 |
let |
|
75 | let map = OwningDirstateMap::new_v2( | |
83 | let (on_disk, map_placeholder) = map.get_pair_mut(); |
|
|||
84 | *map_placeholder = TreeDirstateMap::new_v2( |
|
|||
85 | on_disk, data_size, tree_metadata.data(py), |
|
76 | on_disk, data_size, tree_metadata.data(py), | |
86 | ).map_err(dirstate_error)?; |
|
77 | ).map_err(dirstate_error)?; | |
87 | let map = Self::create_instance(py, map)?; |
|
78 | let map = Self::create_instance(py, map)?; |
@@ -127,11 +127,16 b' pub fn status_wrapper(' | |||||
127 | // The caller may call `copymap.items()` separately |
|
127 | // The caller may call `copymap.items()` separately | |
128 | let list_copies = false; |
|
128 | let list_copies = false; | |
129 |
|
129 | |||
|
130 | let after_status = |res: Result<(DirstateStatus<'_>, _), StatusError>| { | |||
|
131 | let (status_res, warnings) = | |||
|
132 | res.map_err(|e| handle_fallback(py, e))?; | |||
|
133 | build_response(py, status_res, warnings) | |||
|
134 | }; | |||
|
135 | ||||
130 | match matcher.get_type(py).name(py).borrow() { |
|
136 | match matcher.get_type(py).name(py).borrow() { | |
131 | "alwaysmatcher" => { |
|
137 | "alwaysmatcher" => { | |
132 | let matcher = AlwaysMatcher; |
|
138 | let matcher = AlwaysMatcher; | |
133 | let (status_res, warnings) = dmap |
|
139 | dmap.with_status( | |
134 | .status( |
|
|||
135 |
|
|
140 | &matcher, | |
136 |
|
|
141 | root_dir.to_path_buf(), | |
137 |
|
|
142 | ignore_files, | |
@@ -143,9 +148,8 b' pub fn status_wrapper(' | |||||
143 |
|
|
148 | list_copies, | |
144 |
|
|
149 | collect_traversed_dirs, | |
145 |
|
|
150 | }, | |
|
151 | after_status, | |||
146 |
|
|
152 | ) | |
147 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
148 | build_response(py, status_res, warnings) |
|
|||
149 | } |
|
153 | } | |
150 | "exactmatcher" => { |
|
154 | "exactmatcher" => { | |
151 | let files = matcher.call_method( |
|
155 | let files = matcher.call_method( | |
@@ -167,8 +171,7 b' pub fn status_wrapper(' | |||||
167 | let files = files?; |
|
171 | let files = files?; | |
168 | let matcher = FileMatcher::new(files.as_ref()) |
|
172 | let matcher = FileMatcher::new(files.as_ref()) | |
169 | .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; |
|
173 | .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; | |
170 | let (status_res, warnings) = dmap |
|
174 | dmap.with_status( | |
171 | .status( |
|
|||
172 |
|
|
175 | &matcher, | |
173 |
|
|
176 | root_dir.to_path_buf(), | |
174 |
|
|
177 | ignore_files, | |
@@ -180,9 +183,8 b' pub fn status_wrapper(' | |||||
180 |
|
|
183 | list_copies, | |
181 |
|
|
184 | collect_traversed_dirs, | |
182 |
|
|
185 | }, | |
|
186 | after_status, | |||
183 |
|
|
187 | ) | |
184 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
185 | build_response(py, status_res, warnings) |
|
|||
186 | } |
|
188 | } | |
187 | "includematcher" => { |
|
189 | "includematcher" => { | |
188 | // Get the patterns from Python even though most of them are |
|
190 | // Get the patterns from Python even though most of them are | |
@@ -219,8 +221,7 b' pub fn status_wrapper(' | |||||
219 | let matcher = IncludeMatcher::new(ignore_patterns) |
|
221 | let matcher = IncludeMatcher::new(ignore_patterns) | |
220 | .map_err(|e| handle_fallback(py, e.into()))?; |
|
222 | .map_err(|e| handle_fallback(py, e.into()))?; | |
221 |
|
223 | |||
222 | let (status_res, warnings) = dmap |
|
224 | dmap.with_status( | |
223 | .status( |
|
|||
224 |
|
|
225 | &matcher, | |
225 |
|
|
226 | root_dir.to_path_buf(), | |
226 |
|
|
227 | ignore_files, | |
@@ -232,10 +233,8 b' pub fn status_wrapper(' | |||||
232 |
|
|
233 | list_copies, | |
233 |
|
|
234 | collect_traversed_dirs, | |
234 |
|
|
235 | }, | |
|
236 | after_status, | |||
235 |
|
|
237 | ) | |
236 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
237 |
|
||||
238 | build_response(py, status_res, warnings) |
|
|||
239 | } |
|
238 | } | |
240 | e => Err(PyErr::new::<ValueError, _>( |
|
239 | e => Err(PyErr::new::<ValueError, _>( | |
241 | py, |
|
240 | py, |
@@ -25,6 +25,9 b' use hg::utils::files::get_bytes_from_os_' | |||||
25 | use hg::utils::files::get_bytes_from_path; |
|
25 | use hg::utils::files::get_bytes_from_path; | |
26 | use hg::utils::files::get_path_from_bytes; |
|
26 | use hg::utils::files::get_path_from_bytes; | |
27 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
27 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | |
|
28 | use hg::DirstateStatus; | |||
|
29 | use hg::PatternFileWarning; | |||
|
30 | use hg::StatusError; | |||
28 | use hg::StatusOptions; |
|
31 | use hg::StatusOptions; | |
29 | use log::info; |
|
32 | use log::info; | |
30 | use std::io; |
|
33 | use std::io; | |
@@ -230,12 +233,12 b' pub fn run(invocation: &crate::CliInvoca' | |||||
230 | list_copies, |
|
233 | list_copies, | |
231 | collect_traversed_dirs: false, |
|
234 | collect_traversed_dirs: false, | |
232 | }; |
|
235 | }; | |
233 | let (mut ds_status, pattern_warnings) = dmap.status( |
|
236 | ||
234 | &AlwaysMatcher, |
|
237 | type StatusResult<'a> = | |
235 | repo.working_directory_path().to_owned(), |
|
238 | Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>; | |
236 | ignore_files(repo, config), |
|
239 | ||
237 | options, |
|
240 | let after_status = |res: StatusResult| -> Result<_, CommandError> { | |
238 | )?; |
|
241 | let (mut ds_status, pattern_warnings) = res?; | |
239 | for warning in pattern_warnings { |
|
242 | for warning in pattern_warnings { | |
240 | match warning { |
|
243 | match warning { | |
241 | hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui |
|
244 | hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui | |
@@ -338,10 +341,25 b' pub fn run(invocation: &crate::CliInvoca' | |||||
338 | output.display(b"C ", "status.clean", ds_status.clean)?; |
|
341 | output.display(b"C ", "status.clean", ds_status.clean)?; | |
339 | } |
|
342 | } | |
340 |
|
343 | |||
341 |
let |
|
344 | let dirstate_write_needed = ds_status.dirty; | |
342 | let filesystem_time_at_status_start = |
|
345 | let filesystem_time_at_status_start = | |
343 | ds_status.filesystem_time_at_status_start; |
|
346 | ds_status.filesystem_time_at_status_start; | |
344 |
|
347 | |||
|
348 | Ok(( | |||
|
349 | fixup, | |||
|
350 | dirstate_write_needed, | |||
|
351 | filesystem_time_at_status_start, | |||
|
352 | )) | |||
|
353 | }; | |||
|
354 | let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) = | |||
|
355 | dmap.with_status( | |||
|
356 | &AlwaysMatcher, | |||
|
357 | repo.working_directory_path().to_owned(), | |||
|
358 | ignore_files(repo, config), | |||
|
359 | options, | |||
|
360 | after_status, | |||
|
361 | )?; | |||
|
362 | ||||
345 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) |
|
363 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) | |
346 | && !dirstate_write_needed |
|
364 | && !dirstate_write_needed | |
347 | { |
|
365 | { |
General Comments 0
You need to be logged in to leave comments.
Login now