comparison rust/hg-core/src/dirstate/entry.rs @ 48260:269ff8978086

dirstate: store mtimes with nanosecond precision in memory Keep integer seconds since the Unix epoch, together with integer nanoseconds in the `0 <= n < 1e9` range. For now, nanoseconds are still always zero. This commit is about data structure changes. Differential Revision: https://phab.mercurial-scm.org/D11684
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 18 Oct 2021 11:23:07 +0200
parents 84f6b0c41b90
children 68bb472aee9c
comparison
equal deleted inserted replaced
48259:84f6b0c41b90 48260:269ff8978086
12 Added, 12 Added,
13 Removed, 13 Removed,
14 Merged, 14 Merged,
15 } 15 }
16 16
17 /// The C implementation uses all signed types. This will be an issue 17 /// `size` and `mtime.seconds` are truncated to 31 bits.
18 /// either when 4GB+ source files are commonplace or in 2038, whichever 18 ///
19 /// comes first. 19 /// TODO: double-check status algorithm correctness for files
20 #[derive(Debug, PartialEq, Copy, Clone)] 20 /// larger than 2 GiB or modified after 2038.
21 #[derive(Debug, Copy, Clone)]
21 pub struct DirstateEntry { 22 pub struct DirstateEntry {
22 pub(crate) flags: Flags, 23 pub(crate) flags: Flags,
23 mode_size: Option<(u32, u32)>, 24 mode_size: Option<(u32, u32)>,
24 mtime: Option<u32>, 25 mtime: Option<TruncatedTimestamp>,
25 } 26 }
26 27
27 bitflags! { 28 bitflags! {
28 pub(crate) struct Flags: u8 { 29 pub(crate) struct Flags: u8 {
29 const WDIR_TRACKED = 1 << 0; 30 const WDIR_TRACKED = 1 << 0;
35 const FALLBACK_SYMLINK = 1 << 6; 36 const FALLBACK_SYMLINK = 1 << 6;
36 } 37 }
37 } 38 }
38 39
39 /// A Unix timestamp with nanoseconds precision 40 /// A Unix timestamp with nanoseconds precision
40 #[derive(Copy, Clone)] 41 #[derive(Debug, Copy, Clone)]
41 pub struct TruncatedTimestamp { 42 pub struct TruncatedTimestamp {
42 truncated_seconds: u32, 43 truncated_seconds: u32,
43 /// Always in the `0 .. 1_000_000_000` range. 44 /// Always in the `0 .. 1_000_000_000` range.
44 nanoseconds: u32, 45 nanoseconds: u32,
45 } 46 }
86 } 87 }
87 #[cfg(not(unix))] 88 #[cfg(not(unix))]
88 { 89 {
89 metadata.modified().map(Self::from) 90 metadata.modified().map(Self::from)
90 } 91 }
92 }
93
94 pub fn to_integer_second(mut self) -> Self {
95 self.nanoseconds = 0;
96 self
91 } 97 }
92 98
93 /// The lower 31 bits of the number of seconds since the epoch. 99 /// The lower 31 bits of the number of seconds since the epoch.
94 pub fn truncated_seconds(&self) -> u32 { 100 pub fn truncated_seconds(&self) -> u32 {
95 self.truncated_seconds 101 self.truncated_seconds
180 pub fn from_v2_data( 186 pub fn from_v2_data(
181 wdir_tracked: bool, 187 wdir_tracked: bool,
182 p1_tracked: bool, 188 p1_tracked: bool,
183 p2_info: bool, 189 p2_info: bool,
184 mode_size: Option<(u32, u32)>, 190 mode_size: Option<(u32, u32)>,
185 mtime: Option<u32>, 191 mtime: Option<TruncatedTimestamp>,
186 fallback_exec: Option<bool>, 192 fallback_exec: Option<bool>,
187 fallback_symlink: Option<bool>, 193 fallback_symlink: Option<bool>,
188 ) -> Self { 194 ) -> Self {
189 if let Some((mode, size)) = mode_size { 195 if let Some((mode, size)) = mode_size {
190 // TODO: return an error for out of range values? 196 // TODO: return an error for out of range values?
191 assert!(mode & !RANGE_MASK_31BIT == 0); 197 assert!(mode & !RANGE_MASK_31BIT == 0);
192 assert!(size & !RANGE_MASK_31BIT == 0); 198 assert!(size & !RANGE_MASK_31BIT == 0);
193 }
194 if let Some(mtime) = mtime {
195 assert!(mtime & !RANGE_MASK_31BIT == 0);
196 } 199 }
197 let mut flags = Flags::empty(); 200 let mut flags = Flags::empty();
198 flags.set(Flags::WDIR_TRACKED, wdir_tracked); 201 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
199 flags.set(Flags::P1_TRACKED, p1_tracked); 202 flags.set(Flags::P1_TRACKED, p1_tracked);
200 flags.set(Flags::P2_INFO, p2_info); 203 flags.set(Flags::P2_INFO, p2_info);
250 } else { 253 } else {
251 // TODO: return an error for negative values? 254 // TODO: return an error for negative values?
252 let mode = u32::try_from(mode).unwrap(); 255 let mode = u32::try_from(mode).unwrap();
253 let size = u32::try_from(size).unwrap(); 256 let size = u32::try_from(size).unwrap();
254 let mtime = u32::try_from(mtime).unwrap(); 257 let mtime = u32::try_from(mtime).unwrap();
258 let mtime =
259 TruncatedTimestamp::from_already_truncated(mtime, 0)
260 .unwrap();
255 Self { 261 Self {
256 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, 262 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
257 mode_size: Some((mode, size)), 263 mode_size: Some((mode, size)),
258 mtime: Some(mtime), 264 mtime: Some(mtime),
259 } 265 }
342 ) -> ( 348 ) -> (
343 bool, 349 bool,
344 bool, 350 bool,
345 bool, 351 bool,
346 Option<(u32, u32)>, 352 Option<(u32, u32)>,
347 Option<u32>, 353 Option<TruncatedTimestamp>,
348 Option<bool>, 354 Option<bool>,
349 Option<bool>, 355 Option<bool>,
350 ) { 356 ) {
351 if !self.any_tracked() { 357 if !self.any_tracked() {
352 // TODO: return an Option instead? 358 // TODO: return an Option instead?
427 } else if self.flags.contains(Flags::P2_INFO) { 433 } else if self.flags.contains(Flags::P2_INFO) {
428 MTIME_UNSET 434 MTIME_UNSET
429 } else if !self.flags.contains(Flags::P1_TRACKED) { 435 } else if !self.flags.contains(Flags::P1_TRACKED) {
430 MTIME_UNSET 436 MTIME_UNSET
431 } else if let Some(mtime) = self.mtime { 437 } else if let Some(mtime) = self.mtime {
432 i32::try_from(mtime).unwrap() 438 i32::try_from(mtime.truncated_seconds()).unwrap()
433 } else { 439 } else {
434 MTIME_UNSET 440 MTIME_UNSET
435 } 441 }
436 } 442 }
437 443
499 } 505 }
500 } 506 }
501 } 507 }
502 } 508 }
503 509
510 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
511 self.mtime
512 }
513
504 pub fn drop_merge_data(&mut self) { 514 pub fn drop_merge_data(&mut self) {
505 if self.flags.contains(Flags::P2_INFO) { 515 if self.flags.contains(Flags::P2_INFO) {
506 self.flags.remove(Flags::P2_INFO); 516 self.flags.remove(Flags::P2_INFO);
507 self.mode_size = None; 517 self.mode_size = None;
508 self.mtime = None; 518 self.mtime = None;
511 521
512 pub fn set_possibly_dirty(&mut self) { 522 pub fn set_possibly_dirty(&mut self) {
513 self.mtime = None 523 self.mtime = None
514 } 524 }
515 525
516 pub fn set_clean(&mut self, mode: u32, size: u32, mtime: u32) { 526 pub fn set_clean(
527 &mut self,
528 mode: u32,
529 size: u32,
530 mtime: TruncatedTimestamp,
531 ) {
517 let size = size & RANGE_MASK_31BIT; 532 let size = size & RANGE_MASK_31BIT;
518 let mtime = mtime & RANGE_MASK_31BIT;
519 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED); 533 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
520 self.mode_size = Some((mode, size)); 534 self.mode_size = Some((mode, size));
521 self.mtime = Some(mtime); 535 self.mtime = Some(mtime);
522 } 536 }
523 537
575 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { 589 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
576 (self.state().into(), self.mode(), self.size(), self.mtime()) 590 (self.state().into(), self.mode(), self.size(), self.mtime())
577 } 591 }
578 592
579 /// True if the stored mtime would be ambiguous with the current time 593 /// True if the stored mtime would be ambiguous with the current time
580 pub fn need_delay(&self, now: i32) -> bool { 594 pub fn need_delay(&self, now: TruncatedTimestamp) -> bool {
581 self.state() == EntryState::Normal && self.mtime() == now 595 if let Some(mtime) = self.mtime {
596 self.state() == EntryState::Normal
597 && mtime.truncated_seconds() == now.truncated_seconds()
598 } else {
599 false
600 }
582 } 601 }
583 } 602 }
584 603
585 impl EntryState { 604 impl EntryState {
586 pub fn is_tracked(self) -> bool { 605 pub fn is_tracked(self) -> bool {