##// END OF EJS Templates
dirstate-tree: Make `DirstateMap` borrow from a bytes buffer...
Simon Sapin -
r47893:d8ac6237 default
parent child Browse files
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 #[timed]
286 #[timed]
287 pub fn read<'a>(
287 pub fn read(
288 &mut self,
288 &mut self,
289 file_contents: &'a [u8],
289 file_contents: &[u8],
290 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
290 ) -> Result<Option<DirstateParents>, DirstateError> {
291 if file_contents.is_empty() {
291 if file_contents.is_empty() {
292 return Ok(None);
292 return Ok(None);
293 }
293 }
@@ -303,7 +303,7 b' impl DirstateMap {'
303 .into_iter()
303 .into_iter()
304 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
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 pub fn pack(
309 pub fn pack(
@@ -24,7 +24,10 b' use crate::StateMapIter;'
24 use crate::StatusError;
24 use crate::StatusError;
25 use crate::StatusOptions;
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 pub(super) root: ChildNodes,
31 pub(super) root: ChildNodes,
29
32
30 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
33 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
@@ -69,13 +72,58 b" type NodeDataMut<'a> = ("
69 &'a mut Option<HgPathBuf>,
72 &'a mut Option<HgPathBuf>,
70 );
73 );
71
74
72 impl DirstateMap {
75 impl<'on_disk> DirstateMap<'on_disk> {
73 pub fn new() -> Self {
76 pub fn new(
74 Self {
77 on_disk: &'on_disk [u8],
78 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
79 let mut map = Self {
80 on_disk,
75 root: ChildNodes::default(),
81 root: ChildNodes::default(),
76 nodes_with_entry_count: 0,
82 nodes_with_entry_count: 0,
77 nodes_with_copy_source_count: 0,
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 fn get_node(&self, path: &HgPath) -> Option<&Node> {
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 fn clear(&mut self) {
332 fn clear(&mut self) {
285 self.root.clear();
333 self.root.clear();
286 self.nodes_with_entry_count = 0;
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 fn pack(
494 fn pack(
489 &mut self,
495 &mut self,
490 parents: DirstateParents,
496 parents: DirstateParents,
@@ -73,11 +73,6 b' pub trait DirstateMapMethods {'
73 directory: &HgPath,
73 directory: &HgPath,
74 ) -> Result<bool, DirstateMapError>;
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 fn pack(
76 fn pack(
82 &mut self,
77 &mut self,
83 parents: DirstateParents,
78 parents: DirstateParents,
@@ -216,13 +211,6 b' impl DirstateMapMethods for DirstateMap '
216 self.has_dir(directory)
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 fn pack(
214 fn pack(
227 &mut self,
215 &mut self,
228 parents: DirstateParents,
216 parents: DirstateParents,
@@ -12,7 +12,9 b''
12 mod copymap;
12 mod copymap;
13 mod dirs_multiset;
13 mod dirs_multiset;
14 mod dirstate_map;
14 mod dirstate_map;
15 mod dispatch;
15 mod non_normal_entries;
16 mod non_normal_entries;
17 mod owning;
16 mod status;
18 mod status;
17 use crate::{
19 use crate::{
18 dirstate::{
20 dirstate::{
@@ -22,6 +22,7 b' use crate::{'
22 dirstate::non_normal_entries::{
22 dirstate::non_normal_entries::{
23 NonNormalEntries, NonNormalEntriesIterator,
23 NonNormalEntries, NonNormalEntriesIterator,
24 },
24 },
25 dirstate::owning::OwningDirstateMap,
25 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
26 dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
26 parsers::dirstate_parents_to_pytuple,
27 parsers::dirstate_parents_to_pytuple,
27 };
28 };
@@ -58,12 +59,13 b' py_class!(pub class DirstateMap |py| {'
58 let dirstate_error = |_: DirstateError| {
59 let dirstate_error = |_: DirstateError| {
59 PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string())
60 PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string())
60 };
61 };
61 let bytes = on_disk.data(py);
62 let (inner, parents) = if use_dirstate_tree {
62 let (inner, parents) = if use_dirstate_tree {
63 let mut map = hg::dirstate_tree::dirstate_map::DirstateMap::new();
63 let (map, parents) =
64 let parents = map.read(bytes).map_err(dirstate_error)?;
64 OwningDirstateMap::new(py, on_disk)
65 .map_err(dirstate_error)?;
65 (Box::new(map) as _, parents)
66 (Box::new(map) as _, parents)
66 } else {
67 } else {
68 let bytes = on_disk.data(py);
67 let mut map = RustDirstateMap::default();
69 let mut map = RustDirstateMap::default();
68 let parents = map.read(bytes).map_err(dirstate_error)?;
70 let parents = map.read(bytes).map_err(dirstate_error)?;
69 (Box::new(map) as _, parents)
71 (Box::new(map) as _, parents)
General Comments 0
You need to be logged in to leave comments. Login now