##// END OF EJS Templates
rust-dirstatemap: properly decrement counter for tracked descendants...
Raphaël Gomès -
r49866:fbc02ccc stable
parent child Browse files
Show More
@@ -1,1196 +1,1195 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::path::PathBuf;
4 use std::path::PathBuf;
5
5
6 use super::on_disk;
6 use super::on_disk;
7 use super::on_disk::DirstateV2ParseError;
7 use super::on_disk::DirstateV2ParseError;
8 use super::owning::OwningDirstateMap;
8 use super::owning::OwningDirstateMap;
9 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::parse_dirstate_entries;
13 use crate::dirstate::CopyMapIter;
13 use crate::dirstate::CopyMapIter;
14 use crate::dirstate::StateMapIter;
14 use crate::dirstate::StateMapIter;
15 use crate::dirstate::TruncatedTimestamp;
15 use crate::dirstate::TruncatedTimestamp;
16 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
16 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
17 use crate::dirstate::SIZE_NON_NORMAL;
17 use crate::dirstate::SIZE_NON_NORMAL;
18 use crate::matchers::Matcher;
18 use crate::matchers::Matcher;
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::DirstateEntry;
20 use crate::DirstateEntry;
21 use crate::DirstateError;
21 use crate::DirstateError;
22 use crate::DirstateParents;
22 use crate::DirstateParents;
23 use crate::DirstateStatus;
23 use crate::DirstateStatus;
24 use crate::EntryState;
24 use crate::EntryState;
25 use crate::FastHashMap;
25 use crate::FastHashMap;
26 use crate::PatternFileWarning;
26 use crate::PatternFileWarning;
27 use crate::StatusError;
27 use crate::StatusError;
28 use crate::StatusOptions;
28 use crate::StatusOptions;
29
29
30 /// Append to an existing data file if the amount of unreachable data (not used
30 /// Append to an existing data file if the amount of unreachable data (not used
31 /// anymore) is less than this fraction of the total amount of existing data.
31 /// anymore) is less than this fraction of the total amount of existing data.
32 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
32 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33
33
34 pub struct DirstateMap<'on_disk> {
34 pub struct DirstateMap<'on_disk> {
35 /// Contents of the `.hg/dirstate` file
35 /// Contents of the `.hg/dirstate` file
36 pub(super) on_disk: &'on_disk [u8],
36 pub(super) on_disk: &'on_disk [u8],
37
37
38 pub(super) root: ChildNodes<'on_disk>,
38 pub(super) root: ChildNodes<'on_disk>,
39
39
40 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
40 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
41 pub(super) nodes_with_entry_count: u32,
41 pub(super) nodes_with_entry_count: u32,
42
42
43 /// Number of nodes anywhere in the tree that have
43 /// Number of nodes anywhere in the tree that have
44 /// `.copy_source.is_some()`.
44 /// `.copy_source.is_some()`.
45 pub(super) nodes_with_copy_source_count: u32,
45 pub(super) nodes_with_copy_source_count: u32,
46
46
47 /// See on_disk::Header
47 /// See on_disk::Header
48 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
48 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
49
49
50 /// How many bytes of `on_disk` are not used anymore
50 /// How many bytes of `on_disk` are not used anymore
51 pub(super) unreachable_bytes: u32,
51 pub(super) unreachable_bytes: u32,
52 }
52 }
53
53
54 /// Using a plain `HgPathBuf` of the full path from the repository root as a
54 /// Using a plain `HgPathBuf` of the full path from the repository root as a
55 /// map key would also work: all paths in a given map have the same parent
55 /// map key would also work: all paths in a given map have the same parent
56 /// path, so comparing full paths gives the same result as comparing base
56 /// path, so comparing full paths gives the same result as comparing base
57 /// names. However `HashMap` would waste time always re-hashing the same
57 /// names. However `HashMap` would waste time always re-hashing the same
58 /// string prefix.
58 /// string prefix.
59 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
59 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
60
60
61 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
61 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
62 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
62 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
63 pub(super) enum BorrowedPath<'tree, 'on_disk> {
63 pub(super) enum BorrowedPath<'tree, 'on_disk> {
64 InMemory(&'tree HgPathBuf),
64 InMemory(&'tree HgPathBuf),
65 OnDisk(&'on_disk HgPath),
65 OnDisk(&'on_disk HgPath),
66 }
66 }
67
67
68 pub(super) enum ChildNodes<'on_disk> {
68 pub(super) enum ChildNodes<'on_disk> {
69 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
69 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
70 OnDisk(&'on_disk [on_disk::Node]),
70 OnDisk(&'on_disk [on_disk::Node]),
71 }
71 }
72
72
73 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
73 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
74 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
74 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
75 OnDisk(&'on_disk [on_disk::Node]),
75 OnDisk(&'on_disk [on_disk::Node]),
76 }
76 }
77
77
78 pub(super) enum NodeRef<'tree, 'on_disk> {
78 pub(super) enum NodeRef<'tree, 'on_disk> {
79 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
79 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
80 OnDisk(&'on_disk on_disk::Node),
80 OnDisk(&'on_disk on_disk::Node),
81 }
81 }
82
82
83 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
83 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
84 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
84 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
85 match *self {
85 match *self {
86 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
86 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
87 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
87 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
88 }
88 }
89 }
89 }
90 }
90 }
91
91
92 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
92 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
93 type Target = HgPath;
93 type Target = HgPath;
94
94
95 fn deref(&self) -> &HgPath {
95 fn deref(&self) -> &HgPath {
96 match *self {
96 match *self {
97 BorrowedPath::InMemory(in_memory) => in_memory,
97 BorrowedPath::InMemory(in_memory) => in_memory,
98 BorrowedPath::OnDisk(on_disk) => on_disk,
98 BorrowedPath::OnDisk(on_disk) => on_disk,
99 }
99 }
100 }
100 }
101 }
101 }
102
102
103 impl Default for ChildNodes<'_> {
103 impl Default for ChildNodes<'_> {
104 fn default() -> Self {
104 fn default() -> Self {
105 ChildNodes::InMemory(Default::default())
105 ChildNodes::InMemory(Default::default())
106 }
106 }
107 }
107 }
108
108
109 impl<'on_disk> ChildNodes<'on_disk> {
109 impl<'on_disk> ChildNodes<'on_disk> {
110 pub(super) fn as_ref<'tree>(
110 pub(super) fn as_ref<'tree>(
111 &'tree self,
111 &'tree self,
112 ) -> ChildNodesRef<'tree, 'on_disk> {
112 ) -> ChildNodesRef<'tree, 'on_disk> {
113 match self {
113 match self {
114 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
114 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
115 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
115 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
116 }
116 }
117 }
117 }
118
118
119 pub(super) fn is_empty(&self) -> bool {
119 pub(super) fn is_empty(&self) -> bool {
120 match self {
120 match self {
121 ChildNodes::InMemory(nodes) => nodes.is_empty(),
121 ChildNodes::InMemory(nodes) => nodes.is_empty(),
122 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
122 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
123 }
123 }
124 }
124 }
125
125
126 fn make_mut(
126 fn make_mut(
127 &mut self,
127 &mut self,
128 on_disk: &'on_disk [u8],
128 on_disk: &'on_disk [u8],
129 unreachable_bytes: &mut u32,
129 unreachable_bytes: &mut u32,
130 ) -> Result<
130 ) -> Result<
131 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
131 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
132 DirstateV2ParseError,
132 DirstateV2ParseError,
133 > {
133 > {
134 match self {
134 match self {
135 ChildNodes::InMemory(nodes) => Ok(nodes),
135 ChildNodes::InMemory(nodes) => Ok(nodes),
136 ChildNodes::OnDisk(nodes) => {
136 ChildNodes::OnDisk(nodes) => {
137 *unreachable_bytes +=
137 *unreachable_bytes +=
138 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
138 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
139 let nodes = nodes
139 let nodes = nodes
140 .iter()
140 .iter()
141 .map(|node| {
141 .map(|node| {
142 Ok((
142 Ok((
143 node.path(on_disk)?,
143 node.path(on_disk)?,
144 node.to_in_memory_node(on_disk)?,
144 node.to_in_memory_node(on_disk)?,
145 ))
145 ))
146 })
146 })
147 .collect::<Result<_, _>>()?;
147 .collect::<Result<_, _>>()?;
148 *self = ChildNodes::InMemory(nodes);
148 *self = ChildNodes::InMemory(nodes);
149 match self {
149 match self {
150 ChildNodes::InMemory(nodes) => Ok(nodes),
150 ChildNodes::InMemory(nodes) => Ok(nodes),
151 ChildNodes::OnDisk(_) => unreachable!(),
151 ChildNodes::OnDisk(_) => unreachable!(),
152 }
152 }
153 }
153 }
154 }
154 }
155 }
155 }
156 }
156 }
157
157
158 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
158 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
159 pub(super) fn get(
159 pub(super) fn get(
160 &self,
160 &self,
161 base_name: &HgPath,
161 base_name: &HgPath,
162 on_disk: &'on_disk [u8],
162 on_disk: &'on_disk [u8],
163 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
163 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
164 match self {
164 match self {
165 ChildNodesRef::InMemory(nodes) => Ok(nodes
165 ChildNodesRef::InMemory(nodes) => Ok(nodes
166 .get_key_value(base_name)
166 .get_key_value(base_name)
167 .map(|(k, v)| NodeRef::InMemory(k, v))),
167 .map(|(k, v)| NodeRef::InMemory(k, v))),
168 ChildNodesRef::OnDisk(nodes) => {
168 ChildNodesRef::OnDisk(nodes) => {
169 let mut parse_result = Ok(());
169 let mut parse_result = Ok(());
170 let search_result = nodes.binary_search_by(|node| {
170 let search_result = nodes.binary_search_by(|node| {
171 match node.base_name(on_disk) {
171 match node.base_name(on_disk) {
172 Ok(node_base_name) => node_base_name.cmp(base_name),
172 Ok(node_base_name) => node_base_name.cmp(base_name),
173 Err(e) => {
173 Err(e) => {
174 parse_result = Err(e);
174 parse_result = Err(e);
175 // Dummy comparison result, `search_result` won’t
175 // Dummy comparison result, `search_result` won’t
176 // be used since `parse_result` is an error
176 // be used since `parse_result` is an error
177 std::cmp::Ordering::Equal
177 std::cmp::Ordering::Equal
178 }
178 }
179 }
179 }
180 });
180 });
181 parse_result.map(|()| {
181 parse_result.map(|()| {
182 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
182 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
183 })
183 })
184 }
184 }
185 }
185 }
186 }
186 }
187
187
188 /// Iterate in undefined order
188 /// Iterate in undefined order
189 pub(super) fn iter(
189 pub(super) fn iter(
190 &self,
190 &self,
191 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
191 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
192 match self {
192 match self {
193 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
193 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
194 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
194 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
195 ),
195 ),
196 ChildNodesRef::OnDisk(nodes) => {
196 ChildNodesRef::OnDisk(nodes) => {
197 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
197 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
198 }
198 }
199 }
199 }
200 }
200 }
201
201
202 /// Iterate in parallel in undefined order
202 /// Iterate in parallel in undefined order
203 pub(super) fn par_iter(
203 pub(super) fn par_iter(
204 &self,
204 &self,
205 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
205 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
206 {
206 {
207 use rayon::prelude::*;
207 use rayon::prelude::*;
208 match self {
208 match self {
209 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
209 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
210 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
210 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
211 ),
211 ),
212 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
212 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
213 nodes.par_iter().map(NodeRef::OnDisk),
213 nodes.par_iter().map(NodeRef::OnDisk),
214 ),
214 ),
215 }
215 }
216 }
216 }
217
217
218 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
218 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
219 match self {
219 match self {
220 ChildNodesRef::InMemory(nodes) => {
220 ChildNodesRef::InMemory(nodes) => {
221 let mut vec: Vec<_> = nodes
221 let mut vec: Vec<_> = nodes
222 .iter()
222 .iter()
223 .map(|(k, v)| NodeRef::InMemory(k, v))
223 .map(|(k, v)| NodeRef::InMemory(k, v))
224 .collect();
224 .collect();
225 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
225 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
226 match node {
226 match node {
227 NodeRef::InMemory(path, _node) => path.base_name(),
227 NodeRef::InMemory(path, _node) => path.base_name(),
228 NodeRef::OnDisk(_) => unreachable!(),
228 NodeRef::OnDisk(_) => unreachable!(),
229 }
229 }
230 }
230 }
231 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
231 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
232 // value: https://github.com/rust-lang/rust/issues/34162
232 // value: https://github.com/rust-lang/rust/issues/34162
233 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
233 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
234 vec
234 vec
235 }
235 }
236 ChildNodesRef::OnDisk(nodes) => {
236 ChildNodesRef::OnDisk(nodes) => {
237 // Nodes on disk are already sorted
237 // Nodes on disk are already sorted
238 nodes.iter().map(NodeRef::OnDisk).collect()
238 nodes.iter().map(NodeRef::OnDisk).collect()
239 }
239 }
240 }
240 }
241 }
241 }
242 }
242 }
243
243
244 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
244 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
245 pub(super) fn full_path(
245 pub(super) fn full_path(
246 &self,
246 &self,
247 on_disk: &'on_disk [u8],
247 on_disk: &'on_disk [u8],
248 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
248 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
249 match self {
249 match self {
250 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
250 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
251 NodeRef::OnDisk(node) => node.full_path(on_disk),
251 NodeRef::OnDisk(node) => node.full_path(on_disk),
252 }
252 }
253 }
253 }
254
254
255 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
255 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
256 /// HgPath>` detached from `'tree`
256 /// HgPath>` detached from `'tree`
257 pub(super) fn full_path_borrowed(
257 pub(super) fn full_path_borrowed(
258 &self,
258 &self,
259 on_disk: &'on_disk [u8],
259 on_disk: &'on_disk [u8],
260 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
260 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
261 match self {
261 match self {
262 NodeRef::InMemory(path, _node) => match path.full_path() {
262 NodeRef::InMemory(path, _node) => match path.full_path() {
263 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
263 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
264 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
264 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
265 },
265 },
266 NodeRef::OnDisk(node) => {
266 NodeRef::OnDisk(node) => {
267 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
267 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
268 }
268 }
269 }
269 }
270 }
270 }
271
271
272 pub(super) fn base_name(
272 pub(super) fn base_name(
273 &self,
273 &self,
274 on_disk: &'on_disk [u8],
274 on_disk: &'on_disk [u8],
275 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
275 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
276 match self {
276 match self {
277 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
277 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
278 NodeRef::OnDisk(node) => node.base_name(on_disk),
278 NodeRef::OnDisk(node) => node.base_name(on_disk),
279 }
279 }
280 }
280 }
281
281
282 pub(super) fn children(
282 pub(super) fn children(
283 &self,
283 &self,
284 on_disk: &'on_disk [u8],
284 on_disk: &'on_disk [u8],
285 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
285 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
286 match self {
286 match self {
287 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
287 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
288 NodeRef::OnDisk(node) => {
288 NodeRef::OnDisk(node) => {
289 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
289 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
290 }
290 }
291 }
291 }
292 }
292 }
293
293
294 pub(super) fn has_copy_source(&self) -> bool {
294 pub(super) fn has_copy_source(&self) -> bool {
295 match self {
295 match self {
296 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
296 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
297 NodeRef::OnDisk(node) => node.has_copy_source(),
297 NodeRef::OnDisk(node) => node.has_copy_source(),
298 }
298 }
299 }
299 }
300
300
301 pub(super) fn copy_source(
301 pub(super) fn copy_source(
302 &self,
302 &self,
303 on_disk: &'on_disk [u8],
303 on_disk: &'on_disk [u8],
304 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
304 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
305 match self {
305 match self {
306 NodeRef::InMemory(_path, node) => {
306 NodeRef::InMemory(_path, node) => {
307 Ok(node.copy_source.as_ref().map(|s| &**s))
307 Ok(node.copy_source.as_ref().map(|s| &**s))
308 }
308 }
309 NodeRef::OnDisk(node) => node.copy_source(on_disk),
309 NodeRef::OnDisk(node) => node.copy_source(on_disk),
310 }
310 }
311 }
311 }
312 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
312 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
313 /// HgPath>` detached from `'tree`
313 /// HgPath>` detached from `'tree`
314 pub(super) fn copy_source_borrowed(
314 pub(super) fn copy_source_borrowed(
315 &self,
315 &self,
316 on_disk: &'on_disk [u8],
316 on_disk: &'on_disk [u8],
317 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
317 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
318 {
318 {
319 Ok(match self {
319 Ok(match self {
320 NodeRef::InMemory(_path, node) => {
320 NodeRef::InMemory(_path, node) => {
321 node.copy_source.as_ref().map(|source| match source {
321 node.copy_source.as_ref().map(|source| match source {
322 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
322 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
323 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
323 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
324 })
324 })
325 }
325 }
326 NodeRef::OnDisk(node) => node
326 NodeRef::OnDisk(node) => node
327 .copy_source(on_disk)?
327 .copy_source(on_disk)?
328 .map(|source| BorrowedPath::OnDisk(source)),
328 .map(|source| BorrowedPath::OnDisk(source)),
329 })
329 })
330 }
330 }
331
331
332 pub(super) fn entry(
332 pub(super) fn entry(
333 &self,
333 &self,
334 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
334 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
335 match self {
335 match self {
336 NodeRef::InMemory(_path, node) => {
336 NodeRef::InMemory(_path, node) => {
337 Ok(node.data.as_entry().copied())
337 Ok(node.data.as_entry().copied())
338 }
338 }
339 NodeRef::OnDisk(node) => node.entry(),
339 NodeRef::OnDisk(node) => node.entry(),
340 }
340 }
341 }
341 }
342
342
343 pub(super) fn state(
343 pub(super) fn state(
344 &self,
344 &self,
345 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
345 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
346 Ok(self.entry()?.map(|e| e.state()))
346 Ok(self.entry()?.map(|e| e.state()))
347 }
347 }
348
348
349 pub(super) fn cached_directory_mtime(
349 pub(super) fn cached_directory_mtime(
350 &self,
350 &self,
351 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
351 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
352 match self {
352 match self {
353 NodeRef::InMemory(_path, node) => Ok(match node.data {
353 NodeRef::InMemory(_path, node) => Ok(match node.data {
354 NodeData::CachedDirectory { mtime } => Some(mtime),
354 NodeData::CachedDirectory { mtime } => Some(mtime),
355 _ => None,
355 _ => None,
356 }),
356 }),
357 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
357 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
358 }
358 }
359 }
359 }
360
360
361 pub(super) fn descendants_with_entry_count(&self) -> u32 {
361 pub(super) fn descendants_with_entry_count(&self) -> u32 {
362 match self {
362 match self {
363 NodeRef::InMemory(_path, node) => {
363 NodeRef::InMemory(_path, node) => {
364 node.descendants_with_entry_count
364 node.descendants_with_entry_count
365 }
365 }
366 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
366 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
367 }
367 }
368 }
368 }
369
369
370 pub(super) fn tracked_descendants_count(&self) -> u32 {
370 pub(super) fn tracked_descendants_count(&self) -> u32 {
371 match self {
371 match self {
372 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
372 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
373 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
373 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
374 }
374 }
375 }
375 }
376 }
376 }
377
377
378 /// Represents a file or a directory
378 /// Represents a file or a directory
379 #[derive(Default)]
379 #[derive(Default)]
380 pub(super) struct Node<'on_disk> {
380 pub(super) struct Node<'on_disk> {
381 pub(super) data: NodeData,
381 pub(super) data: NodeData,
382
382
383 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
383 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
384
384
385 pub(super) children: ChildNodes<'on_disk>,
385 pub(super) children: ChildNodes<'on_disk>,
386
386
387 /// How many (non-inclusive) descendants of this node have an entry.
387 /// How many (non-inclusive) descendants of this node have an entry.
388 pub(super) descendants_with_entry_count: u32,
388 pub(super) descendants_with_entry_count: u32,
389
389
390 /// How many (non-inclusive) descendants of this node have an entry whose
390 /// How many (non-inclusive) descendants of this node have an entry whose
391 /// state is "tracked".
391 /// state is "tracked".
392 pub(super) tracked_descendants_count: u32,
392 pub(super) tracked_descendants_count: u32,
393 }
393 }
394
394
395 pub(super) enum NodeData {
395 pub(super) enum NodeData {
396 Entry(DirstateEntry),
396 Entry(DirstateEntry),
397 CachedDirectory { mtime: TruncatedTimestamp },
397 CachedDirectory { mtime: TruncatedTimestamp },
398 None,
398 None,
399 }
399 }
400
400
401 impl Default for NodeData {
401 impl Default for NodeData {
402 fn default() -> Self {
402 fn default() -> Self {
403 NodeData::None
403 NodeData::None
404 }
404 }
405 }
405 }
406
406
407 impl NodeData {
407 impl NodeData {
408 fn has_entry(&self) -> bool {
408 fn has_entry(&self) -> bool {
409 match self {
409 match self {
410 NodeData::Entry(_) => true,
410 NodeData::Entry(_) => true,
411 _ => false,
411 _ => false,
412 }
412 }
413 }
413 }
414
414
415 fn as_entry(&self) -> Option<&DirstateEntry> {
415 fn as_entry(&self) -> Option<&DirstateEntry> {
416 match self {
416 match self {
417 NodeData::Entry(entry) => Some(entry),
417 NodeData::Entry(entry) => Some(entry),
418 _ => None,
418 _ => None,
419 }
419 }
420 }
420 }
421 }
421 }
422
422
423 impl<'on_disk> DirstateMap<'on_disk> {
423 impl<'on_disk> DirstateMap<'on_disk> {
424 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
424 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
425 Self {
425 Self {
426 on_disk,
426 on_disk,
427 root: ChildNodes::default(),
427 root: ChildNodes::default(),
428 nodes_with_entry_count: 0,
428 nodes_with_entry_count: 0,
429 nodes_with_copy_source_count: 0,
429 nodes_with_copy_source_count: 0,
430 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
430 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
431 unreachable_bytes: 0,
431 unreachable_bytes: 0,
432 }
432 }
433 }
433 }
434
434
435 #[timed]
435 #[timed]
436 pub fn new_v2(
436 pub fn new_v2(
437 on_disk: &'on_disk [u8],
437 on_disk: &'on_disk [u8],
438 data_size: usize,
438 data_size: usize,
439 metadata: &[u8],
439 metadata: &[u8],
440 ) -> Result<Self, DirstateError> {
440 ) -> Result<Self, DirstateError> {
441 if let Some(data) = on_disk.get(..data_size) {
441 if let Some(data) = on_disk.get(..data_size) {
442 Ok(on_disk::read(data, metadata)?)
442 Ok(on_disk::read(data, metadata)?)
443 } else {
443 } else {
444 Err(DirstateV2ParseError.into())
444 Err(DirstateV2ParseError.into())
445 }
445 }
446 }
446 }
447
447
448 #[timed]
448 #[timed]
449 pub fn new_v1(
449 pub fn new_v1(
450 on_disk: &'on_disk [u8],
450 on_disk: &'on_disk [u8],
451 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
451 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
452 let mut map = Self::empty(on_disk);
452 let mut map = Self::empty(on_disk);
453 if map.on_disk.is_empty() {
453 if map.on_disk.is_empty() {
454 return Ok((map, None));
454 return Ok((map, None));
455 }
455 }
456
456
457 let parents = parse_dirstate_entries(
457 let parents = parse_dirstate_entries(
458 map.on_disk,
458 map.on_disk,
459 |path, entry, copy_source| {
459 |path, entry, copy_source| {
460 let tracked = entry.state().is_tracked();
460 let tracked = entry.state().is_tracked();
461 let node = Self::get_or_insert_node(
461 let node = Self::get_or_insert_node(
462 map.on_disk,
462 map.on_disk,
463 &mut map.unreachable_bytes,
463 &mut map.unreachable_bytes,
464 &mut map.root,
464 &mut map.root,
465 path,
465 path,
466 WithBasename::to_cow_borrowed,
466 WithBasename::to_cow_borrowed,
467 |ancestor| {
467 |ancestor| {
468 if tracked {
468 if tracked {
469 ancestor.tracked_descendants_count += 1
469 ancestor.tracked_descendants_count += 1
470 }
470 }
471 ancestor.descendants_with_entry_count += 1
471 ancestor.descendants_with_entry_count += 1
472 },
472 },
473 )?;
473 )?;
474 assert!(
474 assert!(
475 !node.data.has_entry(),
475 !node.data.has_entry(),
476 "duplicate dirstate entry in read"
476 "duplicate dirstate entry in read"
477 );
477 );
478 assert!(
478 assert!(
479 node.copy_source.is_none(),
479 node.copy_source.is_none(),
480 "duplicate dirstate entry in read"
480 "duplicate dirstate entry in read"
481 );
481 );
482 node.data = NodeData::Entry(*entry);
482 node.data = NodeData::Entry(*entry);
483 node.copy_source = copy_source.map(Cow::Borrowed);
483 node.copy_source = copy_source.map(Cow::Borrowed);
484 map.nodes_with_entry_count += 1;
484 map.nodes_with_entry_count += 1;
485 if copy_source.is_some() {
485 if copy_source.is_some() {
486 map.nodes_with_copy_source_count += 1
486 map.nodes_with_copy_source_count += 1
487 }
487 }
488 Ok(())
488 Ok(())
489 },
489 },
490 )?;
490 )?;
491 let parents = Some(parents.clone());
491 let parents = Some(parents.clone());
492
492
493 Ok((map, parents))
493 Ok((map, parents))
494 }
494 }
495
495
496 /// Assuming dirstate-v2 format, returns whether the next write should
496 /// Assuming dirstate-v2 format, returns whether the next write should
497 /// append to the existing data file that contains `self.on_disk` (true),
497 /// append to the existing data file that contains `self.on_disk` (true),
498 /// or create a new data file from scratch (false).
498 /// or create a new data file from scratch (false).
499 pub(super) fn write_should_append(&self) -> bool {
499 pub(super) fn write_should_append(&self) -> bool {
500 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
500 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
501 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
501 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
502 }
502 }
503
503
504 fn get_node<'tree>(
504 fn get_node<'tree>(
505 &'tree self,
505 &'tree self,
506 path: &HgPath,
506 path: &HgPath,
507 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
507 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
508 let mut children = self.root.as_ref();
508 let mut children = self.root.as_ref();
509 let mut components = path.components();
509 let mut components = path.components();
510 let mut component =
510 let mut component =
511 components.next().expect("expected at least one components");
511 components.next().expect("expected at least one components");
512 loop {
512 loop {
513 if let Some(child) = children.get(component, self.on_disk)? {
513 if let Some(child) = children.get(component, self.on_disk)? {
514 if let Some(next_component) = components.next() {
514 if let Some(next_component) = components.next() {
515 component = next_component;
515 component = next_component;
516 children = child.children(self.on_disk)?;
516 children = child.children(self.on_disk)?;
517 } else {
517 } else {
518 return Ok(Some(child));
518 return Ok(Some(child));
519 }
519 }
520 } else {
520 } else {
521 return Ok(None);
521 return Ok(None);
522 }
522 }
523 }
523 }
524 }
524 }
525
525
526 /// Returns a mutable reference to the node at `path` if it exists
526 /// Returns a mutable reference to the node at `path` if it exists
527 ///
527 ///
528 /// This takes `root` instead of `&mut self` so that callers can mutate
528 /// This takes `root` instead of `&mut self` so that callers can mutate
529 /// other fields while the returned borrow is still valid
529 /// other fields while the returned borrow is still valid
530 fn get_node_mut<'tree>(
530 fn get_node_mut<'tree>(
531 on_disk: &'on_disk [u8],
531 on_disk: &'on_disk [u8],
532 unreachable_bytes: &mut u32,
532 unreachable_bytes: &mut u32,
533 root: &'tree mut ChildNodes<'on_disk>,
533 root: &'tree mut ChildNodes<'on_disk>,
534 path: &HgPath,
534 path: &HgPath,
535 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
535 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
536 let mut children = root;
536 let mut children = root;
537 let mut components = path.components();
537 let mut components = path.components();
538 let mut component =
538 let mut component =
539 components.next().expect("expected at least one components");
539 components.next().expect("expected at least one components");
540 loop {
540 loop {
541 if let Some(child) = children
541 if let Some(child) = children
542 .make_mut(on_disk, unreachable_bytes)?
542 .make_mut(on_disk, unreachable_bytes)?
543 .get_mut(component)
543 .get_mut(component)
544 {
544 {
545 if let Some(next_component) = components.next() {
545 if let Some(next_component) = components.next() {
546 component = next_component;
546 component = next_component;
547 children = &mut child.children;
547 children = &mut child.children;
548 } else {
548 } else {
549 return Ok(Some(child));
549 return Ok(Some(child));
550 }
550 }
551 } else {
551 } else {
552 return Ok(None);
552 return Ok(None);
553 }
553 }
554 }
554 }
555 }
555 }
556
556
557 pub(super) fn get_or_insert<'tree, 'path>(
557 pub(super) fn get_or_insert<'tree, 'path>(
558 &'tree mut self,
558 &'tree mut self,
559 path: &HgPath,
559 path: &HgPath,
560 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
560 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
561 Self::get_or_insert_node(
561 Self::get_or_insert_node(
562 self.on_disk,
562 self.on_disk,
563 &mut self.unreachable_bytes,
563 &mut self.unreachable_bytes,
564 &mut self.root,
564 &mut self.root,
565 path,
565 path,
566 WithBasename::to_cow_owned,
566 WithBasename::to_cow_owned,
567 |_| {},
567 |_| {},
568 )
568 )
569 }
569 }
570
570
571 fn get_or_insert_node<'tree, 'path>(
571 fn get_or_insert_node<'tree, 'path>(
572 on_disk: &'on_disk [u8],
572 on_disk: &'on_disk [u8],
573 unreachable_bytes: &mut u32,
573 unreachable_bytes: &mut u32,
574 root: &'tree mut ChildNodes<'on_disk>,
574 root: &'tree mut ChildNodes<'on_disk>,
575 path: &'path HgPath,
575 path: &'path HgPath,
576 to_cow: impl Fn(
576 to_cow: impl Fn(
577 WithBasename<&'path HgPath>,
577 WithBasename<&'path HgPath>,
578 ) -> WithBasename<Cow<'on_disk, HgPath>>,
578 ) -> WithBasename<Cow<'on_disk, HgPath>>,
579 mut each_ancestor: impl FnMut(&mut Node),
579 mut each_ancestor: impl FnMut(&mut Node),
580 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
580 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
581 let mut child_nodes = root;
581 let mut child_nodes = root;
582 let mut inclusive_ancestor_paths =
582 let mut inclusive_ancestor_paths =
583 WithBasename::inclusive_ancestors_of(path);
583 WithBasename::inclusive_ancestors_of(path);
584 let mut ancestor_path = inclusive_ancestor_paths
584 let mut ancestor_path = inclusive_ancestor_paths
585 .next()
585 .next()
586 .expect("expected at least one inclusive ancestor");
586 .expect("expected at least one inclusive ancestor");
587 loop {
587 loop {
588 // TODO: can we avoid allocating an owned key in cases where the
588 // TODO: can we avoid allocating an owned key in cases where the
589 // map already contains that key, without introducing double
589 // map already contains that key, without introducing double
590 // lookup?
590 // lookup?
591 let child_node = child_nodes
591 let child_node = child_nodes
592 .make_mut(on_disk, unreachable_bytes)?
592 .make_mut(on_disk, unreachable_bytes)?
593 .entry(to_cow(ancestor_path))
593 .entry(to_cow(ancestor_path))
594 .or_default();
594 .or_default();
595 if let Some(next) = inclusive_ancestor_paths.next() {
595 if let Some(next) = inclusive_ancestor_paths.next() {
596 each_ancestor(child_node);
596 each_ancestor(child_node);
597 ancestor_path = next;
597 ancestor_path = next;
598 child_nodes = &mut child_node.children;
598 child_nodes = &mut child_node.children;
599 } else {
599 } else {
600 return Ok(child_node);
600 return Ok(child_node);
601 }
601 }
602 }
602 }
603 }
603 }
604
604
605 fn add_or_remove_file(
605 fn add_or_remove_file(
606 &mut self,
606 &mut self,
607 path: &HgPath,
607 path: &HgPath,
608 old_state: Option<EntryState>,
608 old_state: Option<EntryState>,
609 new_entry: DirstateEntry,
609 new_entry: DirstateEntry,
610 ) -> Result<(), DirstateV2ParseError> {
610 ) -> Result<(), DirstateV2ParseError> {
611 let had_entry = old_state.is_some();
611 let had_entry = old_state.is_some();
612 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
612 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
613 let tracked_count_increment =
613 let tracked_count_increment =
614 match (was_tracked, new_entry.state().is_tracked()) {
614 match (was_tracked, new_entry.state().is_tracked()) {
615 (false, true) => 1,
615 (false, true) => 1,
616 (true, false) => -1,
616 (true, false) => -1,
617 _ => 0,
617 _ => 0,
618 };
618 };
619
619
620 let node = Self::get_or_insert_node(
620 let node = Self::get_or_insert_node(
621 self.on_disk,
621 self.on_disk,
622 &mut self.unreachable_bytes,
622 &mut self.unreachable_bytes,
623 &mut self.root,
623 &mut self.root,
624 path,
624 path,
625 WithBasename::to_cow_owned,
625 WithBasename::to_cow_owned,
626 |ancestor| {
626 |ancestor| {
627 if !had_entry {
627 if !had_entry {
628 ancestor.descendants_with_entry_count += 1;
628 ancestor.descendants_with_entry_count += 1;
629 }
629 }
630
630
631 // We can’t use `+= increment` because the counter is unsigned,
631 // We can’t use `+= increment` because the counter is unsigned,
632 // and we want debug builds to detect accidental underflow
632 // and we want debug builds to detect accidental underflow
633 // through zero
633 // through zero
634 match tracked_count_increment {
634 match tracked_count_increment {
635 1 => ancestor.tracked_descendants_count += 1,
635 1 => ancestor.tracked_descendants_count += 1,
636 -1 => ancestor.tracked_descendants_count -= 1,
636 -1 => ancestor.tracked_descendants_count -= 1,
637 _ => {}
637 _ => {}
638 }
638 }
639 },
639 },
640 )?;
640 )?;
641 if !had_entry {
641 if !had_entry {
642 self.nodes_with_entry_count += 1
642 self.nodes_with_entry_count += 1
643 }
643 }
644 node.data = NodeData::Entry(new_entry);
644 node.data = NodeData::Entry(new_entry);
645 Ok(())
645 Ok(())
646 }
646 }
647
647
648 fn iter_nodes<'tree>(
648 fn iter_nodes<'tree>(
649 &'tree self,
649 &'tree self,
650 ) -> impl Iterator<
650 ) -> impl Iterator<
651 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
651 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
652 > + 'tree {
652 > + 'tree {
653 // Depth first tree traversal.
653 // Depth first tree traversal.
654 //
654 //
655 // If we could afford internal iteration and recursion,
655 // If we could afford internal iteration and recursion,
656 // this would look like:
656 // this would look like:
657 //
657 //
658 // ```
658 // ```
659 // fn traverse_children(
659 // fn traverse_children(
660 // children: &ChildNodes,
660 // children: &ChildNodes,
661 // each: &mut impl FnMut(&Node),
661 // each: &mut impl FnMut(&Node),
662 // ) {
662 // ) {
663 // for child in children.values() {
663 // for child in children.values() {
664 // traverse_children(&child.children, each);
664 // traverse_children(&child.children, each);
665 // each(child);
665 // each(child);
666 // }
666 // }
667 // }
667 // }
668 // ```
668 // ```
669 //
669 //
670 // However we want an external iterator and therefore can’t use the
670 // However we want an external iterator and therefore can’t use the
671 // call stack. Use an explicit stack instead:
671 // call stack. Use an explicit stack instead:
672 let mut stack = Vec::new();
672 let mut stack = Vec::new();
673 let mut iter = self.root.as_ref().iter();
673 let mut iter = self.root.as_ref().iter();
674 std::iter::from_fn(move || {
674 std::iter::from_fn(move || {
675 while let Some(child_node) = iter.next() {
675 while let Some(child_node) = iter.next() {
676 let children = match child_node.children(self.on_disk) {
676 let children = match child_node.children(self.on_disk) {
677 Ok(children) => children,
677 Ok(children) => children,
678 Err(error) => return Some(Err(error)),
678 Err(error) => return Some(Err(error)),
679 };
679 };
680 // Pseudo-recursion
680 // Pseudo-recursion
681 let new_iter = children.iter();
681 let new_iter = children.iter();
682 let old_iter = std::mem::replace(&mut iter, new_iter);
682 let old_iter = std::mem::replace(&mut iter, new_iter);
683 stack.push((child_node, old_iter));
683 stack.push((child_node, old_iter));
684 }
684 }
685 // Found the end of a `children.iter()` iterator.
685 // Found the end of a `children.iter()` iterator.
686 if let Some((child_node, next_iter)) = stack.pop() {
686 if let Some((child_node, next_iter)) = stack.pop() {
687 // "Return" from pseudo-recursion by restoring state from the
687 // "Return" from pseudo-recursion by restoring state from the
688 // explicit stack
688 // explicit stack
689 iter = next_iter;
689 iter = next_iter;
690
690
691 Some(Ok(child_node))
691 Some(Ok(child_node))
692 } else {
692 } else {
693 // Reached the bottom of the stack, we’re done
693 // Reached the bottom of the stack, we’re done
694 None
694 None
695 }
695 }
696 })
696 })
697 }
697 }
698
698
699 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
699 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
700 if let Cow::Borrowed(path) = path {
700 if let Cow::Borrowed(path) = path {
701 *unreachable_bytes += path.len() as u32
701 *unreachable_bytes += path.len() as u32
702 }
702 }
703 }
703 }
704 }
704 }
705
705
706 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
706 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
707 ///
707 ///
708 /// The callback is only called for incoming `Ok` values. Errors are passed
708 /// The callback is only called for incoming `Ok` values. Errors are passed
709 /// through as-is. In order to let it use the `?` operator the callback is
709 /// through as-is. In order to let it use the `?` operator the callback is
710 /// expected to return a `Result` of `Option`, instead of an `Option` of
710 /// expected to return a `Result` of `Option`, instead of an `Option` of
711 /// `Result`.
711 /// `Result`.
712 fn filter_map_results<'a, I, F, A, B, E>(
712 fn filter_map_results<'a, I, F, A, B, E>(
713 iter: I,
713 iter: I,
714 f: F,
714 f: F,
715 ) -> impl Iterator<Item = Result<B, E>> + 'a
715 ) -> impl Iterator<Item = Result<B, E>> + 'a
716 where
716 where
717 I: Iterator<Item = Result<A, E>> + 'a,
717 I: Iterator<Item = Result<A, E>> + 'a,
718 F: Fn(A) -> Result<Option<B>, E> + 'a,
718 F: Fn(A) -> Result<Option<B>, E> + 'a,
719 {
719 {
720 iter.filter_map(move |result| match result {
720 iter.filter_map(move |result| match result {
721 Ok(node) => f(node).transpose(),
721 Ok(node) => f(node).transpose(),
722 Err(e) => Some(Err(e)),
722 Err(e) => Some(Err(e)),
723 })
723 })
724 }
724 }
725
725
726 impl OwningDirstateMap {
726 impl OwningDirstateMap {
727 pub fn clear(&mut self) {
727 pub fn clear(&mut self) {
728 self.with_dmap_mut(|map| {
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
735 pub fn set_entry(
735 pub fn set_entry(
736 &mut self,
736 &mut self,
737 filename: &HgPath,
737 filename: &HgPath,
738 entry: DirstateEntry,
738 entry: DirstateEntry,
739 ) -> Result<(), DirstateV2ParseError> {
739 ) -> Result<(), DirstateV2ParseError> {
740 self.with_dmap_mut(|map| {
740 self.with_dmap_mut(|map| {
741 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
741 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
742 Ok(())
742 Ok(())
743 })
743 })
744 }
744 }
745
745
746 pub fn add_file(
746 pub fn add_file(
747 &mut self,
747 &mut self,
748 filename: &HgPath,
748 filename: &HgPath,
749 entry: DirstateEntry,
749 entry: DirstateEntry,
750 ) -> Result<(), DirstateError> {
750 ) -> Result<(), DirstateError> {
751 let old_state = self.get(filename)?.map(|e| e.state());
751 let old_state = self.get(filename)?.map(|e| e.state());
752 self.with_dmap_mut(|map| {
752 self.with_dmap_mut(|map| {
753 Ok(map.add_or_remove_file(filename, old_state, entry)?)
753 Ok(map.add_or_remove_file(filename, old_state, entry)?)
754 })
754 })
755 }
755 }
756
756
757 pub fn remove_file(
757 pub fn remove_file(
758 &mut self,
758 &mut self,
759 filename: &HgPath,
759 filename: &HgPath,
760 in_merge: bool,
760 in_merge: bool,
761 ) -> Result<(), DirstateError> {
761 ) -> Result<(), DirstateError> {
762 let old_entry_opt = self.get(filename)?;
762 let old_entry_opt = self.get(filename)?;
763 let old_state = old_entry_opt.map(|e| e.state());
763 let old_state = old_entry_opt.map(|e| e.state());
764 let mut size = 0;
764 let mut size = 0;
765 if in_merge {
765 if in_merge {
766 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
766 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
767 // during a merge. So I (marmoute) am not sure we need the
767 // during a merge. So I (marmoute) am not sure we need the
768 // conditionnal at all. Adding double checking this with assert
768 // conditionnal at all. Adding double checking this with assert
769 // would be nice.
769 // would be nice.
770 if let Some(old_entry) = old_entry_opt {
770 if let Some(old_entry) = old_entry_opt {
771 // backup the previous state
771 // backup the previous state
772 if old_entry.state() == EntryState::Merged {
772 if old_entry.state() == EntryState::Merged {
773 size = SIZE_NON_NORMAL;
773 size = SIZE_NON_NORMAL;
774 } else if old_entry.state() == EntryState::Normal
774 } else if old_entry.state() == EntryState::Normal
775 && old_entry.size() == SIZE_FROM_OTHER_PARENT
775 && old_entry.size() == SIZE_FROM_OTHER_PARENT
776 {
776 {
777 // other parent
777 // other parent
778 size = SIZE_FROM_OTHER_PARENT;
778 size = SIZE_FROM_OTHER_PARENT;
779 }
779 }
780 }
780 }
781 }
781 }
782 if size == 0 {
782 if size == 0 {
783 self.copy_map_remove(filename)?;
783 self.copy_map_remove(filename)?;
784 }
784 }
785 self.with_dmap_mut(|map| {
785 self.with_dmap_mut(|map| {
786 let entry = DirstateEntry::new_removed(size);
786 let entry = DirstateEntry::new_removed(size);
787 Ok(map.add_or_remove_file(filename, old_state, entry)?)
787 Ok(map.add_or_remove_file(filename, old_state, entry)?)
788 })
788 })
789 }
789 }
790
790
791 pub fn drop_entry_and_copy_source(
791 pub fn drop_entry_and_copy_source(
792 &mut self,
792 &mut self,
793 filename: &HgPath,
793 filename: &HgPath,
794 ) -> Result<(), DirstateError> {
794 ) -> Result<(), DirstateError> {
795 let was_tracked = self
795 let was_tracked = self
796 .get(filename)?
796 .get(filename)?
797 .map_or(false, |e| e.state().is_tracked());
797 .map_or(false, |e| e.state().is_tracked());
798 struct Dropped {
798 struct Dropped {
799 was_tracked: bool,
799 was_tracked: bool,
800 had_entry: bool,
800 had_entry: bool,
801 had_copy_source: bool,
801 had_copy_source: bool,
802 }
802 }
803
803
804 /// If this returns `Ok(Some((dropped, removed)))`, then
804 /// If this returns `Ok(Some((dropped, removed)))`, then
805 ///
805 ///
806 /// * `dropped` is about the leaf node that was at `filename`
806 /// * `dropped` is about the leaf node that was at `filename`
807 /// * `removed` is whether this particular level of recursion just
807 /// * `removed` is whether this particular level of recursion just
808 /// removed a node in `nodes`.
808 /// removed a node in `nodes`.
809 fn recur<'on_disk>(
809 fn recur<'on_disk>(
810 on_disk: &'on_disk [u8],
810 on_disk: &'on_disk [u8],
811 unreachable_bytes: &mut u32,
811 unreachable_bytes: &mut u32,
812 nodes: &mut ChildNodes<'on_disk>,
812 nodes: &mut ChildNodes<'on_disk>,
813 path: &HgPath,
813 path: &HgPath,
814 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
814 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
815 let (first_path_component, rest_of_path) =
815 let (first_path_component, rest_of_path) =
816 path.split_first_component();
816 path.split_first_component();
817 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
817 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
818 let node = if let Some(node) = nodes.get_mut(first_path_component)
818 let node = if let Some(node) = nodes.get_mut(first_path_component)
819 {
819 {
820 node
820 node
821 } else {
821 } else {
822 return Ok(None);
822 return Ok(None);
823 };
823 };
824 let dropped;
824 let dropped;
825 if let Some(rest) = rest_of_path {
825 if let Some(rest) = rest_of_path {
826 if let Some((d, removed)) = recur(
826 if let Some((d, removed)) = recur(
827 on_disk,
827 on_disk,
828 unreachable_bytes,
828 unreachable_bytes,
829 &mut node.children,
829 &mut node.children,
830 rest,
830 rest,
831 )? {
831 )? {
832 dropped = d;
832 dropped = d;
833 if dropped.had_entry {
833 if dropped.had_entry {
834 node.descendants_with_entry_count = node
834 node.descendants_with_entry_count = node
835 .descendants_with_entry_count
835 .descendants_with_entry_count
836 .checked_sub(1)
836 .checked_sub(1)
837 .expect(
837 .expect(
838 "descendants_with_entry_count should be >= 0",
838 "descendants_with_entry_count should be >= 0",
839 );
839 );
840 }
840 }
841 if dropped.was_tracked {
841 if dropped.was_tracked {
842 node.tracked_descendants_count = node
842 node.tracked_descendants_count = node
843 .tracked_descendants_count
843 .tracked_descendants_count
844 .checked_sub(1)
844 .checked_sub(1)
845 .expect(
845 .expect(
846 "tracked_descendants_count should be >= 0",
846 "tracked_descendants_count should be >= 0",
847 );
847 );
848 }
848 }
849
849
850 // Directory caches must be invalidated when removing a
850 // Directory caches must be invalidated when removing a
851 // child node
851 // child node
852 if removed {
852 if removed {
853 if let NodeData::CachedDirectory { .. } = &node.data {
853 if let NodeData::CachedDirectory { .. } = &node.data {
854 node.data = NodeData::None
854 node.data = NodeData::None
855 }
855 }
856 }
856 }
857 } else {
857 } else {
858 return Ok(None);
858 return Ok(None);
859 }
859 }
860 } else {
860 } else {
861 let had_entry = node.data.has_entry();
861 let entry = node.data.as_entry();
862 let was_tracked = entry.map_or(false, |entry| entry.tracked());
863 let had_entry = entry.is_some();
862 if had_entry {
864 if had_entry {
863 node.data = NodeData::None
865 node.data = NodeData::None
864 }
866 }
865 if let Some(source) = &node.copy_source {
867 if let Some(source) = &node.copy_source {
866 DirstateMap::count_dropped_path(unreachable_bytes, source);
868 DirstateMap::count_dropped_path(unreachable_bytes, source);
867 node.copy_source = None
869 node.copy_source = None
868 }
870 }
869 dropped = Dropped {
871 dropped = Dropped {
870 was_tracked: node
872 was_tracked,
871 .data
872 .as_entry()
873 .map_or(false, |entry| entry.state().is_tracked()),
874 had_entry,
873 had_entry,
875 had_copy_source: node.copy_source.take().is_some(),
874 had_copy_source: node.copy_source.take().is_some(),
876 };
875 };
877 }
876 }
878 // After recursion, for both leaf (rest_of_path is None) nodes and
877 // After recursion, for both leaf (rest_of_path is None) nodes and
879 // parent nodes, remove a node if it just became empty.
878 // parent nodes, remove a node if it just became empty.
880 let remove = !node.data.has_entry()
879 let remove = !node.data.has_entry()
881 && node.copy_source.is_none()
880 && node.copy_source.is_none()
882 && node.children.is_empty();
881 && node.children.is_empty();
883 if remove {
882 if remove {
884 let (key, _) =
883 let (key, _) =
885 nodes.remove_entry(first_path_component).unwrap();
884 nodes.remove_entry(first_path_component).unwrap();
886 DirstateMap::count_dropped_path(
885 DirstateMap::count_dropped_path(
887 unreachable_bytes,
886 unreachable_bytes,
888 key.full_path(),
887 key.full_path(),
889 )
888 )
890 }
889 }
891 Ok(Some((dropped, remove)))
890 Ok(Some((dropped, remove)))
892 }
891 }
893
892
894 self.with_dmap_mut(|map| {
893 self.with_dmap_mut(|map| {
895 if let Some((dropped, _removed)) = recur(
894 if let Some((dropped, _removed)) = recur(
896 map.on_disk,
895 map.on_disk,
897 &mut map.unreachable_bytes,
896 &mut map.unreachable_bytes,
898 &mut map.root,
897 &mut map.root,
899 filename,
898 filename,
900 )? {
899 )? {
901 if dropped.had_entry {
900 if dropped.had_entry {
902 map.nodes_with_entry_count = map
901 map.nodes_with_entry_count = map
903 .nodes_with_entry_count
902 .nodes_with_entry_count
904 .checked_sub(1)
903 .checked_sub(1)
905 .expect("nodes_with_entry_count should be >= 0");
904 .expect("nodes_with_entry_count should be >= 0");
906 }
905 }
907 if dropped.had_copy_source {
906 if dropped.had_copy_source {
908 map.nodes_with_copy_source_count = map
907 map.nodes_with_copy_source_count = map
909 .nodes_with_copy_source_count
908 .nodes_with_copy_source_count
910 .checked_sub(1)
909 .checked_sub(1)
911 .expect("nodes_with_copy_source_count should be >= 0");
910 .expect("nodes_with_copy_source_count should be >= 0");
912 }
911 }
913 } else {
912 } else {
914 debug_assert!(!was_tracked);
913 debug_assert!(!was_tracked);
915 }
914 }
916 Ok(())
915 Ok(())
917 })
916 })
918 }
917 }
919
918
920 pub fn has_tracked_dir(
919 pub fn has_tracked_dir(
921 &mut self,
920 &mut self,
922 directory: &HgPath,
921 directory: &HgPath,
923 ) -> Result<bool, DirstateError> {
922 ) -> Result<bool, DirstateError> {
924 self.with_dmap_mut(|map| {
923 self.with_dmap_mut(|map| {
925 if let Some(node) = map.get_node(directory)? {
924 if let Some(node) = map.get_node(directory)? {
926 // A node without a `DirstateEntry` was created to hold child
925 // A node without a `DirstateEntry` was created to hold child
927 // nodes, and is therefore a directory.
926 // nodes, and is therefore a directory.
928 let state = node.state()?;
927 let state = node.state()?;
929 Ok(state.is_none() && node.tracked_descendants_count() > 0)
928 Ok(state.is_none() && node.tracked_descendants_count() > 0)
930 } else {
929 } else {
931 Ok(false)
930 Ok(false)
932 }
931 }
933 })
932 })
934 }
933 }
935
934
936 pub fn has_dir(
935 pub fn has_dir(
937 &mut self,
936 &mut self,
938 directory: &HgPath,
937 directory: &HgPath,
939 ) -> Result<bool, DirstateError> {
938 ) -> Result<bool, DirstateError> {
940 self.with_dmap_mut(|map| {
939 self.with_dmap_mut(|map| {
941 if let Some(node) = map.get_node(directory)? {
940 if let Some(node) = map.get_node(directory)? {
942 // A node without a `DirstateEntry` was created to hold child
941 // A node without a `DirstateEntry` was created to hold child
943 // nodes, and is therefore a directory.
942 // nodes, and is therefore a directory.
944 let state = node.state()?;
943 let state = node.state()?;
945 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
944 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
946 } else {
945 } else {
947 Ok(false)
946 Ok(false)
948 }
947 }
949 })
948 })
950 }
949 }
951
950
952 #[timed]
951 #[timed]
953 pub fn pack_v1(
952 pub fn pack_v1(
954 &self,
953 &self,
955 parents: DirstateParents,
954 parents: DirstateParents,
956 ) -> Result<Vec<u8>, DirstateError> {
955 ) -> Result<Vec<u8>, DirstateError> {
957 let map = self.get_map();
956 let map = self.get_map();
958 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
957 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
959 // reallocations
958 // reallocations
960 let mut size = parents.as_bytes().len();
959 let mut size = parents.as_bytes().len();
961 for node in map.iter_nodes() {
960 for node in map.iter_nodes() {
962 let node = node?;
961 let node = node?;
963 if node.entry()?.is_some() {
962 if node.entry()?.is_some() {
964 size += packed_entry_size(
963 size += packed_entry_size(
965 node.full_path(map.on_disk)?,
964 node.full_path(map.on_disk)?,
966 node.copy_source(map.on_disk)?,
965 node.copy_source(map.on_disk)?,
967 );
966 );
968 }
967 }
969 }
968 }
970
969
971 let mut packed = Vec::with_capacity(size);
970 let mut packed = Vec::with_capacity(size);
972 packed.extend(parents.as_bytes());
971 packed.extend(parents.as_bytes());
973
972
974 for node in map.iter_nodes() {
973 for node in map.iter_nodes() {
975 let node = node?;
974 let node = node?;
976 if let Some(entry) = node.entry()? {
975 if let Some(entry) = node.entry()? {
977 pack_entry(
976 pack_entry(
978 node.full_path(map.on_disk)?,
977 node.full_path(map.on_disk)?,
979 &entry,
978 &entry,
980 node.copy_source(map.on_disk)?,
979 node.copy_source(map.on_disk)?,
981 &mut packed,
980 &mut packed,
982 );
981 );
983 }
982 }
984 }
983 }
985 Ok(packed)
984 Ok(packed)
986 }
985 }
987
986
988 /// Returns new data and metadata together with whether that data should be
987 /// Returns new data and metadata together with whether that data should be
989 /// appended to the existing data file whose content is at
988 /// appended to the existing data file whose content is at
990 /// `map.on_disk` (true), instead of written to a new data file
989 /// `map.on_disk` (true), instead of written to a new data file
991 /// (false).
990 /// (false).
992 #[timed]
991 #[timed]
993 pub fn pack_v2(
992 pub fn pack_v2(
994 &self,
993 &self,
995 can_append: bool,
994 can_append: bool,
996 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
995 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
997 let map = self.get_map();
996 let map = self.get_map();
998 on_disk::write(map, can_append)
997 on_disk::write(map, can_append)
999 }
998 }
1000
999
1001 /// `callback` allows the caller to process and do something with the
1000 /// `callback` allows the caller to process and do something with the
1002 /// results of the status. This is needed to do so efficiently (i.e.
1001 /// results of the status. This is needed to do so efficiently (i.e.
1003 /// without cloning the `DirstateStatus` object with its paths) because
1002 /// without cloning the `DirstateStatus` object with its paths) because
1004 /// we need to borrow from `Self`.
1003 /// we need to borrow from `Self`.
1005 pub fn with_status<R>(
1004 pub fn with_status<R>(
1006 &mut self,
1005 &mut self,
1007 matcher: &(dyn Matcher + Sync),
1006 matcher: &(dyn Matcher + Sync),
1008 root_dir: PathBuf,
1007 root_dir: PathBuf,
1009 ignore_files: Vec<PathBuf>,
1008 ignore_files: Vec<PathBuf>,
1010 options: StatusOptions,
1009 options: StatusOptions,
1011 callback: impl for<'r> FnOnce(
1010 callback: impl for<'r> FnOnce(
1012 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1011 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1013 ) -> R,
1012 ) -> R,
1014 ) -> R {
1013 ) -> R {
1015 self.with_dmap_mut(|map| {
1014 self.with_dmap_mut(|map| {
1016 callback(super::status::status(
1015 callback(super::status::status(
1017 map,
1016 map,
1018 matcher,
1017 matcher,
1019 root_dir,
1018 root_dir,
1020 ignore_files,
1019 ignore_files,
1021 options,
1020 options,
1022 ))
1021 ))
1023 })
1022 })
1024 }
1023 }
1025
1024
1026 pub fn copy_map_len(&self) -> usize {
1025 pub fn copy_map_len(&self) -> usize {
1027 let map = self.get_map();
1026 let map = self.get_map();
1028 map.nodes_with_copy_source_count as usize
1027 map.nodes_with_copy_source_count as usize
1029 }
1028 }
1030
1029
1031 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1030 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1032 let map = self.get_map();
1031 let map = self.get_map();
1033 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1032 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1034 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1033 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1035 Some((node.full_path(map.on_disk)?, source))
1034 Some((node.full_path(map.on_disk)?, source))
1036 } else {
1035 } else {
1037 None
1036 None
1038 })
1037 })
1039 }))
1038 }))
1040 }
1039 }
1041
1040
1042 pub fn copy_map_contains_key(
1041 pub fn copy_map_contains_key(
1043 &self,
1042 &self,
1044 key: &HgPath,
1043 key: &HgPath,
1045 ) -> Result<bool, DirstateV2ParseError> {
1044 ) -> Result<bool, DirstateV2ParseError> {
1046 let map = self.get_map();
1045 let map = self.get_map();
1047 Ok(if let Some(node) = map.get_node(key)? {
1046 Ok(if let Some(node) = map.get_node(key)? {
1048 node.has_copy_source()
1047 node.has_copy_source()
1049 } else {
1048 } else {
1050 false
1049 false
1051 })
1050 })
1052 }
1051 }
1053
1052
1054 pub fn copy_map_get(
1053 pub fn copy_map_get(
1055 &self,
1054 &self,
1056 key: &HgPath,
1055 key: &HgPath,
1057 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1056 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1058 let map = self.get_map();
1057 let map = self.get_map();
1059 if let Some(node) = map.get_node(key)? {
1058 if let Some(node) = map.get_node(key)? {
1060 if let Some(source) = node.copy_source(map.on_disk)? {
1059 if let Some(source) = node.copy_source(map.on_disk)? {
1061 return Ok(Some(source));
1060 return Ok(Some(source));
1062 }
1061 }
1063 }
1062 }
1064 Ok(None)
1063 Ok(None)
1065 }
1064 }
1066
1065
1067 pub fn copy_map_remove(
1066 pub fn copy_map_remove(
1068 &mut self,
1067 &mut self,
1069 key: &HgPath,
1068 key: &HgPath,
1070 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1069 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1071 self.with_dmap_mut(|map| {
1070 self.with_dmap_mut(|map| {
1072 let count = &mut map.nodes_with_copy_source_count;
1071 let count = &mut map.nodes_with_copy_source_count;
1073 let unreachable_bytes = &mut map.unreachable_bytes;
1072 let unreachable_bytes = &mut map.unreachable_bytes;
1074 Ok(DirstateMap::get_node_mut(
1073 Ok(DirstateMap::get_node_mut(
1075 map.on_disk,
1074 map.on_disk,
1076 unreachable_bytes,
1075 unreachable_bytes,
1077 &mut map.root,
1076 &mut map.root,
1078 key,
1077 key,
1079 )?
1078 )?
1080 .and_then(|node| {
1079 .and_then(|node| {
1081 if let Some(source) = &node.copy_source {
1080 if let Some(source) = &node.copy_source {
1082 *count -= 1;
1081 *count -= 1;
1083 DirstateMap::count_dropped_path(unreachable_bytes, source);
1082 DirstateMap::count_dropped_path(unreachable_bytes, source);
1084 }
1083 }
1085 node.copy_source.take().map(Cow::into_owned)
1084 node.copy_source.take().map(Cow::into_owned)
1086 }))
1085 }))
1087 })
1086 })
1088 }
1087 }
1089
1088
1090 pub fn copy_map_insert(
1089 pub fn copy_map_insert(
1091 &mut self,
1090 &mut self,
1092 key: HgPathBuf,
1091 key: HgPathBuf,
1093 value: HgPathBuf,
1092 value: HgPathBuf,
1094 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1093 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1095 self.with_dmap_mut(|map| {
1094 self.with_dmap_mut(|map| {
1096 let node = DirstateMap::get_or_insert_node(
1095 let node = DirstateMap::get_or_insert_node(
1097 map.on_disk,
1096 map.on_disk,
1098 &mut map.unreachable_bytes,
1097 &mut map.unreachable_bytes,
1099 &mut map.root,
1098 &mut map.root,
1100 &key,
1099 &key,
1101 WithBasename::to_cow_owned,
1100 WithBasename::to_cow_owned,
1102 |_ancestor| {},
1101 |_ancestor| {},
1103 )?;
1102 )?;
1104 if node.copy_source.is_none() {
1103 if node.copy_source.is_none() {
1105 map.nodes_with_copy_source_count += 1
1104 map.nodes_with_copy_source_count += 1
1106 }
1105 }
1107 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1106 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1108 })
1107 })
1109 }
1108 }
1110
1109
1111 pub fn len(&self) -> usize {
1110 pub fn len(&self) -> usize {
1112 let map = self.get_map();
1111 let map = self.get_map();
1113 map.nodes_with_entry_count as usize
1112 map.nodes_with_entry_count as usize
1114 }
1113 }
1115
1114
1116 pub fn contains_key(
1115 pub fn contains_key(
1117 &self,
1116 &self,
1118 key: &HgPath,
1117 key: &HgPath,
1119 ) -> Result<bool, DirstateV2ParseError> {
1118 ) -> Result<bool, DirstateV2ParseError> {
1120 Ok(self.get(key)?.is_some())
1119 Ok(self.get(key)?.is_some())
1121 }
1120 }
1122
1121
1123 pub fn get(
1122 pub fn get(
1124 &self,
1123 &self,
1125 key: &HgPath,
1124 key: &HgPath,
1126 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1125 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1127 let map = self.get_map();
1126 let map = self.get_map();
1128 Ok(if let Some(node) = map.get_node(key)? {
1127 Ok(if let Some(node) = map.get_node(key)? {
1129 node.entry()?
1128 node.entry()?
1130 } else {
1129 } else {
1131 None
1130 None
1132 })
1131 })
1133 }
1132 }
1134
1133
1135 pub fn iter(&self) -> StateMapIter<'_> {
1134 pub fn iter(&self) -> StateMapIter<'_> {
1136 let map = self.get_map();
1135 let map = self.get_map();
1137 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1136 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1138 Ok(if let Some(entry) = node.entry()? {
1137 Ok(if let Some(entry) = node.entry()? {
1139 Some((node.full_path(map.on_disk)?, entry))
1138 Some((node.full_path(map.on_disk)?, entry))
1140 } else {
1139 } else {
1141 None
1140 None
1142 })
1141 })
1143 }))
1142 }))
1144 }
1143 }
1145
1144
1146 pub fn iter_tracked_dirs(
1145 pub fn iter_tracked_dirs(
1147 &mut self,
1146 &mut self,
1148 ) -> Result<
1147 ) -> Result<
1149 Box<
1148 Box<
1150 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1149 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1151 + Send
1150 + Send
1152 + '_,
1151 + '_,
1153 >,
1152 >,
1154 DirstateError,
1153 DirstateError,
1155 > {
1154 > {
1156 let map = self.get_map();
1155 let map = self.get_map();
1157 let on_disk = map.on_disk;
1156 let on_disk = map.on_disk;
1158 Ok(Box::new(filter_map_results(
1157 Ok(Box::new(filter_map_results(
1159 map.iter_nodes(),
1158 map.iter_nodes(),
1160 move |node| {
1159 move |node| {
1161 Ok(if node.tracked_descendants_count() > 0 {
1160 Ok(if node.tracked_descendants_count() > 0 {
1162 Some(node.full_path(on_disk)?)
1161 Some(node.full_path(on_disk)?)
1163 } else {
1162 } else {
1164 None
1163 None
1165 })
1164 })
1166 },
1165 },
1167 )))
1166 )))
1168 }
1167 }
1169
1168
1170 pub fn debug_iter(
1169 pub fn debug_iter(
1171 &self,
1170 &self,
1172 all: bool,
1171 all: bool,
1173 ) -> Box<
1172 ) -> Box<
1174 dyn Iterator<
1173 dyn Iterator<
1175 Item = Result<
1174 Item = Result<
1176 (&HgPath, (u8, i32, i32, i32)),
1175 (&HgPath, (u8, i32, i32, i32)),
1177 DirstateV2ParseError,
1176 DirstateV2ParseError,
1178 >,
1177 >,
1179 > + Send
1178 > + Send
1180 + '_,
1179 + '_,
1181 > {
1180 > {
1182 let map = self.get_map();
1181 let map = self.get_map();
1183 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1182 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1184 let debug_tuple = if let Some(entry) = node.entry()? {
1183 let debug_tuple = if let Some(entry) = node.entry()? {
1185 entry.debug_tuple()
1184 entry.debug_tuple()
1186 } else if !all {
1185 } else if !all {
1187 return Ok(None);
1186 return Ok(None);
1188 } else if let Some(mtime) = node.cached_directory_mtime()? {
1187 } else if let Some(mtime) = node.cached_directory_mtime()? {
1189 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1188 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1190 } else {
1189 } else {
1191 (b' ', 0, -1, -1)
1190 (b' ', 0, -1, -1)
1192 };
1191 };
1193 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1192 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1194 }))
1193 }))
1195 }
1194 }
1196 }
1195 }
General Comments 0
You need to be logged in to leave comments. Login now