##// END OF EJS Templates
dirstate-tree: Maintain a counter of DirstateEntry’s and copy sources...
Simon Sapin -
r47873:214ae40e default
parent child Browse files
Show More
@@ -1,448 +1,495 b''
1 1 use bytes_cast::BytesCast;
2 2 use std::path::PathBuf;
3 3 use std::{collections::BTreeMap, convert::TryInto};
4 4
5 5 use super::path_with_basename::WithBasename;
6 6 use crate::dirstate::parsers::clear_ambiguous_mtime;
7 7 use crate::dirstate::parsers::pack_entry;
8 8 use crate::dirstate::parsers::packed_entry_size;
9 9 use crate::dirstate::parsers::parse_dirstate_entries;
10 10 use crate::dirstate::parsers::parse_dirstate_parents;
11 11 use crate::dirstate::parsers::Timestamp;
12 12 use crate::matchers::Matcher;
13 13 use crate::revlog::node::NULL_NODE;
14 14 use crate::utils::hg_path::{HgPath, HgPathBuf};
15 15 use crate::CopyMapIter;
16 16 use crate::DirstateEntry;
17 17 use crate::DirstateError;
18 18 use crate::DirstateMapError;
19 19 use crate::DirstateParents;
20 20 use crate::DirstateStatus;
21 21 use crate::EntryState;
22 22 use crate::FastHashMap;
23 23 use crate::HgPathCow;
24 24 use crate::PatternFileWarning;
25 25 use crate::StateMapIter;
26 26 use crate::StatusError;
27 27 use crate::StatusOptions;
28 28
29 29 pub struct DirstateMap {
30 30 parents: Option<DirstateParents>,
31 31 dirty_parents: bool,
32 32 root: ChildNodes,
33
34 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
35 nodes_with_entry_count: usize,
36
37 /// Number of nodes anywhere in the tree that have
38 /// `.copy_source.is_some()`.
39 nodes_with_copy_source_count: usize,
33 40 }
34 41
35 42 /// Using a plain `HgPathBuf` of the full path from the repository root as a
36 43 /// map key would also work: all paths in a given map have the same parent
37 44 /// path, so comparing full paths gives the same result as comparing base
38 45 /// names. However `BTreeMap` would waste time always re-comparing the same
39 46 /// string prefix.
40 47 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
41 48
42 49 #[derive(Default)]
43 50 struct Node {
44 51 entry: Option<DirstateEntry>,
45 52 copy_source: Option<HgPathBuf>,
46 53 children: ChildNodes,
47 54 }
48 55
49 56 /// `(full_path, entry, copy_source)`
50 57 type NodeDataMut<'a> = (
51 58 &'a WithBasename<HgPathBuf>,
52 59 &'a mut Option<DirstateEntry>,
53 60 &'a mut Option<HgPathBuf>,
54 61 );
55 62
56 63 impl DirstateMap {
57 64 pub fn new() -> Self {
58 65 Self {
59 66 parents: None,
60 67 dirty_parents: false,
61 68 root: ChildNodes::new(),
69 nodes_with_entry_count: 0,
70 nodes_with_copy_source_count: 0,
62 71 }
63 72 }
64 73
65 74 fn get_node(&self, path: &HgPath) -> Option<&Node> {
66 75 let mut children = &self.root;
67 76 let mut components = path.components();
68 77 let mut component =
69 78 components.next().expect("expected at least one components");
70 79 loop {
71 80 let child = children.get(component)?;
72 81 if let Some(next_component) = components.next() {
73 82 component = next_component;
74 83 children = &child.children;
75 84 } else {
76 85 return Some(child);
77 86 }
78 87 }
79 88 }
80 89
81 fn get_or_insert_node(&mut self, path: &HgPath) -> &mut Node {
82 let mut child_nodes = &mut self.root;
90 /// This takes `root` instead of `&mut self` so that callers can mutate
91 /// other fields while the returned borrow is still valid
92 fn get_or_insert_node<'tree>(
93 root: &'tree mut ChildNodes,
94 path: &HgPath,
95 ) -> &'tree mut Node {
96 let mut child_nodes = root;
83 97 let mut inclusive_ancestor_paths =
84 98 WithBasename::inclusive_ancestors_of(path);
85 99 let mut ancestor_path = inclusive_ancestor_paths
86 100 .next()
87 101 .expect("expected at least one inclusive ancestor");
88 102 loop {
89 103 // TODO: can we avoid double lookup in all cases without allocating
90 104 // an owned key in cases where the map already contains that key?
91 105 let child_node =
92 106 if child_nodes.contains_key(ancestor_path.base_name()) {
93 107 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
94 108 } else {
95 109 // This is always a vacant entry, using `.entry()` lets us
96 110 // return a `&mut Node` of the newly-inserted node without
97 111 // yet another lookup. `BTreeMap::insert` doesn’t do this.
98 112 child_nodes.entry(ancestor_path.to_owned()).or_default()
99 113 };
100 114 if let Some(next) = inclusive_ancestor_paths.next() {
101 115 ancestor_path = next;
102 116 child_nodes = &mut child_node.children;
103 117 } else {
104 118 return child_node;
105 119 }
106 120 }
107 121 }
108 122
123 /// The meaning of `new_copy_source` is:
124 ///
125 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
126 /// * `Some(None)`: set `Node::copy_source` to `None`
127 /// * `None`: leave `Node::copy_source` unchanged
128 fn add_file_node(
129 &mut self,
130 path: &HgPath,
131 new_entry: DirstateEntry,
132 new_copy_source: Option<Option<HgPathBuf>>,
133 ) {
134 let node = Self::get_or_insert_node(&mut self.root, path);
135 if node.entry.is_none() {
136 self.nodes_with_entry_count += 1
137 }
138 if let Some(source) = &new_copy_source {
139 if node.copy_source.is_none() && source.is_some() {
140 self.nodes_with_copy_source_count += 1
141 }
142 if node.copy_source.is_some() && source.is_none() {
143 self.nodes_with_copy_source_count -= 1
144 }
145 }
146 node.entry = Some(new_entry);
147 if let Some(source) = new_copy_source {
148 node.copy_source = source
149 }
150 }
151
109 152 fn iter_nodes<'a>(
110 153 &'a self,
111 154 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
112 155 {
113 156 // Depth first tree traversal.
114 157 //
115 158 // If we could afford internal iteration and recursion,
116 159 // this would look like:
117 160 //
118 161 // ```
119 162 // fn traverse_children(
120 163 // children: &ChildNodes,
121 164 // each: &mut impl FnMut(&Node),
122 165 // ) {
123 166 // for child in children.values() {
124 167 // traverse_children(&child.children, each);
125 168 // each(child);
126 169 // }
127 170 // }
128 171 // ```
129 172 //
130 173 // However we want an external iterator and therefore can’t use the
131 174 // call stack. Use an explicit stack instead:
132 175 let mut stack = Vec::new();
133 176 let mut iter = self.root.iter();
134 177 std::iter::from_fn(move || {
135 178 while let Some((key, child_node)) = iter.next() {
136 179 // Pseudo-recursion
137 180 let new_iter = child_node.children.iter();
138 181 let old_iter = std::mem::replace(&mut iter, new_iter);
139 182 stack.push((key, child_node, old_iter));
140 183 }
141 184 // Found the end of a `children.iter()` iterator.
142 185 if let Some((key, child_node, next_iter)) = stack.pop() {
143 186 // "Return" from pseudo-recursion by restoring state from the
144 187 // explicit stack
145 188 iter = next_iter;
146 189
147 190 Some((key, child_node))
148 191 } else {
149 192 // Reached the bottom of the stack, we’re done
150 193 None
151 194 }
152 195 })
153 196 }
154 197
155 198 /// Mutable iterator for the `(entry, copy source)` of each node.
156 199 ///
157 200 /// It would not be safe to yield mutable references to nodes themeselves
158 201 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
159 202 /// reachable from their ancestor nodes, potentially creating multiple
160 203 /// `&mut` references to a given node.
161 204 fn iter_node_data_mut<'a>(
162 205 &'a mut self,
163 206 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
164 207 // Explict stack for pseudo-recursion, see `iter_nodes` above.
165 208 let mut stack = Vec::new();
166 209 let mut iter = self.root.iter_mut();
167 210 std::iter::from_fn(move || {
168 211 while let Some((key, child_node)) = iter.next() {
169 212 // Pseudo-recursion
170 213 let data =
171 214 (key, &mut child_node.entry, &mut child_node.copy_source);
172 215 let new_iter = child_node.children.iter_mut();
173 216 let old_iter = std::mem::replace(&mut iter, new_iter);
174 217 stack.push((data, old_iter));
175 218 }
176 219 // Found the end of a `children.values_mut()` iterator.
177 220 if let Some((data, next_iter)) = stack.pop() {
178 221 // "Return" from pseudo-recursion by restoring state from the
179 222 // explicit stack
180 223 iter = next_iter;
181 224
182 225 Some(data)
183 226 } else {
184 227 // Reached the bottom of the stack, we’re done
185 228 None
186 229 }
187 230 })
188 231 }
189 232 }
190 233
191 234 impl super::dispatch::DirstateMapMethods for DirstateMap {
192 235 fn clear(&mut self) {
193 236 self.set_parents(&DirstateParents {
194 237 p1: NULL_NODE,
195 238 p2: NULL_NODE,
196 239 });
197 self.root.clear()
240 self.root.clear();
241 self.nodes_with_entry_count = 0;
242 self.nodes_with_copy_source_count = 0;
198 243 }
199 244
200 245 fn add_file(
201 246 &mut self,
202 247 _filename: &HgPath,
203 248 _old_state: EntryState,
204 249 _entry: DirstateEntry,
205 250 ) -> Result<(), DirstateMapError> {
206 251 todo!()
207 252 }
208 253
209 254 fn remove_file(
210 255 &mut self,
211 256 _filename: &HgPath,
212 257 _old_state: EntryState,
213 258 _size: i32,
214 259 ) -> Result<(), DirstateMapError> {
215 260 todo!()
216 261 }
217 262
218 263 fn drop_file(
219 264 &mut self,
220 265 _filename: &HgPath,
221 266 _old_state: EntryState,
222 267 ) -> Result<bool, DirstateMapError> {
223 268 todo!()
224 269 }
225 270
226 271 fn clear_ambiguous_times(
227 272 &mut self,
228 273 _filenames: Vec<HgPathBuf>,
229 274 _now: i32,
230 275 ) {
231 276 todo!()
232 277 }
233 278
234 279 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
235 280 todo!()
236 281 }
237 282
238 283 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
239 284 todo!()
240 285 }
241 286
242 287 fn non_normal_or_other_parent_paths(
243 288 &mut self,
244 289 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
245 290 todo!()
246 291 }
247 292
248 293 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
249 294 todo!()
250 295 }
251 296
252 297 fn iter_non_normal_paths(
253 298 &mut self,
254 299 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
255 300 todo!()
256 301 }
257 302
258 303 fn iter_non_normal_paths_panic(
259 304 &self,
260 305 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
261 306 todo!()
262 307 }
263 308
264 309 fn iter_other_parent_paths(
265 310 &mut self,
266 311 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
267 312 todo!()
268 313 }
269 314
270 315 fn has_tracked_dir(
271 316 &mut self,
272 317 _directory: &HgPath,
273 318 ) -> Result<bool, DirstateMapError> {
274 319 todo!()
275 320 }
276 321
277 322 fn has_dir(
278 323 &mut self,
279 324 _directory: &HgPath,
280 325 ) -> Result<bool, DirstateMapError> {
281 326 todo!()
282 327 }
283 328
284 329 fn parents(
285 330 &mut self,
286 331 file_contents: &[u8],
287 332 ) -> Result<&DirstateParents, DirstateError> {
288 333 if self.parents.is_none() {
289 334 let parents = if !file_contents.is_empty() {
290 335 parse_dirstate_parents(file_contents)?.clone()
291 336 } else {
292 337 DirstateParents {
293 338 p1: NULL_NODE,
294 339 p2: NULL_NODE,
295 340 }
296 341 };
297 342 self.parents = Some(parents);
298 343 }
299 344 Ok(self.parents.as_ref().unwrap())
300 345 }
301 346
302 347 fn set_parents(&mut self, parents: &DirstateParents) {
303 348 self.parents = Some(parents.clone());
304 349 self.dirty_parents = true;
305 350 }
306 351
307 352 fn read<'a>(
308 353 &mut self,
309 354 file_contents: &'a [u8],
310 355 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
311 356 if file_contents.is_empty() {
312 357 return Ok(None);
313 358 }
314 359
315 360 let parents = parse_dirstate_entries(
316 361 file_contents,
317 362 |path, entry, copy_source| {
318 let node = self.get_or_insert_node(path);
319 node.entry = Some(*entry);
320 node.copy_source = copy_source.map(HgPath::to_owned);
363 self.add_file_node(
364 path,
365 *entry,
366 Some(copy_source.map(HgPath::to_owned)),
367 )
321 368 },
322 369 )?;
323 370
324 371 if !self.dirty_parents {
325 372 self.set_parents(parents);
326 373 }
327 374
328 375 Ok(Some(parents))
329 376 }
330 377
331 378 fn pack(
332 379 &mut self,
333 380 parents: DirstateParents,
334 381 now: Timestamp,
335 382 ) -> Result<Vec<u8>, DirstateError> {
336 383 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
337 384 // reallocations
338 385 let mut size = parents.as_bytes().len();
339 386 for (path, node) in self.iter_nodes() {
340 387 if node.entry.is_some() {
341 388 size += packed_entry_size(
342 389 path.full_path(),
343 390 node.copy_source.as_ref(),
344 391 )
345 392 }
346 393 }
347 394
348 395 let mut packed = Vec::with_capacity(size);
349 396 packed.extend(parents.as_bytes());
350 397
351 398 let now: i32 = now.0.try_into().expect("time overflow");
352 399 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
353 400 if let Some(entry) = opt_entry {
354 401 clear_ambiguous_mtime(entry, now);
355 402 pack_entry(
356 403 path.full_path(),
357 404 entry,
358 405 copy_source.as_ref(),
359 406 &mut packed,
360 407 );
361 408 }
362 409 }
363 410 self.dirty_parents = false;
364 411 Ok(packed)
365 412 }
366 413
367 414 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
368 415 todo!()
369 416 }
370 417
371 418 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
372 419 todo!()
373 420 }
374 421
375 422 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
376 423 todo!()
377 424 }
378 425
379 426 fn status<'a>(
380 427 &'a self,
381 428 _matcher: &'a (dyn Matcher + Sync),
382 429 _root_dir: PathBuf,
383 430 _ignore_files: Vec<PathBuf>,
384 431 _options: StatusOptions,
385 432 ) -> Result<
386 433 (
387 434 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
388 435 Vec<PatternFileWarning>,
389 436 ),
390 437 StatusError,
391 438 > {
392 439 todo!()
393 440 }
394 441
395 442 fn copy_map_len(&self) -> usize {
396 todo!()
443 self.nodes_with_copy_source_count
397 444 }
398 445
399 446 fn copy_map_iter(&self) -> CopyMapIter<'_> {
400 447 Box::new(self.iter_nodes().filter_map(|(path, node)| {
401 448 node.copy_source
402 449 .as_ref()
403 450 .map(|copy_source| (path.full_path(), copy_source))
404 451 }))
405 452 }
406 453
407 454 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
408 455 if let Some(node) = self.get_node(key) {
409 456 node.copy_source.is_some()
410 457 } else {
411 458 false
412 459 }
413 460 }
414 461
415 462 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
416 463 self.get_node(key)?.copy_source.as_ref()
417 464 }
418 465
419 466 fn copy_map_remove(&mut self, _key: &HgPath) -> Option<HgPathBuf> {
420 467 todo!()
421 468 }
422 469
423 470 fn copy_map_insert(
424 471 &mut self,
425 472 _key: HgPathBuf,
426 473 _value: HgPathBuf,
427 474 ) -> Option<HgPathBuf> {
428 475 todo!()
429 476 }
430 477
431 478 fn len(&self) -> usize {
432 todo!()
479 self.nodes_with_entry_count
433 480 }
434 481
435 482 fn contains_key(&self, key: &HgPath) -> bool {
436 483 self.get(key).is_some()
437 484 }
438 485
439 486 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
440 487 self.get_node(key)?.entry.as_ref()
441 488 }
442 489
443 490 fn iter(&self) -> StateMapIter<'_> {
444 491 Box::new(self.iter_nodes().filter_map(|(path, node)| {
445 492 node.entry.as_ref().map(|entry| (path.full_path(), entry))
446 493 }))
447 494 }
448 495 }
General Comments 0
You need to be logged in to leave comments. Login now