##// 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 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use bitflags::bitflags;
2 use bitflags::bitflags;
3 use std::convert::TryFrom;
3 use std::convert::TryFrom;
4 use std::time::{SystemTime, UNIX_EPOCH};
4
5
5 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
6 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
6 pub enum EntryState {
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 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
99 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
32
100
33 pub const MTIME_UNSET: i32 = -1;
101 pub const MTIME_UNSET: i32 = -1;
@@ -330,9 +330,9 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on"
330
330
331 pub(super) fn cached_directory_mtime(
331 pub(super) fn cached_directory_mtime(
332 &self,
332 &self,
333 ) -> Option<&'tree on_disk::Timestamp> {
333 ) -> Option<crate::dirstate::Timestamp> {
334 match self {
334 match self {
335 NodeRef::InMemory(_path, node) => match &node.data {
335 NodeRef::InMemory(_path, node) => match node.data {
336 NodeData::CachedDirectory { mtime } => Some(mtime),
336 NodeData::CachedDirectory { mtime } => Some(mtime),
337 _ => None,
337 _ => None,
338 },
338 },
@@ -376,7 +376,7 b" pub(super) struct Node<'on_disk> {"
376
376
377 pub(super) enum NodeData {
377 pub(super) enum NodeData {
378 Entry(DirstateEntry),
378 Entry(DirstateEntry),
379 CachedDirectory { mtime: on_disk::Timestamp },
379 CachedDirectory { mtime: crate::dirstate::Timestamp },
380 None,
380 None,
381 }
381 }
382
382
@@ -2,6 +2,7 b''
2 //!
2 //!
3 //! See `mercurial/helptext/internals/dirstate-v2.txt`
3 //! See `mercurial/helptext/internals/dirstate-v2.txt`
4
4
5 use crate::dirstate::Timestamp;
5 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef};
6 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef};
6 use crate::dirstate_tree::path_with_basename::WithBasename;
7 use crate::dirstate_tree::path_with_basename::WithBasename;
7 use crate::errors::HgError;
8 use crate::errors::HgError;
@@ -15,7 +16,6 b' use bytes_cast::BytesCast;'
15 use format_bytes::format_bytes;
16 use format_bytes::format_bytes;
16 use std::borrow::Cow;
17 use std::borrow::Cow;
17 use std::convert::{TryFrom, TryInto};
18 use std::convert::{TryFrom, TryInto};
18 use std::time::{SystemTime, UNIX_EPOCH};
19
19
20 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
20 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
21 /// This a redundant sanity check more than an actual "magic number" since
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 /// Duration since the Unix epoch
121 /// Duration since the Unix epoch
122 #[derive(BytesCast, Copy, Clone, PartialEq)]
122 #[derive(BytesCast, Copy, Clone)]
123 #[repr(C)]
123 #[repr(C)]
124 pub(super) struct Timestamp {
124 struct PackedTimestamp {
125 seconds: I64Be,
125 seconds: I64Be,
126
126
127 /// In `0 .. 1_000_000_000`.
127 /// In `0 .. 1_000_000_000`.
@@ -316,14 +316,14 b' impl Node {'
316 ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> {
316 ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> {
317 if self.has_entry() {
317 if self.has_entry() {
318 Ok(dirstate_map::NodeData::Entry(self.assume_entry()))
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 Ok(dirstate_map::NodeData::CachedDirectory { mtime })
320 Ok(dirstate_map::NodeData::CachedDirectory { mtime })
321 } else {
321 } else {
322 Ok(dirstate_map::NodeData::None)
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 if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
327 if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
328 Some(self.data.as_timestamp())
328 Some(self.data.as_timestamp())
329 } else {
329 } else {
@@ -423,58 +423,23 b' impl Entry {'
423 }
423 }
424
424
425 fn from_timestamp(timestamp: Timestamp) -> Self {
425 fn from_timestamp(timestamp: Timestamp) -> Self {
426 let packed = PackedTimestamp {
427 seconds: timestamp.seconds().into(),
428 nanoseconds: timestamp.nanoseconds().into(),
429 };
426 // Safety: both types implement the `ByteCast` trait, so we could
430 // Safety: both types implement the `ByteCast` trait, so we could
427 // safely use `as_bytes` and `from_bytes` to do this conversion. Using
431 // safely use `as_bytes` and `from_bytes` to do this conversion. Using
428 // `transmute` instead makes the compiler check that the two types
432 // `transmute` instead makes the compiler check that the two types
429 // have the same size, which eliminates the error case of
433 // have the same size, which eliminates the error case of
430 // `from_bytes`.
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 // Safety: same as above in `from_timestamp`
439 // Safety: same as above in `from_timestamp`
436 unsafe { &*(self as *const Entry as *const Timestamp) }
440 let packed =
437 }
441 unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) };
438 }
442 Timestamp::new(packed.seconds.get(), packed.nanoseconds.get())
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 }
478 }
443 }
479 }
444 }
480
445
@@ -1,3 +1,4 b''
1 use crate::dirstate::entry::Timestamp;
1 use crate::dirstate::status::IgnoreFnType;
2 use crate::dirstate::status::IgnoreFnType;
2 use crate::dirstate_tree::dirstate_map::BorrowedPath;
3 use crate::dirstate_tree::dirstate_map::BorrowedPath;
3 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
4 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
@@ -5,7 +6,6 b' use crate::dirstate_tree::dirstate_map::'
5 use crate::dirstate_tree::dirstate_map::NodeData;
6 use crate::dirstate_tree::dirstate_map::NodeData;
6 use crate::dirstate_tree::dirstate_map::NodeRef;
7 use crate::dirstate_tree::dirstate_map::NodeRef;
7 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
8 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
8 use crate::dirstate_tree::on_disk::Timestamp;
9 use crate::matchers::get_ignore_function;
9 use crate::matchers::get_ignore_function;
10 use crate::matchers::Matcher;
10 use crate::matchers::Matcher;
11 use crate::utils::files::get_bytes_from_os_string;
11 use crate::utils::files::get_bytes_from_os_string;
@@ -182,7 +182,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
182 fn can_skip_fs_readdir(
182 fn can_skip_fs_readdir(
183 &self,
183 &self,
184 directory_metadata: Option<&std::fs::Metadata>,
184 directory_metadata: Option<&std::fs::Metadata>,
185 cached_directory_mtime: Option<&Timestamp>,
185 cached_directory_mtime: Option<Timestamp>,
186 ) -> bool {
186 ) -> bool {
187 if !self.options.list_unknown && !self.options.list_ignored {
187 if !self.options.list_unknown && !self.options.list_ignored {
188 // All states that we care about listing have corresponding
188 // All states that we care about listing have corresponding
@@ -200,7 +200,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
200 if let Some(meta) = directory_metadata {
200 if let Some(meta) = directory_metadata {
201 if let Ok(current_mtime) = meta.modified() {
201 if let Ok(current_mtime) = meta.modified() {
202 let current_mtime = Timestamp::from(current_mtime);
202 let current_mtime = Timestamp::from(current_mtime);
203 if current_mtime == *cached_mtime {
203 if current_mtime == cached_mtime {
204 // The mtime of that directory has not changed
204 // The mtime of that directory has not changed
205 // since then, which means that the results of
205 // since then, which means that the results of
206 // `read_dir` should also be unchanged.
206 // `read_dir` should also be unchanged.
@@ -222,7 +222,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
222 directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
222 directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
223 directory_fs_path: &Path,
223 directory_fs_path: &Path,
224 directory_metadata: Option<&std::fs::Metadata>,
224 directory_metadata: Option<&std::fs::Metadata>,
225 cached_directory_mtime: Option<&Timestamp>,
225 cached_directory_mtime: Option<Timestamp>,
226 is_at_repo_root: bool,
226 is_at_repo_root: bool,
227 ) -> Result<bool, DirstateV2ParseError> {
227 ) -> Result<bool, DirstateV2ParseError> {
228 if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime)
228 if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime)
@@ -468,7 +468,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'"
468 // unlikely enough in practice.
468 // unlikely enough in practice.
469 let timestamp = directory_mtime.into();
469 let timestamp = directory_mtime.into();
470 let cached = dirstate_node.cached_directory_mtime();
470 let cached = dirstate_node.cached_directory_mtime();
471 if cached != Some(&timestamp) {
471 if cached != Some(timestamp) {
472 let hg_path = dirstate_node
472 let hg_path = dirstate_node
473 .full_path_borrowed(self.dmap.on_disk)?
473 .full_path_borrowed(self.dmap.on_disk)?
474 .detach_from_tree();
474 .detach_from_tree();
General Comments 0
You need to be logged in to leave comments. Login now