##// END OF EJS Templates
rust-dirstatemap: remove `removefile` API...
Raphaël Gomès -
r50002:8a17fc50 default
parent child Browse files
Show More
@@ -1,1494 +1,1458 b''
1 1 use bytes_cast::BytesCast;
2 2 use micro_timer::timed;
3 3 use std::borrow::Cow;
4 4 use std::path::PathBuf;
5 5
6 6 use super::on_disk;
7 7 use super::on_disk::DirstateV2ParseError;
8 8 use super::owning::OwningDirstateMap;
9 9 use super::path_with_basename::WithBasename;
10 10 use crate::dirstate::parsers::pack_entry;
11 11 use crate::dirstate::parsers::packed_entry_size;
12 12 use crate::dirstate::parsers::parse_dirstate_entries;
13 13 use crate::dirstate::CopyMapIter;
14 14 use crate::dirstate::DirstateV2Data;
15 15 use crate::dirstate::ParentFileData;
16 16 use crate::dirstate::StateMapIter;
17 17 use crate::dirstate::TruncatedTimestamp;
18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
19 use crate::dirstate::SIZE_NON_NORMAL;
20 18 use crate::matchers::Matcher;
21 19 use crate::utils::hg_path::{HgPath, HgPathBuf};
22 20 use crate::DirstateEntry;
23 21 use crate::DirstateError;
24 22 use crate::DirstateMapError;
25 23 use crate::DirstateParents;
26 24 use crate::DirstateStatus;
27 25 use crate::EntryState;
28 26 use crate::FastHashbrownMap as FastHashMap;
29 27 use crate::PatternFileWarning;
30 28 use crate::StatusError;
31 29 use crate::StatusOptions;
32 30
33 31 /// Append to an existing data file if the amount of unreachable data (not used
34 32 /// anymore) is less than this fraction of the total amount of existing data.
35 33 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
36 34
37 35 pub struct DirstateMap<'on_disk> {
38 36 /// Contents of the `.hg/dirstate` file
39 37 pub(super) on_disk: &'on_disk [u8],
40 38
41 39 pub(super) root: ChildNodes<'on_disk>,
42 40
43 41 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
44 42 pub(super) nodes_with_entry_count: u32,
45 43
46 44 /// Number of nodes anywhere in the tree that have
47 45 /// `.copy_source.is_some()`.
48 46 pub(super) nodes_with_copy_source_count: u32,
49 47
50 48 /// See on_disk::Header
51 49 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
52 50
53 51 /// How many bytes of `on_disk` are not used anymore
54 52 pub(super) unreachable_bytes: u32,
55 53 }
56 54
57 55 /// Using a plain `HgPathBuf` of the full path from the repository root as a
58 56 /// map key would also work: all paths in a given map have the same parent
59 57 /// path, so comparing full paths gives the same result as comparing base
60 58 /// names. However `HashMap` would waste time always re-hashing the same
61 59 /// string prefix.
62 60 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
63 61
64 62 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
65 63 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
66 64 pub(super) enum BorrowedPath<'tree, 'on_disk> {
67 65 InMemory(&'tree HgPathBuf),
68 66 OnDisk(&'on_disk HgPath),
69 67 }
70 68
71 69 pub(super) enum ChildNodes<'on_disk> {
72 70 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
73 71 OnDisk(&'on_disk [on_disk::Node]),
74 72 }
75 73
76 74 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
77 75 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
78 76 OnDisk(&'on_disk [on_disk::Node]),
79 77 }
80 78
81 79 pub(super) enum NodeRef<'tree, 'on_disk> {
82 80 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
83 81 OnDisk(&'on_disk on_disk::Node),
84 82 }
85 83
86 84 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
87 85 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
88 86 match *self {
89 87 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
90 88 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
91 89 }
92 90 }
93 91 }
94 92
95 93 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
96 94 type Target = HgPath;
97 95
98 96 fn deref(&self) -> &HgPath {
99 97 match *self {
100 98 BorrowedPath::InMemory(in_memory) => in_memory,
101 99 BorrowedPath::OnDisk(on_disk) => on_disk,
102 100 }
103 101 }
104 102 }
105 103
106 104 impl Default for ChildNodes<'_> {
107 105 fn default() -> Self {
108 106 ChildNodes::InMemory(Default::default())
109 107 }
110 108 }
111 109
112 110 impl<'on_disk> ChildNodes<'on_disk> {
113 111 pub(super) fn as_ref<'tree>(
114 112 &'tree self,
115 113 ) -> ChildNodesRef<'tree, 'on_disk> {
116 114 match self {
117 115 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
118 116 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
119 117 }
120 118 }
121 119
122 120 pub(super) fn is_empty(&self) -> bool {
123 121 match self {
124 122 ChildNodes::InMemory(nodes) => nodes.is_empty(),
125 123 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
126 124 }
127 125 }
128 126
129 127 fn make_mut(
130 128 &mut self,
131 129 on_disk: &'on_disk [u8],
132 130 unreachable_bytes: &mut u32,
133 131 ) -> Result<
134 132 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
135 133 DirstateV2ParseError,
136 134 > {
137 135 match self {
138 136 ChildNodes::InMemory(nodes) => Ok(nodes),
139 137 ChildNodes::OnDisk(nodes) => {
140 138 *unreachable_bytes +=
141 139 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
142 140 let nodes = nodes
143 141 .iter()
144 142 .map(|node| {
145 143 Ok((
146 144 node.path(on_disk)?,
147 145 node.to_in_memory_node(on_disk)?,
148 146 ))
149 147 })
150 148 .collect::<Result<_, _>>()?;
151 149 *self = ChildNodes::InMemory(nodes);
152 150 match self {
153 151 ChildNodes::InMemory(nodes) => Ok(nodes),
154 152 ChildNodes::OnDisk(_) => unreachable!(),
155 153 }
156 154 }
157 155 }
158 156 }
159 157 }
160 158
161 159 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
162 160 pub(super) fn get(
163 161 &self,
164 162 base_name: &HgPath,
165 163 on_disk: &'on_disk [u8],
166 164 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
167 165 match self {
168 166 ChildNodesRef::InMemory(nodes) => Ok(nodes
169 167 .get_key_value(base_name)
170 168 .map(|(k, v)| NodeRef::InMemory(k, v))),
171 169 ChildNodesRef::OnDisk(nodes) => {
172 170 let mut parse_result = Ok(());
173 171 let search_result = nodes.binary_search_by(|node| {
174 172 match node.base_name(on_disk) {
175 173 Ok(node_base_name) => node_base_name.cmp(base_name),
176 174 Err(e) => {
177 175 parse_result = Err(e);
178 176 // Dummy comparison result, `search_result` won’t
179 177 // be used since `parse_result` is an error
180 178 std::cmp::Ordering::Equal
181 179 }
182 180 }
183 181 });
184 182 parse_result.map(|()| {
185 183 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
186 184 })
187 185 }
188 186 }
189 187 }
190 188
191 189 /// Iterate in undefined order
192 190 pub(super) fn iter(
193 191 &self,
194 192 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
195 193 match self {
196 194 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
197 195 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
198 196 ),
199 197 ChildNodesRef::OnDisk(nodes) => {
200 198 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
201 199 }
202 200 }
203 201 }
204 202
205 203 /// Iterate in parallel in undefined order
206 204 pub(super) fn par_iter(
207 205 &self,
208 206 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
209 207 {
210 208 use rayon::prelude::*;
211 209 match self {
212 210 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
213 211 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
214 212 ),
215 213 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
216 214 nodes.par_iter().map(NodeRef::OnDisk),
217 215 ),
218 216 }
219 217 }
220 218
221 219 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
222 220 match self {
223 221 ChildNodesRef::InMemory(nodes) => {
224 222 let mut vec: Vec<_> = nodes
225 223 .iter()
226 224 .map(|(k, v)| NodeRef::InMemory(k, v))
227 225 .collect();
228 226 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
229 227 match node {
230 228 NodeRef::InMemory(path, _node) => path.base_name(),
231 229 NodeRef::OnDisk(_) => unreachable!(),
232 230 }
233 231 }
234 232 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
235 233 // value: https://github.com/rust-lang/rust/issues/34162
236 234 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
237 235 vec
238 236 }
239 237 ChildNodesRef::OnDisk(nodes) => {
240 238 // Nodes on disk are already sorted
241 239 nodes.iter().map(NodeRef::OnDisk).collect()
242 240 }
243 241 }
244 242 }
245 243 }
246 244
247 245 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
248 246 pub(super) fn full_path(
249 247 &self,
250 248 on_disk: &'on_disk [u8],
251 249 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
252 250 match self {
253 251 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
254 252 NodeRef::OnDisk(node) => node.full_path(on_disk),
255 253 }
256 254 }
257 255
258 256 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
259 257 /// HgPath>` detached from `'tree`
260 258 pub(super) fn full_path_borrowed(
261 259 &self,
262 260 on_disk: &'on_disk [u8],
263 261 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
264 262 match self {
265 263 NodeRef::InMemory(path, _node) => match path.full_path() {
266 264 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
267 265 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
268 266 },
269 267 NodeRef::OnDisk(node) => {
270 268 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
271 269 }
272 270 }
273 271 }
274 272
275 273 pub(super) fn base_name(
276 274 &self,
277 275 on_disk: &'on_disk [u8],
278 276 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
279 277 match self {
280 278 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
281 279 NodeRef::OnDisk(node) => node.base_name(on_disk),
282 280 }
283 281 }
284 282
285 283 pub(super) fn children(
286 284 &self,
287 285 on_disk: &'on_disk [u8],
288 286 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
289 287 match self {
290 288 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
291 289 NodeRef::OnDisk(node) => {
292 290 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
293 291 }
294 292 }
295 293 }
296 294
297 295 pub(super) fn has_copy_source(&self) -> bool {
298 296 match self {
299 297 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
300 298 NodeRef::OnDisk(node) => node.has_copy_source(),
301 299 }
302 300 }
303 301
304 302 pub(super) fn copy_source(
305 303 &self,
306 304 on_disk: &'on_disk [u8],
307 305 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
308 306 match self {
309 307 NodeRef::InMemory(_path, node) => {
310 308 Ok(node.copy_source.as_ref().map(|s| &**s))
311 309 }
312 310 NodeRef::OnDisk(node) => node.copy_source(on_disk),
313 311 }
314 312 }
315 313 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
316 314 /// HgPath>` detached from `'tree`
317 315 pub(super) fn copy_source_borrowed(
318 316 &self,
319 317 on_disk: &'on_disk [u8],
320 318 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
321 319 {
322 320 Ok(match self {
323 321 NodeRef::InMemory(_path, node) => {
324 322 node.copy_source.as_ref().map(|source| match source {
325 323 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
326 324 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
327 325 })
328 326 }
329 327 NodeRef::OnDisk(node) => node
330 328 .copy_source(on_disk)?
331 329 .map(|source| BorrowedPath::OnDisk(source)),
332 330 })
333 331 }
334 332
335 333 pub(super) fn entry(
336 334 &self,
337 335 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
338 336 match self {
339 337 NodeRef::InMemory(_path, node) => {
340 338 Ok(node.data.as_entry().copied())
341 339 }
342 340 NodeRef::OnDisk(node) => node.entry(),
343 341 }
344 342 }
345 343
346 344 pub(super) fn state(
347 345 &self,
348 346 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
349 347 Ok(self.entry()?.and_then(|e| {
350 348 if e.any_tracked() {
351 349 Some(e.state())
352 350 } else {
353 351 None
354 352 }
355 353 }))
356 354 }
357 355
358 356 pub(super) fn cached_directory_mtime(
359 357 &self,
360 358 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
361 359 match self {
362 360 NodeRef::InMemory(_path, node) => Ok(match node.data {
363 361 NodeData::CachedDirectory { mtime } => Some(mtime),
364 362 _ => None,
365 363 }),
366 364 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
367 365 }
368 366 }
369 367
370 368 pub(super) fn descendants_with_entry_count(&self) -> u32 {
371 369 match self {
372 370 NodeRef::InMemory(_path, node) => {
373 371 node.descendants_with_entry_count
374 372 }
375 373 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
376 374 }
377 375 }
378 376
379 377 pub(super) fn tracked_descendants_count(&self) -> u32 {
380 378 match self {
381 379 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
382 380 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
383 381 }
384 382 }
385 383 }
386 384
387 385 /// Represents a file or a directory
388 386 #[derive(Default)]
389 387 pub(super) struct Node<'on_disk> {
390 388 pub(super) data: NodeData,
391 389
392 390 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
393 391
394 392 pub(super) children: ChildNodes<'on_disk>,
395 393
396 394 /// How many (non-inclusive) descendants of this node have an entry.
397 395 pub(super) descendants_with_entry_count: u32,
398 396
399 397 /// How many (non-inclusive) descendants of this node have an entry whose
400 398 /// state is "tracked".
401 399 pub(super) tracked_descendants_count: u32,
402 400 }
403 401
404 402 pub(super) enum NodeData {
405 403 Entry(DirstateEntry),
406 404 CachedDirectory { mtime: TruncatedTimestamp },
407 405 None,
408 406 }
409 407
410 408 impl Default for NodeData {
411 409 fn default() -> Self {
412 410 NodeData::None
413 411 }
414 412 }
415 413
416 414 impl NodeData {
417 415 fn has_entry(&self) -> bool {
418 416 match self {
419 417 NodeData::Entry(_) => true,
420 418 _ => false,
421 419 }
422 420 }
423 421
424 422 fn as_entry(&self) -> Option<&DirstateEntry> {
425 423 match self {
426 424 NodeData::Entry(entry) => Some(entry),
427 425 _ => None,
428 426 }
429 427 }
430 428
431 429 fn as_entry_mut(&mut self) -> Option<&mut DirstateEntry> {
432 430 match self {
433 431 NodeData::Entry(entry) => Some(entry),
434 432 _ => None,
435 433 }
436 434 }
437 435 }
438 436
439 437 impl<'on_disk> DirstateMap<'on_disk> {
440 438 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
441 439 Self {
442 440 on_disk,
443 441 root: ChildNodes::default(),
444 442 nodes_with_entry_count: 0,
445 443 nodes_with_copy_source_count: 0,
446 444 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
447 445 unreachable_bytes: 0,
448 446 }
449 447 }
450 448
451 449 #[timed]
452 450 pub fn new_v2(
453 451 on_disk: &'on_disk [u8],
454 452 data_size: usize,
455 453 metadata: &[u8],
456 454 ) -> Result<Self, DirstateError> {
457 455 if let Some(data) = on_disk.get(..data_size) {
458 456 Ok(on_disk::read(data, metadata)?)
459 457 } else {
460 458 Err(DirstateV2ParseError.into())
461 459 }
462 460 }
463 461
464 462 #[timed]
465 463 pub fn new_v1(
466 464 on_disk: &'on_disk [u8],
467 465 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
468 466 let mut map = Self::empty(on_disk);
469 467 if map.on_disk.is_empty() {
470 468 return Ok((map, None));
471 469 }
472 470
473 471 let parents = parse_dirstate_entries(
474 472 map.on_disk,
475 473 |path, entry, copy_source| {
476 474 let tracked = entry.state().is_tracked();
477 475 let node = Self::get_or_insert_node(
478 476 map.on_disk,
479 477 &mut map.unreachable_bytes,
480 478 &mut map.root,
481 479 path,
482 480 WithBasename::to_cow_borrowed,
483 481 |ancestor| {
484 482 if tracked {
485 483 ancestor.tracked_descendants_count += 1
486 484 }
487 485 ancestor.descendants_with_entry_count += 1
488 486 },
489 487 )?;
490 488 assert!(
491 489 !node.data.has_entry(),
492 490 "duplicate dirstate entry in read"
493 491 );
494 492 assert!(
495 493 node.copy_source.is_none(),
496 494 "duplicate dirstate entry in read"
497 495 );
498 496 node.data = NodeData::Entry(*entry);
499 497 node.copy_source = copy_source.map(Cow::Borrowed);
500 498 map.nodes_with_entry_count += 1;
501 499 if copy_source.is_some() {
502 500 map.nodes_with_copy_source_count += 1
503 501 }
504 502 Ok(())
505 503 },
506 504 )?;
507 505 let parents = Some(parents.clone());
508 506
509 507 Ok((map, parents))
510 508 }
511 509
512 510 /// Assuming dirstate-v2 format, returns whether the next write should
513 511 /// append to the existing data file that contains `self.on_disk` (true),
514 512 /// or create a new data file from scratch (false).
515 513 pub(super) fn write_should_append(&self) -> bool {
516 514 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
517 515 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
518 516 }
519 517
520 518 fn get_node<'tree>(
521 519 &'tree self,
522 520 path: &HgPath,
523 521 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
524 522 let mut children = self.root.as_ref();
525 523 let mut components = path.components();
526 524 let mut component =
527 525 components.next().expect("expected at least one components");
528 526 loop {
529 527 if let Some(child) = children.get(component, self.on_disk)? {
530 528 if let Some(next_component) = components.next() {
531 529 component = next_component;
532 530 children = child.children(self.on_disk)?;
533 531 } else {
534 532 return Ok(Some(child));
535 533 }
536 534 } else {
537 535 return Ok(None);
538 536 }
539 537 }
540 538 }
541 539
542 540 /// Returns a mutable reference to the node at `path` if it exists
543 541 ///
544 542 /// This takes `root` instead of `&mut self` so that callers can mutate
545 543 /// other fields while the returned borrow is still valid
546 544 fn get_node_mut<'tree>(
547 545 on_disk: &'on_disk [u8],
548 546 unreachable_bytes: &mut u32,
549 547 root: &'tree mut ChildNodes<'on_disk>,
550 548 path: &HgPath,
551 549 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
552 550 let mut children = root;
553 551 let mut components = path.components();
554 552 let mut component =
555 553 components.next().expect("expected at least one components");
556 554 loop {
557 555 if let Some(child) = children
558 556 .make_mut(on_disk, unreachable_bytes)?
559 557 .get_mut(component)
560 558 {
561 559 if let Some(next_component) = components.next() {
562 560 component = next_component;
563 561 children = &mut child.children;
564 562 } else {
565 563 return Ok(Some(child));
566 564 }
567 565 } else {
568 566 return Ok(None);
569 567 }
570 568 }
571 569 }
572 570
573 571 pub(super) fn get_or_insert<'tree, 'path>(
574 572 &'tree mut self,
575 573 path: &HgPath,
576 574 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
577 575 Self::get_or_insert_node(
578 576 self.on_disk,
579 577 &mut self.unreachable_bytes,
580 578 &mut self.root,
581 579 path,
582 580 WithBasename::to_cow_owned,
583 581 |_| {},
584 582 )
585 583 }
586 584
587 585 fn get_or_insert_node<'tree, 'path>(
588 586 on_disk: &'on_disk [u8],
589 587 unreachable_bytes: &mut u32,
590 588 root: &'tree mut ChildNodes<'on_disk>,
591 589 path: &'path HgPath,
592 590 to_cow: impl Fn(
593 591 WithBasename<&'path HgPath>,
594 592 ) -> WithBasename<Cow<'on_disk, HgPath>>,
595 593 mut each_ancestor: impl FnMut(&mut Node),
596 594 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
597 595 let mut child_nodes = root;
598 596 let mut inclusive_ancestor_paths =
599 597 WithBasename::inclusive_ancestors_of(path);
600 598 let mut ancestor_path = inclusive_ancestor_paths
601 599 .next()
602 600 .expect("expected at least one inclusive ancestor");
603 601 loop {
604 602 let (_, child_node) = child_nodes
605 603 .make_mut(on_disk, unreachable_bytes)?
606 604 .raw_entry_mut()
607 605 .from_key(ancestor_path.base_name())
608 606 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
609 607 if let Some(next) = inclusive_ancestor_paths.next() {
610 608 each_ancestor(child_node);
611 609 ancestor_path = next;
612 610 child_nodes = &mut child_node.children;
613 611 } else {
614 612 return Ok(child_node);
615 613 }
616 614 }
617 615 }
618 616
619 617 fn reset_state(
620 618 &mut self,
621 619 filename: &HgPath,
622 620 old_entry_opt: Option<DirstateEntry>,
623 621 wc_tracked: bool,
624 622 p1_tracked: bool,
625 623 p2_info: bool,
626 624 has_meaningful_mtime: bool,
627 625 parent_file_data_opt: Option<ParentFileData>,
628 626 ) -> Result<(), DirstateError> {
629 627 let (had_entry, was_tracked) = match old_entry_opt {
630 628 Some(old_entry) => (true, old_entry.tracked()),
631 629 None => (false, false),
632 630 };
633 631 let node = Self::get_or_insert_node(
634 632 self.on_disk,
635 633 &mut self.unreachable_bytes,
636 634 &mut self.root,
637 635 filename,
638 636 WithBasename::to_cow_owned,
639 637 |ancestor| {
640 638 if !had_entry {
641 639 ancestor.descendants_with_entry_count += 1;
642 640 }
643 641 if was_tracked {
644 642 if !wc_tracked {
645 643 ancestor.tracked_descendants_count = ancestor
646 644 .tracked_descendants_count
647 645 .checked_sub(1)
648 646 .expect("tracked count to be >= 0");
649 647 }
650 648 } else {
651 649 if wc_tracked {
652 650 ancestor.tracked_descendants_count += 1;
653 651 }
654 652 }
655 653 },
656 654 )?;
657 655
658 656 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
659 657 DirstateV2Data {
660 658 wc_tracked,
661 659 p1_tracked,
662 660 p2_info,
663 661 mode_size: parent_file_data.mode_size,
664 662 mtime: if has_meaningful_mtime {
665 663 parent_file_data.mtime
666 664 } else {
667 665 None
668 666 },
669 667 ..Default::default()
670 668 }
671 669 } else {
672 670 DirstateV2Data {
673 671 wc_tracked,
674 672 p1_tracked,
675 673 p2_info,
676 674 ..Default::default()
677 675 }
678 676 };
679 677 if !had_entry {
680 678 self.nodes_with_entry_count += 1;
681 679 }
682 680 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
683 681 Ok(())
684 682 }
685 683
686 684 fn set_tracked(
687 685 &mut self,
688 686 filename: &HgPath,
689 687 old_entry_opt: Option<DirstateEntry>,
690 688 ) -> Result<bool, DirstateV2ParseError> {
691 689 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
692 690 let had_entry = old_entry_opt.is_some();
693 691 let tracked_count_increment = if was_tracked { 0 } else { 1 };
694 692 let mut new = false;
695 693
696 694 let node = Self::get_or_insert_node(
697 695 self.on_disk,
698 696 &mut self.unreachable_bytes,
699 697 &mut self.root,
700 698 filename,
701 699 WithBasename::to_cow_owned,
702 700 |ancestor| {
703 701 if !had_entry {
704 702 ancestor.descendants_with_entry_count += 1;
705 703 }
706 704
707 705 ancestor.tracked_descendants_count += tracked_count_increment;
708 706 },
709 707 )?;
710 708 let new_entry = if let Some(old_entry) = old_entry_opt {
711 709 let mut e = old_entry.clone();
712 710 if e.tracked() {
713 711 // XXX
714 712 // This is probably overkill for more case, but we need this to
715 713 // fully replace the `normallookup` call with `set_tracked`
716 714 // one. Consider smoothing this in the future.
717 715 e.set_possibly_dirty();
718 716 } else {
719 717 new = true;
720 718 e.set_tracked();
721 719 }
722 720 e
723 721 } else {
724 722 self.nodes_with_entry_count += 1;
725 723 new = true;
726 724 DirstateEntry::new_tracked()
727 725 };
728 726 node.data = NodeData::Entry(new_entry);
729 727 Ok(new)
730 728 }
731 729
732 730 fn add_or_remove_file(
733 731 &mut self,
734 732 path: &HgPath,
735 733 old_state: Option<EntryState>,
736 734 new_entry: DirstateEntry,
737 735 ) -> Result<(), DirstateV2ParseError> {
738 736 let had_entry = old_state.is_some();
739 737 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
740 738 let tracked_count_increment =
741 739 match (was_tracked, new_entry.state().is_tracked()) {
742 740 (false, true) => 1,
743 741 (true, false) => -1,
744 742 _ => 0,
745 743 };
746 744
747 745 let node = Self::get_or_insert_node(
748 746 self.on_disk,
749 747 &mut self.unreachable_bytes,
750 748 &mut self.root,
751 749 path,
752 750 WithBasename::to_cow_owned,
753 751 |ancestor| {
754 752 if !had_entry {
755 753 ancestor.descendants_with_entry_count += 1;
756 754 }
757 755
758 756 // We can’t use `+= increment` because the counter is unsigned,
759 757 // and we want debug builds to detect accidental underflow
760 758 // through zero
761 759 match tracked_count_increment {
762 760 1 => ancestor.tracked_descendants_count += 1,
763 761 -1 => ancestor.tracked_descendants_count -= 1,
764 762 _ => {}
765 763 }
766 764 },
767 765 )?;
768 766 if !had_entry {
769 767 self.nodes_with_entry_count += 1
770 768 }
771 769 node.data = NodeData::Entry(new_entry);
772 770 Ok(())
773 771 }
774 772
775 773 /// It is the responsibility of the caller to know that there was an entry
776 774 /// there before. Does not handle the removal of copy source
777 775 fn set_untracked(
778 776 &mut self,
779 777 filename: &HgPath,
780 778 old_entry: DirstateEntry,
781 779 ) -> Result<(), DirstateV2ParseError> {
782 780 let node = Self::get_or_insert_node(
783 781 self.on_disk,
784 782 &mut self.unreachable_bytes,
785 783 &mut self.root,
786 784 filename,
787 785 WithBasename::to_cow_owned,
788 786 |ancestor| {
789 787 ancestor.tracked_descendants_count = ancestor
790 788 .tracked_descendants_count
791 789 .checked_sub(1)
792 790 .expect("tracked_descendants_count should be >= 0");
793 791 },
794 792 )?;
795 793 let mut new_entry = old_entry.clone();
796 794 new_entry.set_untracked();
797 795 node.data = NodeData::Entry(new_entry);
798 796 Ok(())
799 797 }
800 798
801 799 fn set_clean(
802 800 &mut self,
803 801 filename: &HgPath,
804 802 old_entry: DirstateEntry,
805 803 mode: u32,
806 804 size: u32,
807 805 mtime: TruncatedTimestamp,
808 806 ) -> Result<(), DirstateError> {
809 807 let node = Self::get_or_insert_node(
810 808 self.on_disk,
811 809 &mut self.unreachable_bytes,
812 810 &mut self.root,
813 811 filename,
814 812 WithBasename::to_cow_owned,
815 813 |ancestor| {
816 814 if !old_entry.tracked() {
817 815 ancestor.tracked_descendants_count += 1;
818 816 }
819 817 },
820 818 )?;
821 819 let mut new_entry = old_entry.clone();
822 820 new_entry.set_clean(mode, size, mtime);
823 821 node.data = NodeData::Entry(new_entry);
824 822 Ok(())
825 823 }
826 824
827 825 fn set_possibly_dirty(
828 826 &mut self,
829 827 filename: &HgPath,
830 828 ) -> Result<(), DirstateError> {
831 829 let node = Self::get_or_insert_node(
832 830 self.on_disk,
833 831 &mut self.unreachable_bytes,
834 832 &mut self.root,
835 833 filename,
836 834 WithBasename::to_cow_owned,
837 835 |_ancestor| {},
838 836 )?;
839 837 let entry = node.data.as_entry_mut().expect("entry should exist");
840 838 entry.set_possibly_dirty();
841 839 node.data = NodeData::Entry(*entry);
842 840 Ok(())
843 841 }
844 842
845 843 fn iter_nodes<'tree>(
846 844 &'tree self,
847 845 ) -> impl Iterator<
848 846 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
849 847 > + 'tree {
850 848 // Depth first tree traversal.
851 849 //
852 850 // If we could afford internal iteration and recursion,
853 851 // this would look like:
854 852 //
855 853 // ```
856 854 // fn traverse_children(
857 855 // children: &ChildNodes,
858 856 // each: &mut impl FnMut(&Node),
859 857 // ) {
860 858 // for child in children.values() {
861 859 // traverse_children(&child.children, each);
862 860 // each(child);
863 861 // }
864 862 // }
865 863 // ```
866 864 //
867 865 // However we want an external iterator and therefore can’t use the
868 866 // call stack. Use an explicit stack instead:
869 867 let mut stack = Vec::new();
870 868 let mut iter = self.root.as_ref().iter();
871 869 std::iter::from_fn(move || {
872 870 while let Some(child_node) = iter.next() {
873 871 let children = match child_node.children(self.on_disk) {
874 872 Ok(children) => children,
875 873 Err(error) => return Some(Err(error)),
876 874 };
877 875 // Pseudo-recursion
878 876 let new_iter = children.iter();
879 877 let old_iter = std::mem::replace(&mut iter, new_iter);
880 878 stack.push((child_node, old_iter));
881 879 }
882 880 // Found the end of a `children.iter()` iterator.
883 881 if let Some((child_node, next_iter)) = stack.pop() {
884 882 // "Return" from pseudo-recursion by restoring state from the
885 883 // explicit stack
886 884 iter = next_iter;
887 885
888 886 Some(Ok(child_node))
889 887 } else {
890 888 // Reached the bottom of the stack, we’re done
891 889 None
892 890 }
893 891 })
894 892 }
895 893
896 894 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
897 895 if let Cow::Borrowed(path) = path {
898 896 *unreachable_bytes += path.len() as u32
899 897 }
900 898 }
901 899 }
902 900
903 901 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
904 902 ///
905 903 /// The callback is only called for incoming `Ok` values. Errors are passed
906 904 /// through as-is. In order to let it use the `?` operator the callback is
907 905 /// expected to return a `Result` of `Option`, instead of an `Option` of
908 906 /// `Result`.
909 907 fn filter_map_results<'a, I, F, A, B, E>(
910 908 iter: I,
911 909 f: F,
912 910 ) -> impl Iterator<Item = Result<B, E>> + 'a
913 911 where
914 912 I: Iterator<Item = Result<A, E>> + 'a,
915 913 F: Fn(A) -> Result<Option<B>, E> + 'a,
916 914 {
917 915 iter.filter_map(move |result| match result {
918 916 Ok(node) => f(node).transpose(),
919 917 Err(e) => Some(Err(e)),
920 918 })
921 919 }
922 920
923 921 impl OwningDirstateMap {
924 922 pub fn clear(&mut self) {
925 923 self.with_dmap_mut(|map| {
926 924 map.root = Default::default();
927 925 map.nodes_with_entry_count = 0;
928 926 map.nodes_with_copy_source_count = 0;
929 927 });
930 928 }
931 929
932 930 pub fn set_entry(
933 931 &mut self,
934 932 filename: &HgPath,
935 933 entry: DirstateEntry,
936 934 ) -> Result<(), DirstateV2ParseError> {
937 935 self.with_dmap_mut(|map| {
938 936 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
939 937 Ok(())
940 938 })
941 939 }
942 940
943 941 pub fn add_file(
944 942 &mut self,
945 943 filename: &HgPath,
946 944 entry: DirstateEntry,
947 945 ) -> Result<(), DirstateError> {
948 946 let old_state = self.get(filename)?.map(|e| e.state());
949 947 self.with_dmap_mut(|map| {
950 948 Ok(map.add_or_remove_file(filename, old_state, entry)?)
951 949 })
952 950 }
953 951
954 952 pub fn set_tracked(
955 953 &mut self,
956 954 filename: &HgPath,
957 955 ) -> Result<bool, DirstateV2ParseError> {
958 956 let old_entry_opt = self.get(filename)?;
959 957 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
960 958 }
961 959
962 960 pub fn set_untracked(
963 961 &mut self,
964 962 filename: &HgPath,
965 963 ) -> Result<bool, DirstateError> {
966 964 let old_entry_opt = self.get(filename)?;
967 965 match old_entry_opt {
968 966 None => Ok(false),
969 967 Some(old_entry) => {
970 968 if !old_entry.tracked() {
971 969 // `DirstateMap::set_untracked` is not a noop if
972 970 // already not tracked as it will decrement the
973 971 // tracked counters while going down.
974 972 return Ok(true);
975 973 }
976 974 if old_entry.added() {
977 975 // Untracking an "added" entry will just result in a
978 976 // worthless entry (and other parts of the code will
979 977 // complain about it), just drop it entirely.
980 978 self.drop_entry_and_copy_source(filename)?;
981 979 return Ok(true);
982 980 }
983 981 if !old_entry.p2_info() {
984 982 self.copy_map_remove(filename)?;
985 983 }
986 984
987 985 self.with_dmap_mut(|map| {
988 986 map.set_untracked(filename, old_entry)?;
989 987 Ok(true)
990 988 })
991 989 }
992 990 }
993 991 }
994 992
995 993 pub fn set_clean(
996 994 &mut self,
997 995 filename: &HgPath,
998 996 mode: u32,
999 997 size: u32,
1000 998 mtime: TruncatedTimestamp,
1001 999 ) -> Result<(), DirstateError> {
1002 1000 let old_entry = match self.get(filename)? {
1003 1001 None => {
1004 1002 return Err(
1005 1003 DirstateMapError::PathNotFound(filename.into()).into()
1006 1004 )
1007 1005 }
1008 1006 Some(e) => e,
1009 1007 };
1010 1008 self.copy_map_remove(filename)?;
1011 1009 self.with_dmap_mut(|map| {
1012 1010 map.set_clean(filename, old_entry, mode, size, mtime)
1013 1011 })
1014 1012 }
1015 1013
1016 1014 pub fn set_possibly_dirty(
1017 1015 &mut self,
1018 1016 filename: &HgPath,
1019 1017 ) -> Result<(), DirstateError> {
1020 1018 if self.get(filename)?.is_none() {
1021 1019 return Err(DirstateMapError::PathNotFound(filename.into()).into());
1022 1020 }
1023 1021 self.with_dmap_mut(|map| map.set_possibly_dirty(filename))
1024 1022 }
1025 1023
1026 1024 pub fn reset_state(
1027 1025 &mut self,
1028 1026 filename: &HgPath,
1029 1027 wc_tracked: bool,
1030 1028 p1_tracked: bool,
1031 1029 p2_info: bool,
1032 1030 has_meaningful_mtime: bool,
1033 1031 parent_file_data_opt: Option<ParentFileData>,
1034 1032 ) -> Result<(), DirstateError> {
1035 1033 if !(p1_tracked || p2_info || wc_tracked) {
1036 1034 self.drop_entry_and_copy_source(filename)?;
1037 1035 return Ok(());
1038 1036 }
1039 1037 self.copy_map_remove(filename)?;
1040 1038 let old_entry_opt = self.get(filename)?;
1041 1039 self.with_dmap_mut(|map| {
1042 1040 map.reset_state(
1043 1041 filename,
1044 1042 old_entry_opt,
1045 1043 wc_tracked,
1046 1044 p1_tracked,
1047 1045 p2_info,
1048 1046 has_meaningful_mtime,
1049 1047 parent_file_data_opt,
1050 1048 )
1051 1049 })
1052 1050 }
1053 1051
1054 pub fn remove_file(
1055 &mut self,
1056 filename: &HgPath,
1057 in_merge: bool,
1058 ) -> Result<(), DirstateError> {
1059 let old_entry_opt = self.get(filename)?;
1060 let old_state = old_entry_opt.map(|e| e.state());
1061 let mut size = 0;
1062 if in_merge {
1063 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
1064 // during a merge. So I (marmoute) am not sure we need the
1065 // conditionnal at all. Adding double checking this with assert
1066 // would be nice.
1067 if let Some(old_entry) = old_entry_opt {
1068 // backup the previous state
1069 if old_entry.state() == EntryState::Merged {
1070 size = SIZE_NON_NORMAL;
1071 } else if old_entry.state() == EntryState::Normal
1072 && old_entry.size() == SIZE_FROM_OTHER_PARENT
1073 {
1074 // other parent
1075 size = SIZE_FROM_OTHER_PARENT;
1076 }
1077 }
1078 }
1079 if size == 0 {
1080 self.copy_map_remove(filename)?;
1081 }
1082 self.with_dmap_mut(|map| {
1083 let entry = DirstateEntry::new_removed(size);
1084 Ok(map.add_or_remove_file(filename, old_state, entry)?)
1085 })
1086 }
1087
1088 1052 pub fn drop_entry_and_copy_source(
1089 1053 &mut self,
1090 1054 filename: &HgPath,
1091 1055 ) -> Result<(), DirstateError> {
1092 1056 let was_tracked = self
1093 1057 .get(filename)?
1094 1058 .map_or(false, |e| e.state().is_tracked());
1095 1059 struct Dropped {
1096 1060 was_tracked: bool,
1097 1061 had_entry: bool,
1098 1062 had_copy_source: bool,
1099 1063 }
1100 1064
1101 1065 /// If this returns `Ok(Some((dropped, removed)))`, then
1102 1066 ///
1103 1067 /// * `dropped` is about the leaf node that was at `filename`
1104 1068 /// * `removed` is whether this particular level of recursion just
1105 1069 /// removed a node in `nodes`.
1106 1070 fn recur<'on_disk>(
1107 1071 on_disk: &'on_disk [u8],
1108 1072 unreachable_bytes: &mut u32,
1109 1073 nodes: &mut ChildNodes<'on_disk>,
1110 1074 path: &HgPath,
1111 1075 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
1112 1076 let (first_path_component, rest_of_path) =
1113 1077 path.split_first_component();
1114 1078 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
1115 1079 let node = if let Some(node) = nodes.get_mut(first_path_component)
1116 1080 {
1117 1081 node
1118 1082 } else {
1119 1083 return Ok(None);
1120 1084 };
1121 1085 let dropped;
1122 1086 if let Some(rest) = rest_of_path {
1123 1087 if let Some((d, removed)) = recur(
1124 1088 on_disk,
1125 1089 unreachable_bytes,
1126 1090 &mut node.children,
1127 1091 rest,
1128 1092 )? {
1129 1093 dropped = d;
1130 1094 if dropped.had_entry {
1131 1095 node.descendants_with_entry_count = node
1132 1096 .descendants_with_entry_count
1133 1097 .checked_sub(1)
1134 1098 .expect(
1135 1099 "descendants_with_entry_count should be >= 0",
1136 1100 );
1137 1101 }
1138 1102 if dropped.was_tracked {
1139 1103 node.tracked_descendants_count = node
1140 1104 .tracked_descendants_count
1141 1105 .checked_sub(1)
1142 1106 .expect(
1143 1107 "tracked_descendants_count should be >= 0",
1144 1108 );
1145 1109 }
1146 1110
1147 1111 // Directory caches must be invalidated when removing a
1148 1112 // child node
1149 1113 if removed {
1150 1114 if let NodeData::CachedDirectory { .. } = &node.data {
1151 1115 node.data = NodeData::None
1152 1116 }
1153 1117 }
1154 1118 } else {
1155 1119 return Ok(None);
1156 1120 }
1157 1121 } else {
1158 1122 let entry = node.data.as_entry();
1159 1123 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1160 1124 let had_entry = entry.is_some();
1161 1125 if had_entry {
1162 1126 node.data = NodeData::None
1163 1127 }
1164 1128 let mut had_copy_source = false;
1165 1129 if let Some(source) = &node.copy_source {
1166 1130 DirstateMap::count_dropped_path(unreachable_bytes, source);
1167 1131 had_copy_source = true;
1168 1132 node.copy_source = None
1169 1133 }
1170 1134 dropped = Dropped {
1171 1135 was_tracked,
1172 1136 had_entry,
1173 1137 had_copy_source,
1174 1138 };
1175 1139 }
1176 1140 // After recursion, for both leaf (rest_of_path is None) nodes and
1177 1141 // parent nodes, remove a node if it just became empty.
1178 1142 let remove = !node.data.has_entry()
1179 1143 && node.copy_source.is_none()
1180 1144 && node.children.is_empty();
1181 1145 if remove {
1182 1146 let (key, _) =
1183 1147 nodes.remove_entry(first_path_component).unwrap();
1184 1148 DirstateMap::count_dropped_path(
1185 1149 unreachable_bytes,
1186 1150 key.full_path(),
1187 1151 )
1188 1152 }
1189 1153 Ok(Some((dropped, remove)))
1190 1154 }
1191 1155
1192 1156 self.with_dmap_mut(|map| {
1193 1157 if let Some((dropped, _removed)) = recur(
1194 1158 map.on_disk,
1195 1159 &mut map.unreachable_bytes,
1196 1160 &mut map.root,
1197 1161 filename,
1198 1162 )? {
1199 1163 if dropped.had_entry {
1200 1164 map.nodes_with_entry_count = map
1201 1165 .nodes_with_entry_count
1202 1166 .checked_sub(1)
1203 1167 .expect("nodes_with_entry_count should be >= 0");
1204 1168 }
1205 1169 if dropped.had_copy_source {
1206 1170 map.nodes_with_copy_source_count = map
1207 1171 .nodes_with_copy_source_count
1208 1172 .checked_sub(1)
1209 1173 .expect("nodes_with_copy_source_count should be >= 0");
1210 1174 }
1211 1175 } else {
1212 1176 debug_assert!(!was_tracked);
1213 1177 }
1214 1178 Ok(())
1215 1179 })
1216 1180 }
1217 1181
1218 1182 pub fn has_tracked_dir(
1219 1183 &mut self,
1220 1184 directory: &HgPath,
1221 1185 ) -> Result<bool, DirstateError> {
1222 1186 self.with_dmap_mut(|map| {
1223 1187 if let Some(node) = map.get_node(directory)? {
1224 1188 // A node without a `DirstateEntry` was created to hold child
1225 1189 // nodes, and is therefore a directory.
1226 1190 let state = node.state()?;
1227 1191 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1228 1192 } else {
1229 1193 Ok(false)
1230 1194 }
1231 1195 })
1232 1196 }
1233 1197
1234 1198 pub fn has_dir(
1235 1199 &mut self,
1236 1200 directory: &HgPath,
1237 1201 ) -> Result<bool, DirstateError> {
1238 1202 self.with_dmap_mut(|map| {
1239 1203 if let Some(node) = map.get_node(directory)? {
1240 1204 // A node without a `DirstateEntry` was created to hold child
1241 1205 // nodes, and is therefore a directory.
1242 1206 let state = node.state()?;
1243 1207 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1244 1208 } else {
1245 1209 Ok(false)
1246 1210 }
1247 1211 })
1248 1212 }
1249 1213
1250 1214 #[timed]
1251 1215 pub fn pack_v1(
1252 1216 &self,
1253 1217 parents: DirstateParents,
1254 1218 ) -> Result<Vec<u8>, DirstateError> {
1255 1219 let map = self.get_map();
1256 1220 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1257 1221 // reallocations
1258 1222 let mut size = parents.as_bytes().len();
1259 1223 for node in map.iter_nodes() {
1260 1224 let node = node?;
1261 1225 if node.entry()?.is_some() {
1262 1226 size += packed_entry_size(
1263 1227 node.full_path(map.on_disk)?,
1264 1228 node.copy_source(map.on_disk)?,
1265 1229 );
1266 1230 }
1267 1231 }
1268 1232
1269 1233 let mut packed = Vec::with_capacity(size);
1270 1234 packed.extend(parents.as_bytes());
1271 1235
1272 1236 for node in map.iter_nodes() {
1273 1237 let node = node?;
1274 1238 if let Some(entry) = node.entry()? {
1275 1239 pack_entry(
1276 1240 node.full_path(map.on_disk)?,
1277 1241 &entry,
1278 1242 node.copy_source(map.on_disk)?,
1279 1243 &mut packed,
1280 1244 );
1281 1245 }
1282 1246 }
1283 1247 Ok(packed)
1284 1248 }
1285 1249
1286 1250 /// Returns new data and metadata together with whether that data should be
1287 1251 /// appended to the existing data file whose content is at
1288 1252 /// `map.on_disk` (true), instead of written to a new data file
1289 1253 /// (false).
1290 1254 #[timed]
1291 1255 pub fn pack_v2(
1292 1256 &self,
1293 1257 can_append: bool,
1294 1258 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1295 1259 let map = self.get_map();
1296 1260 on_disk::write(map, can_append)
1297 1261 }
1298 1262
1299 1263 /// `callback` allows the caller to process and do something with the
1300 1264 /// results of the status. This is needed to do so efficiently (i.e.
1301 1265 /// without cloning the `DirstateStatus` object with its paths) because
1302 1266 /// we need to borrow from `Self`.
1303 1267 pub fn with_status<R>(
1304 1268 &mut self,
1305 1269 matcher: &(dyn Matcher + Sync),
1306 1270 root_dir: PathBuf,
1307 1271 ignore_files: Vec<PathBuf>,
1308 1272 options: StatusOptions,
1309 1273 callback: impl for<'r> FnOnce(
1310 1274 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1311 1275 ) -> R,
1312 1276 ) -> R {
1313 1277 self.with_dmap_mut(|map| {
1314 1278 callback(super::status::status(
1315 1279 map,
1316 1280 matcher,
1317 1281 root_dir,
1318 1282 ignore_files,
1319 1283 options,
1320 1284 ))
1321 1285 })
1322 1286 }
1323 1287
1324 1288 pub fn copy_map_len(&self) -> usize {
1325 1289 let map = self.get_map();
1326 1290 map.nodes_with_copy_source_count as usize
1327 1291 }
1328 1292
1329 1293 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1330 1294 let map = self.get_map();
1331 1295 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1332 1296 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1333 1297 Some((node.full_path(map.on_disk)?, source))
1334 1298 } else {
1335 1299 None
1336 1300 })
1337 1301 }))
1338 1302 }
1339 1303
1340 1304 pub fn copy_map_contains_key(
1341 1305 &self,
1342 1306 key: &HgPath,
1343 1307 ) -> Result<bool, DirstateV2ParseError> {
1344 1308 let map = self.get_map();
1345 1309 Ok(if let Some(node) = map.get_node(key)? {
1346 1310 node.has_copy_source()
1347 1311 } else {
1348 1312 false
1349 1313 })
1350 1314 }
1351 1315
1352 1316 pub fn copy_map_get(
1353 1317 &self,
1354 1318 key: &HgPath,
1355 1319 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1356 1320 let map = self.get_map();
1357 1321 if let Some(node) = map.get_node(key)? {
1358 1322 if let Some(source) = node.copy_source(map.on_disk)? {
1359 1323 return Ok(Some(source));
1360 1324 }
1361 1325 }
1362 1326 Ok(None)
1363 1327 }
1364 1328
1365 1329 pub fn copy_map_remove(
1366 1330 &mut self,
1367 1331 key: &HgPath,
1368 1332 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1369 1333 self.with_dmap_mut(|map| {
1370 1334 let count = &mut map.nodes_with_copy_source_count;
1371 1335 let unreachable_bytes = &mut map.unreachable_bytes;
1372 1336 Ok(DirstateMap::get_node_mut(
1373 1337 map.on_disk,
1374 1338 unreachable_bytes,
1375 1339 &mut map.root,
1376 1340 key,
1377 1341 )?
1378 1342 .and_then(|node| {
1379 1343 if let Some(source) = &node.copy_source {
1380 1344 *count -= 1;
1381 1345 DirstateMap::count_dropped_path(unreachable_bytes, source);
1382 1346 }
1383 1347 node.copy_source.take().map(Cow::into_owned)
1384 1348 }))
1385 1349 })
1386 1350 }
1387 1351
1388 1352 pub fn copy_map_insert(
1389 1353 &mut self,
1390 1354 key: HgPathBuf,
1391 1355 value: HgPathBuf,
1392 1356 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1393 1357 self.with_dmap_mut(|map| {
1394 1358 let node = DirstateMap::get_or_insert_node(
1395 1359 map.on_disk,
1396 1360 &mut map.unreachable_bytes,
1397 1361 &mut map.root,
1398 1362 &key,
1399 1363 WithBasename::to_cow_owned,
1400 1364 |_ancestor| {},
1401 1365 )?;
1402 1366 if node.copy_source.is_none() {
1403 1367 map.nodes_with_copy_source_count += 1
1404 1368 }
1405 1369 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1406 1370 })
1407 1371 }
1408 1372
1409 1373 pub fn len(&self) -> usize {
1410 1374 let map = self.get_map();
1411 1375 map.nodes_with_entry_count as usize
1412 1376 }
1413 1377
1414 1378 pub fn contains_key(
1415 1379 &self,
1416 1380 key: &HgPath,
1417 1381 ) -> Result<bool, DirstateV2ParseError> {
1418 1382 Ok(self.get(key)?.is_some())
1419 1383 }
1420 1384
1421 1385 pub fn get(
1422 1386 &self,
1423 1387 key: &HgPath,
1424 1388 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1425 1389 let map = self.get_map();
1426 1390 Ok(if let Some(node) = map.get_node(key)? {
1427 1391 node.entry()?
1428 1392 } else {
1429 1393 None
1430 1394 })
1431 1395 }
1432 1396
1433 1397 pub fn iter(&self) -> StateMapIter<'_> {
1434 1398 let map = self.get_map();
1435 1399 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1436 1400 Ok(if let Some(entry) = node.entry()? {
1437 1401 Some((node.full_path(map.on_disk)?, entry))
1438 1402 } else {
1439 1403 None
1440 1404 })
1441 1405 }))
1442 1406 }
1443 1407
1444 1408 pub fn iter_tracked_dirs(
1445 1409 &mut self,
1446 1410 ) -> Result<
1447 1411 Box<
1448 1412 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1449 1413 + Send
1450 1414 + '_,
1451 1415 >,
1452 1416 DirstateError,
1453 1417 > {
1454 1418 let map = self.get_map();
1455 1419 let on_disk = map.on_disk;
1456 1420 Ok(Box::new(filter_map_results(
1457 1421 map.iter_nodes(),
1458 1422 move |node| {
1459 1423 Ok(if node.tracked_descendants_count() > 0 {
1460 1424 Some(node.full_path(on_disk)?)
1461 1425 } else {
1462 1426 None
1463 1427 })
1464 1428 },
1465 1429 )))
1466 1430 }
1467 1431
1468 1432 pub fn debug_iter(
1469 1433 &self,
1470 1434 all: bool,
1471 1435 ) -> Box<
1472 1436 dyn Iterator<
1473 1437 Item = Result<
1474 1438 (&HgPath, (u8, i32, i32, i32)),
1475 1439 DirstateV2ParseError,
1476 1440 >,
1477 1441 > + Send
1478 1442 + '_,
1479 1443 > {
1480 1444 let map = self.get_map();
1481 1445 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1482 1446 let debug_tuple = if let Some(entry) = node.entry()? {
1483 1447 entry.debug_tuple()
1484 1448 } else if !all {
1485 1449 return Ok(None);
1486 1450 } else if let Some(mtime) = node.cached_directory_mtime()? {
1487 1451 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1488 1452 } else {
1489 1453 (b' ', 0, -1, -1)
1490 1454 };
1491 1455 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1492 1456 }))
1493 1457 }
1494 1458 }
@@ -1,592 +1,573 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 9 //! `hg-core` package.
10 10
11 11 use std::cell::{RefCell, RefMut};
12 12 use std::convert::TryInto;
13 13
14 14 use cpython::{
15 15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
16 16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
17 17 };
18 18 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
19 19
20 20 use crate::{
21 21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 22 dirstate::item::DirstateItem,
23 23 pybytes_deref::PyBytesDeref,
24 24 };
25 25 use hg::{
26 26 dirstate::StateMapIter,
27 27 dirstate_tree::on_disk::DirstateV2ParseError,
28 28 dirstate_tree::owning::OwningDirstateMap,
29 29 revlog::Node,
30 30 utils::files::normalize_case,
31 31 utils::hg_path::{HgPath, HgPathBuf},
32 32 DirstateEntry, DirstateError, DirstateParents, EntryState,
33 33 };
34 34
35 35 // TODO
36 36 // This object needs to share references to multiple members of its Rust
37 37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
38 38 // Right now `CopyMap` is done, but it needs to have an explicit reference
39 39 // to `RustDirstateMap` which itself needs to have an encapsulation for
40 40 // every method in `CopyMap` (copymapcopy, etc.).
41 41 // This is ugly and hard to maintain.
42 42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
43 43 // `py_class!` is already implemented and does not mention
44 44 // `RustDirstateMap`, rightfully so.
45 45 // All attributes also have to have a separate refcount data attribute for
46 46 // leaks, with all methods that go along for reference sharing.
47 47 py_class!(pub class DirstateMap |py| {
48 48 @shared data inner: OwningDirstateMap;
49 49
50 50 /// Returns a `(dirstate_map, parents)` tuple
51 51 @staticmethod
52 52 def new_v1(
53 53 on_disk: PyBytes,
54 54 ) -> PyResult<PyObject> {
55 55 let on_disk = PyBytesDeref::new(py, on_disk);
56 56 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
57 57 .map_err(|e| dirstate_error(py, e))?;
58 58 let map = Self::create_instance(py, map)?;
59 59 let p1 = PyBytes::new(py, parents.p1.as_bytes());
60 60 let p2 = PyBytes::new(py, parents.p2.as_bytes());
61 61 let parents = (p1, p2);
62 62 Ok((map, parents).to_py_object(py).into_object())
63 63 }
64 64
65 65 /// Returns a DirstateMap
66 66 @staticmethod
67 67 def new_v2(
68 68 on_disk: PyBytes,
69 69 data_size: usize,
70 70 tree_metadata: PyBytes,
71 71 ) -> PyResult<PyObject> {
72 72 let dirstate_error = |e: DirstateError| {
73 73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
74 74 };
75 75 let on_disk = PyBytesDeref::new(py, on_disk);
76 76 let map = OwningDirstateMap::new_v2(
77 77 on_disk, data_size, tree_metadata.data(py),
78 78 ).map_err(dirstate_error)?;
79 79 let map = Self::create_instance(py, map)?;
80 80 Ok(map.into_object())
81 81 }
82 82
83 83 def clear(&self) -> PyResult<PyObject> {
84 84 self.inner(py).borrow_mut().clear();
85 85 Ok(py.None())
86 86 }
87 87
88 88 def get(
89 89 &self,
90 90 key: PyObject,
91 91 default: Option<PyObject> = None
92 92 ) -> PyResult<Option<PyObject>> {
93 93 let key = key.extract::<PyBytes>(py)?;
94 94 match self
95 95 .inner(py)
96 96 .borrow()
97 97 .get(HgPath::new(key.data(py)))
98 98 .map_err(|e| v2_error(py, e))?
99 99 {
100 100 Some(entry) => {
101 101 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
102 102 },
103 103 None => Ok(default)
104 104 }
105 105 }
106 106
107 107 def set_dirstate_item(
108 108 &self,
109 109 path: PyObject,
110 110 item: DirstateItem
111 111 ) -> PyResult<PyObject> {
112 112 let f = path.extract::<PyBytes>(py)?;
113 113 let filename = HgPath::new(f.data(py));
114 114 self.inner(py)
115 115 .borrow_mut()
116 116 .set_entry(filename, item.get_entry(py))
117 117 .map_err(|e| v2_error(py, e))?;
118 118 Ok(py.None())
119 119 }
120 120
121 121 def addfile(
122 122 &self,
123 123 f: PyBytes,
124 124 item: DirstateItem,
125 125 ) -> PyResult<PyNone> {
126 126 let filename = HgPath::new(f.data(py));
127 127 let entry = item.get_entry(py);
128 128 self.inner(py)
129 129 .borrow_mut()
130 130 .add_file(filename, entry)
131 131 .map_err(|e |dirstate_error(py, e))?;
132 132 Ok(PyNone)
133 133 }
134 134
135 135 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
136 136 let bytes = f.extract::<PyBytes>(py)?;
137 137 let path = HgPath::new(bytes.data(py));
138 138 let res = self.inner(py).borrow_mut().set_tracked(path);
139 139 let was_tracked = res.or_else(|_| {
140 140 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
141 141 })?;
142 142 Ok(was_tracked.to_py_object(py))
143 143 }
144 144
145 145 def set_untracked(&self, f: PyObject) -> PyResult<PyBool> {
146 146 let bytes = f.extract::<PyBytes>(py)?;
147 147 let path = HgPath::new(bytes.data(py));
148 148 let res = self.inner(py).borrow_mut().set_untracked(path);
149 149 let was_tracked = res.or_else(|_| {
150 150 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
151 151 })?;
152 152 Ok(was_tracked.to_py_object(py))
153 153 }
154 154
155 155 def set_clean(
156 156 &self,
157 157 f: PyObject,
158 158 mode: u32,
159 159 size: u32,
160 160 mtime: (i64, u32, bool)
161 161 ) -> PyResult<PyNone> {
162 162 let (mtime_s, mtime_ns, second_ambiguous) = mtime;
163 163 let timestamp = TruncatedTimestamp::new_truncate(
164 164 mtime_s, mtime_ns, second_ambiguous
165 165 );
166 166 let bytes = f.extract::<PyBytes>(py)?;
167 167 let path = HgPath::new(bytes.data(py));
168 168 let res = self.inner(py).borrow_mut().set_clean(
169 169 path, mode, size, timestamp,
170 170 );
171 171 res.or_else(|_| {
172 172 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
173 173 })?;
174 174 Ok(PyNone)
175 175 }
176 176
177 177 def set_possibly_dirty(&self, f: PyObject) -> PyResult<PyNone> {
178 178 let bytes = f.extract::<PyBytes>(py)?;
179 179 let path = HgPath::new(bytes.data(py));
180 180 let res = self.inner(py).borrow_mut().set_possibly_dirty(path);
181 181 res.or_else(|_| {
182 182 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
183 183 })?;
184 184 Ok(PyNone)
185 185 }
186 186
187 187 def reset_state(
188 188 &self,
189 189 f: PyObject,
190 190 wc_tracked: bool,
191 191 p1_tracked: bool,
192 192 p2_info: bool,
193 193 has_meaningful_mtime: bool,
194 194 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
195 195 ) -> PyResult<PyNone> {
196 196 let mut has_meaningful_mtime = has_meaningful_mtime;
197 197 let parent_file_data = match parentfiledata {
198 198 None => {
199 199 has_meaningful_mtime = false;
200 200 None
201 201 },
202 202 Some(data) => {
203 203 let (mode, size, mtime_info) = data;
204 204 let mtime = if let Some(mtime_info) = mtime_info {
205 205 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
206 206 let timestamp = TruncatedTimestamp::new_truncate(
207 207 mtime_s, mtime_ns, second_ambiguous
208 208 );
209 209 Some(timestamp)
210 210 } else {
211 211 has_meaningful_mtime = false;
212 212 None
213 213 };
214 214 Some(ParentFileData {
215 215 mode_size: Some((mode, size)),
216 216 mtime,
217 217 })
218 218 }
219 219 };
220 220 let bytes = f.extract::<PyBytes>(py)?;
221 221 let path = HgPath::new(bytes.data(py));
222 222 let res = self.inner(py).borrow_mut().reset_state(
223 223 path,
224 224 wc_tracked,
225 225 p1_tracked,
226 226 p2_info,
227 227 has_meaningful_mtime,
228 228 parent_file_data,
229 229 );
230 230 res.or_else(|_| {
231 231 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
232 232 })?;
233 233 Ok(PyNone)
234 234 }
235 235
236 def removefile(
237 &self,
238 f: PyObject,
239 in_merge: PyObject
240 ) -> PyResult<PyObject> {
241 self.inner(py).borrow_mut()
242 .remove_file(
243 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
244 in_merge.extract::<PyBool>(py)?.is_true(),
245 )
246 .or_else(|_| {
247 Err(PyErr::new::<exc::OSError, _>(
248 py,
249 "Dirstate error".to_string(),
250 ))
251 })?;
252 Ok(py.None())
253 }
254
255 236 def drop_item_and_copy_source(
256 237 &self,
257 238 f: PyBytes,
258 239 ) -> PyResult<PyNone> {
259 240 self.inner(py)
260 241 .borrow_mut()
261 242 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
262 243 .map_err(|e |dirstate_error(py, e))?;
263 244 Ok(PyNone)
264 245 }
265 246
266 247 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
267 248 let d = d.extract::<PyBytes>(py)?;
268 249 Ok(self.inner(py).borrow_mut()
269 250 .has_tracked_dir(HgPath::new(d.data(py)))
270 251 .map_err(|e| {
271 252 PyErr::new::<exc::ValueError, _>(py, e.to_string())
272 253 })?
273 254 .to_py_object(py))
274 255 }
275 256
276 257 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
277 258 let d = d.extract::<PyBytes>(py)?;
278 259 Ok(self.inner(py).borrow_mut()
279 260 .has_dir(HgPath::new(d.data(py)))
280 261 .map_err(|e| {
281 262 PyErr::new::<exc::ValueError, _>(py, e.to_string())
282 263 })?
283 264 .to_py_object(py))
284 265 }
285 266
286 267 def write_v1(
287 268 &self,
288 269 p1: PyObject,
289 270 p2: PyObject,
290 271 ) -> PyResult<PyBytes> {
291 272 let inner = self.inner(py).borrow();
292 273 let parents = DirstateParents {
293 274 p1: extract_node_id(py, &p1)?,
294 275 p2: extract_node_id(py, &p2)?,
295 276 };
296 277 let result = inner.pack_v1(parents);
297 278 match result {
298 279 Ok(packed) => Ok(PyBytes::new(py, &packed)),
299 280 Err(_) => Err(PyErr::new::<exc::OSError, _>(
300 281 py,
301 282 "Dirstate error".to_string(),
302 283 )),
303 284 }
304 285 }
305 286
306 287 /// Returns new data together with whether that data should be appended to
307 288 /// the existing data file whose content is at `self.on_disk` (True),
308 289 /// instead of written to a new data file (False).
309 290 def write_v2(
310 291 &self,
311 292 can_append: bool,
312 293 ) -> PyResult<PyObject> {
313 294 let inner = self.inner(py).borrow();
314 295 let result = inner.pack_v2(can_append);
315 296 match result {
316 297 Ok((packed, tree_metadata, append)) => {
317 298 let packed = PyBytes::new(py, &packed);
318 299 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
319 300 let tuple = (packed, tree_metadata, append);
320 301 Ok(tuple.to_py_object(py).into_object())
321 302 },
322 303 Err(_) => Err(PyErr::new::<exc::OSError, _>(
323 304 py,
324 305 "Dirstate error".to_string(),
325 306 )),
326 307 }
327 308 }
328 309
329 310 def filefoldmapasdict(&self) -> PyResult<PyDict> {
330 311 let dict = PyDict::new(py);
331 312 for item in self.inner(py).borrow_mut().iter() {
332 313 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
333 314 if entry.state() != EntryState::Removed {
334 315 let key = normalize_case(path);
335 316 let value = path;
336 317 dict.set_item(
337 318 py,
338 319 PyBytes::new(py, key.as_bytes()).into_object(),
339 320 PyBytes::new(py, value.as_bytes()).into_object(),
340 321 )?;
341 322 }
342 323 }
343 324 Ok(dict)
344 325 }
345 326
346 327 def __len__(&self) -> PyResult<usize> {
347 328 Ok(self.inner(py).borrow().len())
348 329 }
349 330
350 331 def __contains__(&self, key: PyObject) -> PyResult<bool> {
351 332 let key = key.extract::<PyBytes>(py)?;
352 333 self.inner(py)
353 334 .borrow()
354 335 .contains_key(HgPath::new(key.data(py)))
355 336 .map_err(|e| v2_error(py, e))
356 337 }
357 338
358 339 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
359 340 let key = key.extract::<PyBytes>(py)?;
360 341 let key = HgPath::new(key.data(py));
361 342 match self
362 343 .inner(py)
363 344 .borrow()
364 345 .get(key)
365 346 .map_err(|e| v2_error(py, e))?
366 347 {
367 348 Some(entry) => {
368 349 Ok(DirstateItem::new_as_pyobject(py, entry)?)
369 350 },
370 351 None => Err(PyErr::new::<exc::KeyError, _>(
371 352 py,
372 353 String::from_utf8_lossy(key.as_bytes()),
373 354 )),
374 355 }
375 356 }
376 357
377 358 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
378 359 let leaked_ref = self.inner(py).leak_immutable();
379 360 DirstateMapKeysIterator::from_inner(
380 361 py,
381 362 unsafe { leaked_ref.map(py, |o| o.iter()) },
382 363 )
383 364 }
384 365
385 366 def items(&self) -> PyResult<DirstateMapItemsIterator> {
386 367 let leaked_ref = self.inner(py).leak_immutable();
387 368 DirstateMapItemsIterator::from_inner(
388 369 py,
389 370 unsafe { leaked_ref.map(py, |o| o.iter()) },
390 371 )
391 372 }
392 373
393 374 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
394 375 let leaked_ref = self.inner(py).leak_immutable();
395 376 DirstateMapKeysIterator::from_inner(
396 377 py,
397 378 unsafe { leaked_ref.map(py, |o| o.iter()) },
398 379 )
399 380 }
400 381
401 382 // TODO all copymap* methods, see docstring above
402 383 def copymapcopy(&self) -> PyResult<PyDict> {
403 384 let dict = PyDict::new(py);
404 385 for item in self.inner(py).borrow().copy_map_iter() {
405 386 let (key, value) = item.map_err(|e| v2_error(py, e))?;
406 387 dict.set_item(
407 388 py,
408 389 PyBytes::new(py, key.as_bytes()),
409 390 PyBytes::new(py, value.as_bytes()),
410 391 )?;
411 392 }
412 393 Ok(dict)
413 394 }
414 395
415 396 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
416 397 let key = key.extract::<PyBytes>(py)?;
417 398 match self
418 399 .inner(py)
419 400 .borrow()
420 401 .copy_map_get(HgPath::new(key.data(py)))
421 402 .map_err(|e| v2_error(py, e))?
422 403 {
423 404 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
424 405 None => Err(PyErr::new::<exc::KeyError, _>(
425 406 py,
426 407 String::from_utf8_lossy(key.data(py)),
427 408 )),
428 409 }
429 410 }
430 411 def copymap(&self) -> PyResult<CopyMap> {
431 412 CopyMap::from_inner(py, self.clone_ref(py))
432 413 }
433 414
434 415 def copymaplen(&self) -> PyResult<usize> {
435 416 Ok(self.inner(py).borrow().copy_map_len())
436 417 }
437 418 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
438 419 let key = key.extract::<PyBytes>(py)?;
439 420 self.inner(py)
440 421 .borrow()
441 422 .copy_map_contains_key(HgPath::new(key.data(py)))
442 423 .map_err(|e| v2_error(py, e))
443 424 }
444 425 def copymapget(
445 426 &self,
446 427 key: PyObject,
447 428 default: Option<PyObject>
448 429 ) -> PyResult<Option<PyObject>> {
449 430 let key = key.extract::<PyBytes>(py)?;
450 431 match self
451 432 .inner(py)
452 433 .borrow()
453 434 .copy_map_get(HgPath::new(key.data(py)))
454 435 .map_err(|e| v2_error(py, e))?
455 436 {
456 437 Some(copy) => Ok(Some(
457 438 PyBytes::new(py, copy.as_bytes()).into_object(),
458 439 )),
459 440 None => Ok(default),
460 441 }
461 442 }
462 443 def copymapsetitem(
463 444 &self,
464 445 key: PyObject,
465 446 value: PyObject
466 447 ) -> PyResult<PyObject> {
467 448 let key = key.extract::<PyBytes>(py)?;
468 449 let value = value.extract::<PyBytes>(py)?;
469 450 self.inner(py)
470 451 .borrow_mut()
471 452 .copy_map_insert(
472 453 HgPathBuf::from_bytes(key.data(py)),
473 454 HgPathBuf::from_bytes(value.data(py)),
474 455 )
475 456 .map_err(|e| v2_error(py, e))?;
476 457 Ok(py.None())
477 458 }
478 459 def copymappop(
479 460 &self,
480 461 key: PyObject,
481 462 default: Option<PyObject>
482 463 ) -> PyResult<Option<PyObject>> {
483 464 let key = key.extract::<PyBytes>(py)?;
484 465 match self
485 466 .inner(py)
486 467 .borrow_mut()
487 468 .copy_map_remove(HgPath::new(key.data(py)))
488 469 .map_err(|e| v2_error(py, e))?
489 470 {
490 471 Some(copy) => Ok(Some(
491 472 PyBytes::new(py, copy.as_bytes()).into_object(),
492 473 )),
493 474 None => Ok(default),
494 475 }
495 476 }
496 477
497 478 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
498 479 let leaked_ref = self.inner(py).leak_immutable();
499 480 CopyMapKeysIterator::from_inner(
500 481 py,
501 482 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
502 483 )
503 484 }
504 485
505 486 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
506 487 let leaked_ref = self.inner(py).leak_immutable();
507 488 CopyMapItemsIterator::from_inner(
508 489 py,
509 490 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
510 491 )
511 492 }
512 493
513 494 def tracked_dirs(&self) -> PyResult<PyList> {
514 495 let dirs = PyList::new(py, &[]);
515 496 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
516 497 .map_err(|e |dirstate_error(py, e))?
517 498 {
518 499 let path = path.map_err(|e| v2_error(py, e))?;
519 500 let path = PyBytes::new(py, path.as_bytes());
520 501 dirs.append(py, path.into_object())
521 502 }
522 503 Ok(dirs)
523 504 }
524 505
525 506 def debug_iter(&self, all: bool) -> PyResult<PyList> {
526 507 let dirs = PyList::new(py, &[]);
527 508 for item in self.inner(py).borrow().debug_iter(all) {
528 509 let (path, (state, mode, size, mtime)) =
529 510 item.map_err(|e| v2_error(py, e))?;
530 511 let path = PyBytes::new(py, path.as_bytes());
531 512 let item = (path, state, mode, size, mtime);
532 513 dirs.append(py, item.to_py_object(py).into_object())
533 514 }
534 515 Ok(dirs)
535 516 }
536 517 });
537 518
538 519 impl DirstateMap {
539 520 pub fn get_inner_mut<'a>(
540 521 &'a self,
541 522 py: Python<'a>,
542 523 ) -> RefMut<'a, OwningDirstateMap> {
543 524 self.inner(py).borrow_mut()
544 525 }
545 526 fn translate_key(
546 527 py: Python,
547 528 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
548 529 ) -> PyResult<Option<PyBytes>> {
549 530 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
550 531 Ok(Some(PyBytes::new(py, f.as_bytes())))
551 532 }
552 533 fn translate_key_value(
553 534 py: Python,
554 535 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
555 536 ) -> PyResult<Option<(PyBytes, PyObject)>> {
556 537 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
557 538 Ok(Some((
558 539 PyBytes::new(py, f.as_bytes()),
559 540 DirstateItem::new_as_pyobject(py, entry)?,
560 541 )))
561 542 }
562 543 }
563 544
564 545 py_shared_iterator!(
565 546 DirstateMapKeysIterator,
566 547 UnsafePyLeaked<StateMapIter<'static>>,
567 548 DirstateMap::translate_key,
568 549 Option<PyBytes>
569 550 );
570 551
571 552 py_shared_iterator!(
572 553 DirstateMapItemsIterator,
573 554 UnsafePyLeaked<StateMapIter<'static>>,
574 555 DirstateMap::translate_key_value,
575 556 Option<(PyBytes, PyObject)>
576 557 );
577 558
578 559 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
579 560 let bytes = obj.extract::<PyBytes>(py)?;
580 561 match bytes.data(py).try_into() {
581 562 Ok(s) => Ok(s),
582 563 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
583 564 }
584 565 }
585 566
586 567 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
587 568 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
588 569 }
589 570
590 571 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
591 572 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
592 573 }
General Comments 0
You need to be logged in to leave comments. Login now