##// END OF EJS Templates
rust-dirstatemap: add #[timed] to dirstatemap read for comparison...
Raphaël Gomès -
r46135:80bf7b1a default
parent child Browse files
Show More
@@ -1,503 +1,467
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 use crate::revlog::node::NULL_NODE_ID;
9 9 use crate::{
10 10 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
11 11 pack_dirstate, parse_dirstate,
12 12 utils::{
13 13 files::normalize_case,
14 14 hg_path::{HgPath, HgPathBuf},
15 15 },
16 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
17 DirstateParents, DirstateParseError, FastHashMap, StateMap,
16 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError, DirstateParents,
17 DirstateParseError, FastHashMap, StateMap,
18 18 };
19 19 use core::borrow::Borrow;
20 use micro_timer::timed;
20 21 use std::collections::HashSet;
21 22 use std::convert::TryInto;
22 23 use std::iter::FromIterator;
23 24 use std::ops::Deref;
24 25 use std::time::Duration;
25 26
26 27 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
27 28
28 29 const MTIME_UNSET: i32 = -1;
29 30
30 31 #[derive(Default)]
31 32 pub struct DirstateMap {
32 33 state_map: StateMap,
33 34 pub copy_map: CopyMap,
34 35 file_fold_map: Option<FileFoldMap>,
35 36 pub dirs: Option<DirsMultiset>,
36 37 pub all_dirs: Option<DirsMultiset>,
37 38 non_normal_set: Option<HashSet<HgPathBuf>>,
38 39 other_parent_set: Option<HashSet<HgPathBuf>>,
39 40 parents: Option<DirstateParents>,
40 41 dirty_parents: bool,
41 42 }
42 43
43 44 /// Should only really be used in python interface code, for clarity
44 45 impl Deref for DirstateMap {
45 46 type Target = StateMap;
46 47
47 48 fn deref(&self) -> &Self::Target {
48 49 &self.state_map
49 50 }
50 51 }
51 52
52 53 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 iter: I,
55 ) -> Self {
54 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(iter: I) -> Self {
56 55 Self {
57 56 state_map: iter.into_iter().collect(),
58 57 ..Self::default()
59 58 }
60 59 }
61 60 }
62 61
63 62 impl DirstateMap {
64 63 pub fn new() -> Self {
65 64 Self::default()
66 65 }
67 66
68 67 pub fn clear(&mut self) {
69 68 self.state_map.clear();
70 69 self.copy_map.clear();
71 70 self.file_fold_map = None;
72 71 self.non_normal_set = None;
73 72 self.other_parent_set = None;
74 73 self.set_parents(&DirstateParents {
75 74 p1: NULL_NODE_ID,
76 75 p2: NULL_NODE_ID,
77 76 })
78 77 }
79 78
80 79 /// Add a tracked file to the dirstate
81 80 pub fn add_file(
82 81 &mut self,
83 82 filename: &HgPath,
84 83 old_state: EntryState,
85 84 entry: DirstateEntry,
86 85 ) -> Result<(), DirstateMapError> {
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
88 {
86 if old_state == EntryState::Unknown || old_state == EntryState::Removed {
89 87 if let Some(ref mut dirs) = self.dirs {
90 88 dirs.add_path(filename)?;
91 89 }
92 90 }
93 91 if old_state == EntryState::Unknown {
94 92 if let Some(ref mut all_dirs) = self.all_dirs {
95 93 all_dirs.add_path(filename)?;
96 94 }
97 95 }
98 96 self.state_map.insert(filename.to_owned(), entry.to_owned());
99 97
100 98 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 99 self.get_non_normal_other_parent_entries()
102 100 .0
103 101 .insert(filename.to_owned());
104 102 }
105 103
106 104 if entry.size == SIZE_FROM_OTHER_PARENT {
107 105 self.get_non_normal_other_parent_entries()
108 106 .1
109 107 .insert(filename.to_owned());
110 108 }
111 109 Ok(())
112 110 }
113 111
114 112 /// Mark a file as removed in the dirstate.
115 113 ///
116 114 /// The `size` parameter is used to store sentinel values that indicate
117 115 /// the file's previous state. In the future, we should refactor this
118 116 /// to be more explicit about what that state is.
119 117 pub fn remove_file(
120 118 &mut self,
121 119 filename: &HgPath,
122 120 old_state: EntryState,
123 121 size: i32,
124 122 ) -> Result<(), DirstateMapError> {
125 if old_state != EntryState::Unknown && old_state != EntryState::Removed
126 {
123 if old_state != EntryState::Unknown && old_state != EntryState::Removed {
127 124 if let Some(ref mut dirs) = self.dirs {
128 125 dirs.delete_path(filename)?;
129 126 }
130 127 }
131 128 if old_state == EntryState::Unknown {
132 129 if let Some(ref mut all_dirs) = self.all_dirs {
133 130 all_dirs.add_path(filename)?;
134 131 }
135 132 }
136 133
137 134 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 135 file_fold_map.remove(&normalize_case(filename));
139 136 }
140 137 self.state_map.insert(
141 138 filename.to_owned(),
142 139 DirstateEntry {
143 140 state: EntryState::Removed,
144 141 mode: 0,
145 142 size,
146 143 mtime: 0,
147 144 },
148 145 );
149 146 self.get_non_normal_other_parent_entries()
150 147 .0
151 148 .insert(filename.to_owned());
152 149 Ok(())
153 150 }
154 151
155 152 /// Remove a file from the dirstate.
156 153 /// Returns `true` if the file was previously recorded.
157 154 pub fn drop_file(
158 155 &mut self,
159 156 filename: &HgPath,
160 157 old_state: EntryState,
161 158 ) -> Result<bool, DirstateMapError> {
162 159 let exists = self.state_map.remove(filename).is_some();
163 160
164 161 if exists {
165 162 if old_state != EntryState::Removed {
166 163 if let Some(ref mut dirs) = self.dirs {
167 164 dirs.delete_path(filename)?;
168 165 }
169 166 }
170 167 if let Some(ref mut all_dirs) = self.all_dirs {
171 168 all_dirs.delete_path(filename)?;
172 169 }
173 170 }
174 171 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 172 file_fold_map.remove(&normalize_case(filename));
176 173 }
177 174 self.get_non_normal_other_parent_entries()
178 175 .0
179 176 .remove(filename);
180 177
181 178 Ok(exists)
182 179 }
183 180
184 pub fn clear_ambiguous_times(
185 &mut self,
186 filenames: Vec<HgPathBuf>,
187 now: i32,
188 ) {
181 pub fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
189 182 for filename in filenames {
190 183 let mut changed = false;
191 184 self.state_map
192 185 .entry(filename.to_owned())
193 186 .and_modify(|entry| {
194 if entry.state == EntryState::Normal && entry.mtime == now
195 {
187 if entry.state == EntryState::Normal && entry.mtime == now {
196 188 changed = true;
197 189 *entry = DirstateEntry {
198 190 mtime: MTIME_UNSET,
199 191 ..*entry
200 192 };
201 193 }
202 194 });
203 195 if changed {
204 196 self.get_non_normal_other_parent_entries()
205 197 .0
206 198 .insert(filename.to_owned());
207 199 }
208 200 }
209 201 }
210 202
211 pub fn non_normal_entries_remove(
212 &mut self,
213 key: impl AsRef<HgPath>,
214 ) -> bool {
203 pub fn non_normal_entries_remove(&mut self, key: impl AsRef<HgPath>) -> bool {
215 204 self.get_non_normal_other_parent_entries()
216 205 .0
217 206 .remove(key.as_ref())
218 207 }
219 pub fn non_normal_entries_union(
220 &mut self,
221 other: HashSet<HgPathBuf>,
222 ) -> Vec<HgPathBuf> {
208 pub fn non_normal_entries_union(&mut self, other: HashSet<HgPathBuf>) -> Vec<HgPathBuf> {
223 209 self.get_non_normal_other_parent_entries()
224 210 .0
225 211 .union(&other)
226 212 .map(ToOwned::to_owned)
227 213 .collect()
228 214 }
229 215
230 216 pub fn get_non_normal_other_parent_entries(
231 217 &mut self,
232 218 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
233 219 self.set_non_normal_other_parent_entries(false);
234 220 (
235 221 self.non_normal_set.as_mut().unwrap(),
236 222 self.other_parent_set.as_mut().unwrap(),
237 223 )
238 224 }
239 225
240 226 /// Useful to get immutable references to those sets in contexts where
241 227 /// you only have an immutable reference to the `DirstateMap`, like when
242 228 /// sharing references with Python.
243 229 ///
244 230 /// TODO, get rid of this along with the other "setter/getter" stuff when
245 231 /// a nice typestate plan is defined.
246 232 ///
247 233 /// # Panics
248 234 ///
249 235 /// Will panic if either set is `None`.
250 236 pub fn get_non_normal_other_parent_entries_panic(
251 237 &self,
252 238 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
253 239 (
254 240 self.non_normal_set.as_ref().unwrap(),
255 241 self.other_parent_set.as_ref().unwrap(),
256 242 )
257 243 }
258 244
259 245 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
260 if !force
261 && self.non_normal_set.is_some()
262 && self.other_parent_set.is_some()
263 {
246 if !force && self.non_normal_set.is_some() && self.other_parent_set.is_some() {
264 247 return;
265 248 }
266 249 let mut non_normal = HashSet::new();
267 250 let mut other_parent = HashSet::new();
268 251
269 252 for (
270 253 filename,
271 254 DirstateEntry {
272 255 state, size, mtime, ..
273 256 },
274 257 ) in self.state_map.iter()
275 258 {
276 259 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
277 260 non_normal.insert(filename.to_owned());
278 261 }
279 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
280 {
262 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT {
281 263 other_parent.insert(filename.to_owned());
282 264 }
283 265 }
284 266 self.non_normal_set = Some(non_normal);
285 267 self.other_parent_set = Some(other_parent);
286 268 }
287 269
288 270 /// Both of these setters and their uses appear to be the simplest way to
289 271 /// emulate a Python lazy property, but it is ugly and unidiomatic.
290 272 /// TODO One day, rewriting this struct using the typestate might be a
291 273 /// good idea.
292 274 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
293 275 if self.all_dirs.is_none() {
294 self.all_dirs =
295 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
276 self.all_dirs = Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
296 277 }
297 278 Ok(())
298 279 }
299 280
300 281 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
301 282 if self.dirs.is_none() {
302 283 self.dirs = Some(DirsMultiset::from_dirstate(
303 284 &self.state_map,
304 285 Some(EntryState::Removed),
305 286 )?);
306 287 }
307 288 Ok(())
308 289 }
309 290
310 pub fn has_tracked_dir(
311 &mut self,
312 directory: &HgPath,
313 ) -> Result<bool, DirstateMapError> {
291 pub fn has_tracked_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateMapError> {
314 292 self.set_dirs()?;
315 293 Ok(self.dirs.as_ref().unwrap().contains(directory))
316 294 }
317 295
318 pub fn has_dir(
319 &mut self,
320 directory: &HgPath,
321 ) -> Result<bool, DirstateMapError> {
296 pub fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateMapError> {
322 297 self.set_all_dirs()?;
323 298 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
324 299 }
325 300
326 pub fn parents(
327 &mut self,
328 file_contents: &[u8],
329 ) -> Result<&DirstateParents, DirstateError> {
301 pub fn parents(&mut self, file_contents: &[u8]) -> Result<&DirstateParents, DirstateError> {
330 302 if let Some(ref parents) = self.parents {
331 303 return Ok(parents);
332 304 }
333 305 let parents;
334 306 if file_contents.len() == PARENT_SIZE * 2 {
335 307 parents = DirstateParents {
336 308 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
337 309 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
338 310 .try_into()
339 311 .unwrap(),
340 312 };
341 313 } else if file_contents.is_empty() {
342 314 parents = DirstateParents {
343 315 p1: NULL_NODE_ID,
344 316 p2: NULL_NODE_ID,
345 317 };
346 318 } else {
347 319 return Err(DirstateError::Parse(DirstateParseError::Damaged));
348 320 }
349 321
350 322 self.parents = Some(parents);
351 323 Ok(self.parents.as_ref().unwrap())
352 324 }
353 325
354 326 pub fn set_parents(&mut self, parents: &DirstateParents) {
355 327 self.parents = Some(parents.clone());
356 328 self.dirty_parents = true;
357 329 }
358 330
359 pub fn read(
360 &mut self,
361 file_contents: &[u8],
362 ) -> Result<Option<DirstateParents>, DirstateError> {
331 #[timed]
332 pub fn read(&mut self, file_contents: &[u8]) -> Result<Option<DirstateParents>, DirstateError> {
363 333 if file_contents.is_empty() {
364 334 return Ok(None);
365 335 }
366 336
367 337 let (parents, entries, copies) = parse_dirstate(file_contents)?;
368 338 self.state_map.extend(
369 339 entries
370 340 .into_iter()
371 341 .map(|(path, entry)| (path.to_owned(), entry)),
372 342 );
373 343 self.copy_map.extend(
374 344 copies
375 345 .into_iter()
376 346 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
377 347 );
378 348
379 349 if !self.dirty_parents {
380 350 self.set_parents(&parents);
381 351 }
382 352
383 353 Ok(Some(parents))
384 354 }
385 355
386 356 pub fn pack(
387 357 &mut self,
388 358 parents: DirstateParents,
389 359 now: Duration,
390 360 ) -> Result<Vec<u8>, DirstateError> {
391 let packed =
392 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
361 let packed = pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
393 362
394 363 self.dirty_parents = false;
395 364
396 365 self.set_non_normal_other_parent_entries(true);
397 366 Ok(packed)
398 367 }
399 368
400 369 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
401 370 if let Some(ref file_fold_map) = self.file_fold_map {
402 371 return file_fold_map;
403 372 }
404 373 let mut new_file_fold_map = FileFoldMap::default();
405 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
406 {
374 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow() {
407 375 if *state == EntryState::Removed {
408 new_file_fold_map
409 .insert(normalize_case(filename), filename.to_owned());
376 new_file_fold_map.insert(normalize_case(filename), filename.to_owned());
410 377 }
411 378 }
412 379 self.file_fold_map = Some(new_file_fold_map);
413 380 self.file_fold_map.as_ref().unwrap()
414 381 }
415 382 }
416 383
417 384 #[cfg(test)]
418 385 mod tests {
419 386 use super::*;
420 387
421 388 #[test]
422 389 fn test_dirs_multiset() {
423 390 let mut map = DirstateMap::new();
424 391 assert!(map.dirs.is_none());
425 392 assert!(map.all_dirs.is_none());
426 393
427 394 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
428 395 assert!(map.all_dirs.is_some());
429 396 assert!(map.dirs.is_none());
430 397
431 398 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
432 399 assert!(map.dirs.is_some());
433 400 }
434 401
435 402 #[test]
436 403 fn test_add_file() {
437 404 let mut map = DirstateMap::new();
438 405
439 406 assert_eq!(0, map.len());
440 407
441 408 map.add_file(
442 409 HgPath::new(b"meh"),
443 410 EntryState::Normal,
444 411 DirstateEntry {
445 412 state: EntryState::Normal,
446 413 mode: 1337,
447 414 mtime: 1337,
448 415 size: 1337,
449 416 },
450 417 )
451 418 .unwrap();
452 419
453 420 assert_eq!(1, map.len());
454 421 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
455 422 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
456 423 }
457 424
458 425 #[test]
459 426 fn test_non_normal_other_parent_entries() {
460 427 let mut map: DirstateMap = [
461 428 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
462 429 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
463 430 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
464 431 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
465 432 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
466 433 (b"f6", (EntryState::Added, 1337, 1337, -1)),
467 434 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
468 435 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
469 436 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
470 437 (b"fa", (EntryState::Added, 1337, -2, 1337)),
471 438 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
472 439 ]
473 440 .iter()
474 441 .map(|(fname, (state, mode, size, mtime))| {
475 442 (
476 443 HgPathBuf::from_bytes(fname.as_ref()),
477 444 DirstateEntry {
478 445 state: *state,
479 446 mode: *mode,
480 447 size: *size,
481 448 mtime: *mtime,
482 449 },
483 450 )
484 451 })
485 452 .collect();
486 453
487 454 let mut non_normal = [
488 455 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
489 456 ]
490 457 .iter()
491 458 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
492 459 .collect();
493 460
494 461 let mut other_parent = HashSet::new();
495 462 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
496 463 let entries = map.get_non_normal_other_parent_entries();
497 464
498 assert_eq!(
499 (&mut non_normal, &mut other_parent),
500 (entries.0, entries.1)
501 );
465 assert_eq!((&mut non_normal, &mut other_parent), (entries.0, entries.1));
502 466 }
503 467 }
General Comments 0
You need to be logged in to leave comments. Login now