Show More
@@ -0,0 +1,175 b'' | |||
|
1 | use crate::dirstate::owning::OwningDirstateMap; | |
|
2 | use hg::dirstate::parsers::Timestamp; | |
|
3 | use hg::dirstate_tree::dispatch::DirstateMapMethods; | |
|
4 | use hg::matchers::Matcher; | |
|
5 | use hg::utils::hg_path::{HgPath, HgPathBuf}; | |
|
6 | use hg::CopyMapIter; | |
|
7 | use hg::DirstateEntry; | |
|
8 | use hg::DirstateError; | |
|
9 | use hg::DirstateMapError; | |
|
10 | use hg::DirstateParents; | |
|
11 | use hg::DirstateStatus; | |
|
12 | use hg::EntryState; | |
|
13 | use hg::PatternFileWarning; | |
|
14 | use hg::StateMapIter; | |
|
15 | use hg::StatusError; | |
|
16 | use hg::StatusOptions; | |
|
17 | use std::path::PathBuf; | |
|
18 | ||
|
19 | impl DirstateMapMethods for OwningDirstateMap { | |
|
20 | fn clear(&mut self) { | |
|
21 | self.get_mut().clear() | |
|
22 | } | |
|
23 | ||
|
24 | fn add_file( | |
|
25 | &mut self, | |
|
26 | filename: &HgPath, | |
|
27 | old_state: EntryState, | |
|
28 | entry: DirstateEntry, | |
|
29 | ) -> Result<(), DirstateMapError> { | |
|
30 | self.get_mut().add_file(filename, old_state, entry) | |
|
31 | } | |
|
32 | ||
|
33 | fn remove_file( | |
|
34 | &mut self, | |
|
35 | filename: &HgPath, | |
|
36 | old_state: EntryState, | |
|
37 | size: i32, | |
|
38 | ) -> Result<(), DirstateMapError> { | |
|
39 | self.get_mut().remove_file(filename, old_state, size) | |
|
40 | } | |
|
41 | ||
|
42 | fn drop_file( | |
|
43 | &mut self, | |
|
44 | filename: &HgPath, | |
|
45 | old_state: EntryState, | |
|
46 | ) -> Result<bool, DirstateMapError> { | |
|
47 | self.get_mut().drop_file(filename, old_state) | |
|
48 | } | |
|
49 | ||
|
50 | fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) { | |
|
51 | self.get_mut().clear_ambiguous_times(filenames, now) | |
|
52 | } | |
|
53 | ||
|
54 | fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool { | |
|
55 | self.get_mut().non_normal_entries_contains(key) | |
|
56 | } | |
|
57 | ||
|
58 | fn non_normal_entries_remove(&mut self, key: &HgPath) { | |
|
59 | self.get_mut().non_normal_entries_remove(key) | |
|
60 | } | |
|
61 | ||
|
62 | fn non_normal_or_other_parent_paths( | |
|
63 | &mut self, | |
|
64 | ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> { | |
|
65 | self.get_mut().non_normal_or_other_parent_paths() | |
|
66 | } | |
|
67 | ||
|
68 | fn set_non_normal_other_parent_entries(&mut self, force: bool) { | |
|
69 | self.get_mut().set_non_normal_other_parent_entries(force) | |
|
70 | } | |
|
71 | ||
|
72 | fn iter_non_normal_paths( | |
|
73 | &mut self, | |
|
74 | ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> { | |
|
75 | self.get_mut().iter_non_normal_paths() | |
|
76 | } | |
|
77 | ||
|
78 | fn iter_non_normal_paths_panic( | |
|
79 | &self, | |
|
80 | ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> { | |
|
81 | self.get().iter_non_normal_paths_panic() | |
|
82 | } | |
|
83 | ||
|
84 | fn iter_other_parent_paths( | |
|
85 | &mut self, | |
|
86 | ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> { | |
|
87 | self.get_mut().iter_other_parent_paths() | |
|
88 | } | |
|
89 | ||
|
90 | fn has_tracked_dir( | |
|
91 | &mut self, | |
|
92 | directory: &HgPath, | |
|
93 | ) -> Result<bool, DirstateMapError> { | |
|
94 | self.get_mut().has_tracked_dir(directory) | |
|
95 | } | |
|
96 | ||
|
97 | fn has_dir( | |
|
98 | &mut self, | |
|
99 | directory: &HgPath, | |
|
100 | ) -> Result<bool, DirstateMapError> { | |
|
101 | self.get_mut().has_dir(directory) | |
|
102 | } | |
|
103 | ||
|
104 | fn pack( | |
|
105 | &mut self, | |
|
106 | parents: DirstateParents, | |
|
107 | now: Timestamp, | |
|
108 | ) -> Result<Vec<u8>, DirstateError> { | |
|
109 | self.get_mut().pack(parents, now) | |
|
110 | } | |
|
111 | ||
|
112 | fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> { | |
|
113 | self.get_mut().set_all_dirs() | |
|
114 | } | |
|
115 | ||
|
116 | fn set_dirs(&mut self) -> Result<(), DirstateMapError> { | |
|
117 | self.get_mut().set_dirs() | |
|
118 | } | |
|
119 | ||
|
120 | fn status<'a>( | |
|
121 | &'a mut self, | |
|
122 | matcher: &'a (dyn Matcher + Sync), | |
|
123 | root_dir: PathBuf, | |
|
124 | ignore_files: Vec<PathBuf>, | |
|
125 | options: StatusOptions, | |
|
126 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> | |
|
127 | { | |
|
128 | self.get_mut() | |
|
129 | .status(matcher, root_dir, ignore_files, options) | |
|
130 | } | |
|
131 | ||
|
132 | fn copy_map_len(&self) -> usize { | |
|
133 | self.get().copy_map_len() | |
|
134 | } | |
|
135 | ||
|
136 | fn copy_map_iter(&self) -> CopyMapIter<'_> { | |
|
137 | self.get().copy_map_iter() | |
|
138 | } | |
|
139 | ||
|
140 | fn copy_map_contains_key(&self, key: &HgPath) -> bool { | |
|
141 | self.get().copy_map_contains_key(key) | |
|
142 | } | |
|
143 | ||
|
144 | fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> { | |
|
145 | self.get().copy_map_get(key) | |
|
146 | } | |
|
147 | ||
|
148 | fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> { | |
|
149 | self.get_mut().copy_map_remove(key) | |
|
150 | } | |
|
151 | ||
|
152 | fn copy_map_insert( | |
|
153 | &mut self, | |
|
154 | key: HgPathBuf, | |
|
155 | value: HgPathBuf, | |
|
156 | ) -> Option<HgPathBuf> { | |
|
157 | self.get_mut().copy_map_insert(key, value) | |
|
158 | } | |
|
159 | ||
|
160 | fn len(&self) -> usize { | |
|
161 | self.get().len() | |
|
162 | } | |
|
163 | ||
|
164 | fn contains_key(&self, key: &HgPath) -> bool { | |
|
165 | self.get().contains_key(key) | |
|
166 | } | |
|
167 | ||
|
168 | fn get(&self, key: &HgPath) -> Option<&DirstateEntry> { | |
|
169 | self.get().get(key) | |
|
170 | } | |
|
171 | ||
|
172 | fn iter(&self) -> StateMapIter<'_> { | |
|
173 | self.get().iter() | |
|
174 | } | |
|
175 | } |
@@ -0,0 +1,97 b'' | |||
|
1 | use cpython::PyBytes; | |
|
2 | use cpython::Python; | |
|
3 | use hg::dirstate_tree::dirstate_map::DirstateMap; | |
|
4 | use hg::DirstateError; | |
|
5 | use hg::DirstateParents; | |
|
6 | ||
|
7 | /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it | |
|
8 | /// borrows. This is similar to the owning-ref crate. | |
|
9 | /// | |
|
10 | /// This is similar to [`OwningRef`] which is more limited because it | |
|
11 | /// represents exactly one `&T` reference next to the value it borrows, as | |
|
12 | /// opposed to a struct that may contain an arbitrary number of references in | |
|
13 | /// arbitrarily-nested data structures. | |
|
14 | /// | |
|
15 | /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html | |
|
16 | pub(super) struct OwningDirstateMap { | |
|
17 | /// Owned handle to a bytes buffer with a stable address. | |
|
18 | /// | |
|
19 | /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>. | |
|
20 | on_disk: PyBytes, | |
|
21 | ||
|
22 | /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the | |
|
23 | /// language cannot represent a lifetime referencing a sibling field. | |
|
24 | /// This is not quite a self-referencial struct (moving this struct is not | |
|
25 | /// a problem as it doesn’t change the address of the bytes buffer owned | |
|
26 | /// by `PyBytes`) but touches similar borrow-checker limitations. | |
|
27 | ptr: *mut (), | |
|
28 | } | |
|
29 | ||
|
30 | impl OwningDirstateMap { | |
|
31 | pub fn new( | |
|
32 | py: Python, | |
|
33 | on_disk: PyBytes, | |
|
34 | ) -> Result<(Self, Option<DirstateParents>), DirstateError> { | |
|
35 | let bytes: &'_ [u8] = on_disk.data(py); | |
|
36 | let (map, parents) = DirstateMap::new(bytes)?; | |
|
37 | ||
|
38 | // Like in `bytes` above, this `'_` lifetime parameter borrows from | |
|
39 | // the bytes buffer owned by `on_disk`. | |
|
40 | let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map)); | |
|
41 | ||
|
42 | // Erase the pointed type entirely in order to erase the lifetime. | |
|
43 | let ptr: *mut () = ptr.cast(); | |
|
44 | ||
|
45 | Ok((Self { on_disk, ptr }, parents)) | |
|
46 | } | |
|
47 | ||
|
48 | pub fn get_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> { | |
|
49 | // SAFETY: We cast the type-erased pointer back to the same type it had | |
|
50 | // in `new`, except with a different lifetime parameter. This time we | |
|
51 | // connect the lifetime to that of `self`. This cast is valid because | |
|
52 | // `self` owns the same `PyBytes` whose buffer `DirstateMap` | |
|
53 | // references. That buffer has a stable memory address because the byte | |
|
54 | // string value of a `PyBytes` is immutable. | |
|
55 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); | |
|
56 | // SAFETY: we dereference that pointer, connecting the lifetime of the | |
|
57 | // new `&mut` to that of `self`. This is valid because the | |
|
58 | // raw pointer is to a boxed value, and `self` owns that box. | |
|
59 | unsafe { &mut *ptr } | |
|
60 | } | |
|
61 | ||
|
62 | pub fn get<'a>(&'a self) -> &'a DirstateMap<'a> { | |
|
63 | // SAFETY: same reasoning as in `get_mut` above. | |
|
64 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); | |
|
65 | unsafe { &*ptr } | |
|
66 | } | |
|
67 | } | |
|
68 | ||
|
69 | impl Drop for OwningDirstateMap { | |
|
70 | fn drop(&mut self) { | |
|
71 | // Silence a "field is never read" warning, and demonstrate that this | |
|
72 | // value is still alive. | |
|
73 | let _ = &self.on_disk; | |
|
74 | // SAFETY: this cast is the same as in `get_mut`, and is valid for the | |
|
75 | // same reason. `self.on_disk` still exists at this point, drop glue | |
|
76 | // will drop it implicitly after this `drop` method returns. | |
|
77 | let ptr: *mut DirstateMap<'_> = self.ptr.cast(); | |
|
78 | // SAFETY: `Box::from_raw` takes ownership of the box away from `self`. | |
|
79 | // This is fine because drop glue does nothig for `*mut ()` and we’re | |
|
80 | // in `drop`, so `get` and `get_mut` cannot be called again. | |
|
81 | unsafe { drop(Box::from_raw(ptr)) } | |
|
82 | } | |
|
83 | } | |
|
84 | ||
|
85 | fn _static_assert_is_send<T: Send>() {} | |
|
86 | ||
|
87 | fn _static_assert_fields_are_send() { | |
|
88 | _static_assert_is_send::<PyBytes>(); | |
|
89 | _static_assert_is_send::<Box<DirstateMap<'_>>>(); | |
|
90 | } | |
|
91 | ||
|
92 | // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because | |
|
93 | // thread-safety of raw pointers is unknown in the general case. However this | |
|
94 | // particular raw pointer represents a `Box<DirstateMap<'on_disk>>` that we | |
|
95 | // own. Since that `Box` and `PyBytes` are both `Send` as shown in above, it | |
|
96 | // is sound to mark this struct as `Send` too. | |
|
97 | unsafe impl Send for OwningDirstateMap {} |
@@ -284,10 +284,10 b' impl DirstateMap {' | |||
|
284 | 284 | } |
|
285 | 285 | |
|
286 | 286 | #[timed] |
|
287 |
pub fn read |
|
|
287 | pub fn read( | |
|
288 | 288 | &mut self, |
|
289 |
file_contents: & |
|
|
290 |
) -> Result<Option< |
|
|
289 | file_contents: &[u8], | |
|
290 | ) -> Result<Option<DirstateParents>, DirstateError> { | |
|
291 | 291 | if file_contents.is_empty() { |
|
292 | 292 | return Ok(None); |
|
293 | 293 | } |
@@ -303,7 +303,7 b' impl DirstateMap {' | |||
|
303 | 303 | .into_iter() |
|
304 | 304 | .map(|(path, copy)| (path.to_owned(), copy.to_owned())), |
|
305 | 305 | ); |
|
306 | Ok(Some(parents)) | |
|
306 | Ok(Some(parents.clone())) | |
|
307 | 307 | } |
|
308 | 308 | |
|
309 | 309 | pub fn pack( |
@@ -24,7 +24,10 b' use crate::StateMapIter;' | |||
|
24 | 24 | use crate::StatusError; |
|
25 | 25 | use crate::StatusOptions; |
|
26 | 26 | |
|
27 | pub struct DirstateMap { | |
|
27 | pub struct DirstateMap<'on_disk> { | |
|
28 | /// Contents of the `.hg/dirstate` file | |
|
29 | on_disk: &'on_disk [u8], | |
|
30 | ||
|
28 | 31 | pub(super) root: ChildNodes, |
|
29 | 32 | |
|
30 | 33 | /// Number of nodes anywhere in the tree that have `.entry.is_some()`. |
@@ -69,13 +72,58 b" type NodeDataMut<'a> = (" | |||
|
69 | 72 | &'a mut Option<HgPathBuf>, |
|
70 | 73 | ); |
|
71 | 74 | |
|
72 | impl DirstateMap { | |
|
73 |
pub fn new( |
|
|
74 | Self { | |
|
75 | impl<'on_disk> DirstateMap<'on_disk> { | |
|
76 | pub fn new( | |
|
77 | on_disk: &'on_disk [u8], | |
|
78 | ) -> Result<(Self, Option<DirstateParents>), DirstateError> { | |
|
79 | let mut map = Self { | |
|
80 | on_disk, | |
|
75 | 81 | root: ChildNodes::default(), |
|
76 | 82 | nodes_with_entry_count: 0, |
|
77 | 83 | nodes_with_copy_source_count: 0, |
|
84 | }; | |
|
85 | let parents = map.read()?; | |
|
86 | Ok((map, parents)) | |
|
87 | } | |
|
88 | ||
|
89 | /// Should only be called in `new` | |
|
90 | #[timed] | |
|
91 | fn read(&mut self) -> Result<Option<DirstateParents>, DirstateError> { | |
|
92 | if self.on_disk.is_empty() { | |
|
93 | return Ok(None); | |
|
78 | 94 | } |
|
95 | ||
|
96 | let parents = parse_dirstate_entries( | |
|
97 | self.on_disk, | |
|
98 | |path, entry, copy_source| { | |
|
99 | let tracked = entry.state.is_tracked(); | |
|
100 | let node = Self::get_or_insert_node_tracing_ancestors( | |
|
101 | &mut self.root, | |
|
102 | path, | |
|
103 | |ancestor| { | |
|
104 | if tracked { | |
|
105 | ancestor.tracked_descendants_count += 1 | |
|
106 | } | |
|
107 | }, | |
|
108 | ); | |
|
109 | assert!( | |
|
110 | node.entry.is_none(), | |
|
111 | "duplicate dirstate entry in read" | |
|
112 | ); | |
|
113 | assert!( | |
|
114 | node.copy_source.is_none(), | |
|
115 | "duplicate dirstate entry in read" | |
|
116 | ); | |
|
117 | node.entry = Some(*entry); | |
|
118 | node.copy_source = copy_source.map(HgPath::to_owned); | |
|
119 | self.nodes_with_entry_count += 1; | |
|
120 | if copy_source.is_some() { | |
|
121 | self.nodes_with_copy_source_count += 1 | |
|
122 | } | |
|
123 | }, | |
|
124 | )?; | |
|
125 | ||
|
126 | Ok(Some(parents.clone())) | |
|
79 | 127 | } |
|
80 | 128 | |
|
81 | 129 | fn get_node(&self, path: &HgPath) -> Option<&Node> { |
@@ -280,7 +328,7 b' impl DirstateMap {' | |||
|
280 | 328 | } |
|
281 | 329 | } |
|
282 | 330 | |
|
283 | impl super::dispatch::DirstateMapMethods for DirstateMap { | |
|
331 | impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> { | |
|
284 | 332 | fn clear(&mut self) { |
|
285 | 333 | self.root.clear(); |
|
286 | 334 | self.nodes_with_entry_count = 0; |
@@ -443,48 +491,6 b' impl super::dispatch::DirstateMapMethods' | |||
|
443 | 491 | } |
|
444 | 492 | } |
|
445 | 493 | |
|
446 | #[timed] | |
|
447 | fn read<'a>( | |
|
448 | &mut self, | |
|
449 | file_contents: &'a [u8], | |
|
450 | ) -> Result<Option<&'a DirstateParents>, DirstateError> { | |
|
451 | if file_contents.is_empty() { | |
|
452 | return Ok(None); | |
|
453 | } | |
|
454 | ||
|
455 | let parents = parse_dirstate_entries( | |
|
456 | file_contents, | |
|
457 | |path, entry, copy_source| { | |
|
458 | let tracked = entry.state.is_tracked(); | |
|
459 | let node = Self::get_or_insert_node_tracing_ancestors( | |
|
460 | &mut self.root, | |
|
461 | path, | |
|
462 | |ancestor| { | |
|
463 | if tracked { | |
|
464 | ancestor.tracked_descendants_count += 1 | |
|
465 | } | |
|
466 | }, | |
|
467 | ); | |
|
468 | assert!( | |
|
469 | node.entry.is_none(), | |
|
470 | "duplicate dirstate entry in read" | |
|
471 | ); | |
|
472 | assert!( | |
|
473 | node.copy_source.is_none(), | |
|
474 | "duplicate dirstate entry in read" | |
|
475 | ); | |
|
476 | node.entry = Some(*entry); | |
|
477 | node.copy_source = copy_source.map(HgPath::to_owned); | |
|
478 | self.nodes_with_entry_count += 1; | |
|
479 | if copy_source.is_some() { | |
|
480 | self.nodes_with_copy_source_count += 1 | |
|
481 | } | |
|
482 | }, | |
|
483 | )?; | |
|
484 | ||
|
485 | Ok(Some(parents)) | |
|
486 | } | |
|
487 | ||
|
488 | 494 | fn pack( |
|
489 | 495 | &mut self, |
|
490 | 496 | parents: DirstateParents, |
@@ -73,11 +73,6 b' pub trait DirstateMapMethods {' | |||
|
73 | 73 | directory: &HgPath, |
|
74 | 74 | ) -> Result<bool, DirstateMapError>; |
|
75 | 75 | |
|
76 | fn read<'a>( | |
|
77 | &mut self, | |
|
78 | file_contents: &'a [u8], | |
|
79 | ) -> Result<Option<&'a DirstateParents>, DirstateError>; | |
|
80 | ||
|
81 | 76 | fn pack( |
|
82 | 77 | &mut self, |
|
83 | 78 | parents: DirstateParents, |
@@ -216,13 +211,6 b' impl DirstateMapMethods for DirstateMap ' | |||
|
216 | 211 | self.has_dir(directory) |
|
217 | 212 | } |
|
218 | 213 | |
|
219 | fn read<'a>( | |
|
220 | &mut self, | |
|
221 | file_contents: &'a [u8], | |
|
222 | ) -> Result<Option<&'a DirstateParents>, DirstateError> { | |
|
223 | self.read(file_contents) | |
|
224 | } | |
|
225 | ||
|
226 | 214 | fn pack( |
|
227 | 215 | &mut self, |
|
228 | 216 | parents: DirstateParents, |
@@ -12,7 +12,9 b'' | |||
|
12 | 12 | mod copymap; |
|
13 | 13 | mod dirs_multiset; |
|
14 | 14 | mod dirstate_map; |
|
15 | mod dispatch; | |
|
15 | 16 | mod non_normal_entries; |
|
17 | mod owning; | |
|
16 | 18 | mod status; |
|
17 | 19 | use crate::{ |
|
18 | 20 | dirstate::{ |
@@ -22,6 +22,7 b' use crate::{' | |||
|
22 | 22 | dirstate::non_normal_entries::{ |
|
23 | 23 | NonNormalEntries, NonNormalEntriesIterator, |
|
24 | 24 | }, |
|
25 | dirstate::owning::OwningDirstateMap, | |
|
25 | 26 | dirstate::{dirs_multiset::Dirs, make_dirstate_tuple}, |
|
26 | 27 | parsers::dirstate_parents_to_pytuple, |
|
27 | 28 | }; |
@@ -58,12 +59,13 b' py_class!(pub class DirstateMap |py| {' | |||
|
58 | 59 | let dirstate_error = |_: DirstateError| { |
|
59 | 60 | PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()) |
|
60 | 61 | }; |
|
61 | let bytes = on_disk.data(py); | |
|
62 | 62 | let (inner, parents) = if use_dirstate_tree { |
|
63 | let mut map = hg::dirstate_tree::dirstate_map::DirstateMap::new(); | |
|
64 | let parents = map.read(bytes).map_err(dirstate_error)?; | |
|
63 | let (map, parents) = | |
|
64 | OwningDirstateMap::new(py, on_disk) | |
|
65 | .map_err(dirstate_error)?; | |
|
65 | 66 | (Box::new(map) as _, parents) |
|
66 | 67 | } else { |
|
68 | let bytes = on_disk.data(py); | |
|
67 | 69 | let mut map = RustDirstateMap::default(); |
|
68 | 70 | let parents = map.read(bytes).map_err(dirstate_error)?; |
|
69 | 71 | (Box::new(map) as _, parents) |
General Comments 0
You need to be logged in to leave comments.
Login now