rust/hg-core/src/dirstate_tree/on_disk.rs
changeset 48192 d2f760c2c91c
parent 48191 a5a673ec8f6f
child 48193 320de901896a
equal deleted inserted replaced
48191:a5a673ec8f6f 48192:d2f760c2c91c
     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],