1 //! The "version 2" disk representation of the dirstate |
1 //! The "version 2" disk representation of the dirstate |
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; |
8 use crate::utils::hg_path::HgPath; |
9 use crate::utils::hg_path::HgPath; |
9 use crate::DirstateEntry; |
10 use crate::DirstateEntry; |
13 use bytes_cast::unaligned::{I32Be, I64Be, U16Be, U32Be}; |
14 use bytes_cast::unaligned::{I32Be, I64Be, U16Be, U32Be}; |
14 use bytes_cast::BytesCast; |
15 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 |
22 /// `.hg/requires` already governs which format should be used. |
22 /// `.hg/requires` already governs which format should be used. |
23 pub const V2_FORMAT_MARKER: &[u8; 12] = b"dirstate-v2\n"; |
23 pub const V2_FORMAT_MARKER: &[u8; 12] = b"dirstate-v2\n"; |
117 size: I32Be, |
117 size: I32Be, |
118 mtime: I32Be, |
118 mtime: I32Be, |
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`. |
128 /// |
128 /// |
129 /// This timestamp is after `(seconds, 0)` by this many nanoseconds. |
129 /// This timestamp is after `(seconds, 0)` by this many nanoseconds. |
314 pub(super) fn node_data( |
314 pub(super) fn node_data( |
315 &self, |
315 &self, |
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 { |
330 None |
330 None |
331 } |
331 } |
421 }; |
421 }; |
422 (flags, raw_entry) |
422 (flags, raw_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 |
481 fn read_hg_path( |
446 fn read_hg_path( |
482 on_disk: &[u8], |
447 on_disk: &[u8], |