##// END OF EJS Templates
dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp...
Simon Sapin -
r49006:d2f760c2 default
parent child Browse files
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<&'tree on_disk::Timestamp> {
333 ) -> Option<crate::dirstate::Timestamp> {
334 334 match self {
335 NodeRef::InMemory(_path, node) => match &node.data {
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: on_disk::Timestamp },
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, PartialEq)]
122 #[derive(BytesCast, Copy, Clone)]
123 123 #[repr(C)]
124 pub(super) struct Timestamp {
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(&mtime) = self.cached_directory_mtime() {
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<&Timestamp> {
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>(timestamp) }
435 unsafe { std::mem::transmute::<PackedTimestamp, Entry>(packed) }
432 436 }
433 437
434 fn as_timestamp(&self) -> &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<&Timestamp>,
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 == *cached_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<&Timestamp>,
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(&timestamp) {
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