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