Show More
@@ -1,6 +1,7 b'' | |||
|
1 | 1 | use crate::errors::HgError; |
|
2 | 2 | use bitflags::bitflags; |
|
3 | 3 | use std::convert::TryFrom; |
|
4 | use std::time::{SystemTime, UNIX_EPOCH}; | |
|
4 | 5 | |
|
5 | 6 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
|
6 | 7 | pub enum EntryState { |
@@ -28,6 +29,73 b' bitflags! {' | |||
|
28 | 29 | } |
|
29 | 30 | } |
|
30 | 31 | |
|
32 | #[derive(Copy, Clone, PartialEq)] | |
|
33 | pub struct Timestamp { | |
|
34 | seconds: i64, | |
|
35 | ||
|
36 | /// In `0 .. 1_000_000_000`. | |
|
37 | /// | |
|
38 | /// This timestamp is after `(seconds, 0)` by this many nanoseconds. | |
|
39 | nanoseconds: u32, | |
|
40 | } | |
|
41 | ||
|
42 | impl Timestamp { | |
|
43 | pub fn new(seconds: i64, nanoseconds: u32) -> Self { | |
|
44 | Self { | |
|
45 | seconds, | |
|
46 | nanoseconds, | |
|
47 | } | |
|
48 | } | |
|
49 | ||
|
50 | pub fn seconds(&self) -> i64 { | |
|
51 | self.seconds | |
|
52 | } | |
|
53 | ||
|
54 | pub fn nanoseconds(&self) -> u32 { | |
|
55 | self.nanoseconds | |
|
56 | } | |
|
57 | } | |
|
58 | ||
|
59 | impl From<SystemTime> for Timestamp { | |
|
60 | fn from(system_time: SystemTime) -> Self { | |
|
61 | // On Unix, `SystemTime` is a wrapper for the `timespec` C struct: | |
|
62 | // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec | |
|
63 | // We want to effectively access its fields, but the Rust standard | |
|
64 | // library does not expose them. The best we can do is: | |
|
65 | let seconds; | |
|
66 | let nanoseconds; | |
|
67 | match system_time.duration_since(UNIX_EPOCH) { | |
|
68 | Ok(duration) => { | |
|
69 | seconds = duration.as_secs() as i64; | |
|
70 | nanoseconds = duration.subsec_nanos(); | |
|
71 | } | |
|
72 | Err(error) => { | |
|
73 | // `system_time` is before `UNIX_EPOCH`. | |
|
74 | // We need to undo this algorithm: | |
|
75 | // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41 | |
|
76 | let negative = error.duration(); | |
|
77 | let negative_secs = negative.as_secs() as i64; | |
|
78 | let negative_nanos = negative.subsec_nanos(); | |
|
79 | if negative_nanos == 0 { | |
|
80 | seconds = -negative_secs; | |
|
81 | nanoseconds = 0; | |
|
82 | } else { | |
|
83 | // For example if `system_time` was 4.3Β seconds before | |
|
84 | // the Unix epoch we get a Duration that represents | |
|
85 | // `(-4, -0.3)` but we want `(-5, +0.7)`: | |
|
86 | const NSEC_PER_SEC: u32 = 1_000_000_000; | |
|
87 | seconds = -1 - negative_secs; | |
|
88 | nanoseconds = NSEC_PER_SEC - negative_nanos; | |
|
89 | } | |
|
90 | } | |
|
91 | }; | |
|
92 | Self { | |
|
93 | seconds, | |
|
94 | nanoseconds, | |
|
95 | } | |
|
96 | } | |
|
97 | } | |
|
98 | ||
|
31 | 99 | pub const V1_RANGEMASK: i32 = 0x7FFFFFFF; |
|
32 | 100 | |
|
33 | 101 | pub const MTIME_UNSET: i32 = -1; |
@@ -330,9 +330,9 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on" | |||
|
330 | 330 | |
|
331 | 331 | pub(super) fn cached_directory_mtime( |
|
332 | 332 | &self, |
|
333 |
) -> Option< |
|
|
333 | ) -> Option<crate::dirstate::Timestamp> { | |
|
334 | 334 | match self { |
|
335 |
NodeRef::InMemory(_path, node) => match |
|
|
335 | NodeRef::InMemory(_path, node) => match node.data { | |
|
336 | 336 | NodeData::CachedDirectory { mtime } => Some(mtime), |
|
337 | 337 | _ => None, |
|
338 | 338 | }, |
@@ -376,7 +376,7 b" pub(super) struct Node<'on_disk> {" | |||
|
376 | 376 | |
|
377 | 377 | pub(super) enum NodeData { |
|
378 | 378 | Entry(DirstateEntry), |
|
379 |
CachedDirectory { mtime: |
|
|
379 | CachedDirectory { mtime: crate::dirstate::Timestamp }, | |
|
380 | 380 | None, |
|
381 | 381 | } |
|
382 | 382 |
@@ -2,6 +2,7 b'' | |||
|
2 | 2 | //! |
|
3 | 3 | //! See `mercurial/helptext/internals/dirstate-v2.txt` |
|
4 | 4 | |
|
5 | use crate::dirstate::Timestamp; | |
|
5 | 6 | use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; |
|
6 | 7 | use crate::dirstate_tree::path_with_basename::WithBasename; |
|
7 | 8 | use crate::errors::HgError; |
@@ -15,7 +16,6 b' use bytes_cast::BytesCast;' | |||
|
15 | 16 | use format_bytes::format_bytes; |
|
16 | 17 | use std::borrow::Cow; |
|
17 | 18 | use std::convert::{TryFrom, TryInto}; |
|
18 | use std::time::{SystemTime, UNIX_EPOCH}; | |
|
19 | 19 | |
|
20 | 20 | /// Added at the start of `.hg/dirstate` when the "v2" format is used. |
|
21 | 21 | /// This a redundant sanity check more than an actual "magic number" since |
@@ -119,9 +119,9 b' struct Entry {' | |||
|
119 | 119 | } |
|
120 | 120 | |
|
121 | 121 | /// Duration since the Unix epoch |
|
122 |
#[derive(BytesCast, Copy, Clone |
|
|
122 | #[derive(BytesCast, Copy, Clone)] | |
|
123 | 123 | #[repr(C)] |
|
124 |
|
|
|
124 | struct PackedTimestamp { | |
|
125 | 125 | seconds: I64Be, |
|
126 | 126 | |
|
127 | 127 | /// In `0 .. 1_000_000_000`. |
@@ -316,14 +316,14 b' impl Node {' | |||
|
316 | 316 | ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> { |
|
317 | 317 | if self.has_entry() { |
|
318 | 318 | Ok(dirstate_map::NodeData::Entry(self.assume_entry())) |
|
319 |
} else if let Some( |
|
|
319 | } else if let Some(mtime) = self.cached_directory_mtime() { | |
|
320 | 320 | Ok(dirstate_map::NodeData::CachedDirectory { mtime }) |
|
321 | 321 | } else { |
|
322 | 322 | Ok(dirstate_map::NodeData::None) |
|
323 | 323 | } |
|
324 | 324 | } |
|
325 | 325 | |
|
326 |
pub(super) fn cached_directory_mtime(&self) -> Option< |
|
|
326 | pub(super) fn cached_directory_mtime(&self) -> Option<Timestamp> { | |
|
327 | 327 | if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() { |
|
328 | 328 | Some(self.data.as_timestamp()) |
|
329 | 329 | } else { |
@@ -423,58 +423,23 b' impl Entry {' | |||
|
423 | 423 | } |
|
424 | 424 | |
|
425 | 425 | fn from_timestamp(timestamp: Timestamp) -> Self { |
|
426 | let packed = PackedTimestamp { | |
|
427 | seconds: timestamp.seconds().into(), | |
|
428 | nanoseconds: timestamp.nanoseconds().into(), | |
|
429 | }; | |
|
426 | 430 | // Safety: both types implement the `ByteCast` trait, so we could |
|
427 | 431 | // safely use `as_bytes` and `from_bytes` to do this conversion. Using |
|
428 | 432 | // `transmute` instead makes the compiler check that the two types |
|
429 | 433 | // have the same size, which eliminates the error case of |
|
430 | 434 | // `from_bytes`. |
|
431 |
unsafe { std::mem::transmute::<Timestamp, Entry>( |
|
|
435 | unsafe { std::mem::transmute::<PackedTimestamp, Entry>(packed) } | |
|
432 | 436 | } |
|
433 | 437 | |
|
434 |
fn as_timestamp( |
|
|
438 | fn as_timestamp(self) -> Timestamp { | |
|
435 | 439 | // Safety: same as above in `from_timestamp` |
|
436 | unsafe { &*(self as *const Entry as *const Timestamp) } | |
|
437 | } | |
|
438 | } | |
|
439 | ||
|
440 | impl Timestamp { | |
|
441 | pub fn seconds(&self) -> i64 { | |
|
442 | self.seconds.get() | |
|
443 | } | |
|
444 | } | |
|
445 | ||
|
446 | impl From<SystemTime> for Timestamp { | |
|
447 | fn from(system_time: SystemTime) -> Self { | |
|
448 | // On Unix, `SystemTime` is a wrapper for the `timespec` C struct: | |
|
449 | // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec | |
|
450 | // We want to effectively access its fields, but the Rust standard | |
|
451 | // library does not expose them. The best we can do is: | |
|
452 | let (secs, nanos) = match system_time.duration_since(UNIX_EPOCH) { | |
|
453 | Ok(duration) => { | |
|
454 | (duration.as_secs() as i64, duration.subsec_nanos()) | |
|
455 | } | |
|
456 | Err(error) => { | |
|
457 | // `system_time` is before `UNIX_EPOCH`. | |
|
458 | // We need to undo this algorithm: | |
|
459 | // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41 | |
|
460 | let negative = error.duration(); | |
|
461 | let negative_secs = negative.as_secs() as i64; | |
|
462 | let negative_nanos = negative.subsec_nanos(); | |
|
463 | if negative_nanos == 0 { | |
|
464 | (-negative_secs, 0) | |
|
465 | } else { | |
|
466 | // For example if `system_time` was 4.3Β seconds before | |
|
467 | // the Unix epoch we get a Duration that represents | |
|
468 | // `(-4, -0.3)` but we want `(-5, +0.7)`: | |
|
469 | const NSEC_PER_SEC: u32 = 1_000_000_000; | |
|
470 | (-1 - negative_secs, NSEC_PER_SEC - negative_nanos) | |
|
471 | } | |
|
472 | } | |
|
473 | }; | |
|
474 | Timestamp { | |
|
475 | seconds: secs.into(), | |
|
476 | nanoseconds: nanos.into(), | |
|
477 | } | |
|
440 | let packed = | |
|
441 | unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) }; | |
|
442 | Timestamp::new(packed.seconds.get(), packed.nanoseconds.get()) | |
|
478 | 443 | } |
|
479 | 444 | } |
|
480 | 445 |
@@ -1,3 +1,4 b'' | |||
|
1 | use crate::dirstate::entry::Timestamp; | |
|
1 | 2 | use crate::dirstate::status::IgnoreFnType; |
|
2 | 3 | use crate::dirstate_tree::dirstate_map::BorrowedPath; |
|
3 | 4 | use crate::dirstate_tree::dirstate_map::ChildNodesRef; |
@@ -5,7 +6,6 b' use crate::dirstate_tree::dirstate_map::' | |||
|
5 | 6 | use crate::dirstate_tree::dirstate_map::NodeData; |
|
6 | 7 | use crate::dirstate_tree::dirstate_map::NodeRef; |
|
7 | 8 | use crate::dirstate_tree::on_disk::DirstateV2ParseError; |
|
8 | use crate::dirstate_tree::on_disk::Timestamp; | |
|
9 | 9 | use crate::matchers::get_ignore_function; |
|
10 | 10 | use crate::matchers::Matcher; |
|
11 | 11 | use crate::utils::files::get_bytes_from_os_string; |
@@ -182,7 +182,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
182 | 182 | fn can_skip_fs_readdir( |
|
183 | 183 | &self, |
|
184 | 184 | directory_metadata: Option<&std::fs::Metadata>, |
|
185 |
cached_directory_mtime: Option< |
|
|
185 | cached_directory_mtime: Option<Timestamp>, | |
|
186 | 186 | ) -> bool { |
|
187 | 187 | if !self.options.list_unknown && !self.options.list_ignored { |
|
188 | 188 | // All states that we care about listing have corresponding |
@@ -200,7 +200,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
200 | 200 | if let Some(meta) = directory_metadata { |
|
201 | 201 | if let Ok(current_mtime) = meta.modified() { |
|
202 | 202 | let current_mtime = Timestamp::from(current_mtime); |
|
203 |
if current_mtime == |
|
|
203 | if current_mtime == cached_mtime { | |
|
204 | 204 | // The mtime of that directory has not changed |
|
205 | 205 | // since then, which means that the results of |
|
206 | 206 | // `read_dir` should also be unchanged. |
@@ -222,7 +222,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
222 | 222 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
|
223 | 223 | directory_fs_path: &Path, |
|
224 | 224 | directory_metadata: Option<&std::fs::Metadata>, |
|
225 |
cached_directory_mtime: Option< |
|
|
225 | cached_directory_mtime: Option<Timestamp>, | |
|
226 | 226 | is_at_repo_root: bool, |
|
227 | 227 | ) -> Result<bool, DirstateV2ParseError> { |
|
228 | 228 | if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime) |
@@ -468,7 +468,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
468 | 468 | // unlikely enough in practice. |
|
469 | 469 | let timestamp = directory_mtime.into(); |
|
470 | 470 | let cached = dirstate_node.cached_directory_mtime(); |
|
471 |
if cached != Some( |
|
|
471 | if cached != Some(timestamp) { | |
|
472 | 472 | let hg_path = dirstate_node |
|
473 | 473 | .full_path_borrowed(self.dmap.on_disk)? |
|
474 | 474 | .detach_from_tree(); |
General Comments 0
You need to be logged in to leave comments.
Login now