This instance will be upgraded to Heptapod 0.31.0 on 2022-05-19 at 11:00 UTC+2 (a few minutes of down time)

Commit d2f760c2 authored by Simon Sapin's avatar Simon Sapin
Browse files

dirstate-v2: Separate Rust structs for Timestamp and PackedTimestamp

PackedTimestamp is now exclusively for dirstate-v2 serialization purpose.
It contains unaligned big-endian integers. Timestamp is used everywhere else
and contains native Rust integers.

Differential Revision: https://phab.mercurial-scm.org/D11632
parent a5a673ec8f6f
use crate::errors::HgError;
use bitflags::bitflags;
use std::convert::TryFrom;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EntryState {
......@@ -28,6 +29,73 @@
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct Timestamp {
seconds: i64,
/// In `0 .. 1_000_000_000`.
///
/// This timestamp is after `(seconds, 0)` by this many nanoseconds.
nanoseconds: u32,
}
impl Timestamp {
pub fn new(seconds: i64, nanoseconds: u32) -> Self {
Self {
seconds,
nanoseconds,
}
}
pub fn seconds(&self) -> i64 {
self.seconds
}
pub fn nanoseconds(&self) -> u32 {
self.nanoseconds
}
}
impl From<SystemTime> for Timestamp {
fn from(system_time: SystemTime) -> Self {
// On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
// We want to effectively access its fields, but the Rust standard
// library does not expose them. The best we can do is:
let seconds;
let nanoseconds;
match system_time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
seconds = duration.as_secs() as i64;
nanoseconds = duration.subsec_nanos();
}
Err(error) => {
// `system_time` is before `UNIX_EPOCH`.
// We need to undo this algorithm:
// https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
let negative = error.duration();
let negative_secs = negative.as_secs() as i64;
let negative_nanos = negative.subsec_nanos();
if negative_nanos == 0 {
seconds = -negative_secs;
nanoseconds = 0;
} else {
// For example if `system_time` was 4.3 seconds before
// the Unix epoch we get a Duration that represents
// `(-4, -0.3)` but we want `(-5, +0.7)`:
const NSEC_PER_SEC: u32 = 1_000_000_000;
seconds = -1 - negative_secs;
nanoseconds = NSEC_PER_SEC - negative_nanos;
}
}
};
Self {
seconds,
nanoseconds,
}
}
}
pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
pub const MTIME_UNSET: i32 = -1;
......
......@@ -330,5 +330,5 @@
pub(super) fn cached_directory_mtime(
&self,
) -> Option<&'tree on_disk::Timestamp> {
) -> Option<crate::dirstate::Timestamp> {
match self {
......@@ -334,5 +334,5 @@
match self {
NodeRef::InMemory(_path, node) => match &node.data {
NodeRef::InMemory(_path, node) => match node.data {
NodeData::CachedDirectory { mtime } => Some(mtime),
_ => None,
},
......@@ -376,7 +376,7 @@
pub(super) enum NodeData {
Entry(DirstateEntry),
CachedDirectory { mtime: on_disk::Timestamp },
CachedDirectory { mtime: crate::dirstate::Timestamp },
None,
}
......
......@@ -2,6 +2,7 @@
//!
//! See `mercurial/helptext/internals/dirstate-v2.txt`
use crate::dirstate::Timestamp;
use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef};
use crate::dirstate_tree::path_with_basename::WithBasename;
use crate::errors::HgError;
......@@ -15,7 +16,6 @@
use format_bytes::format_bytes;
use std::borrow::Cow;
use std::convert::{TryFrom, TryInto};
use std::time::{SystemTime, UNIX_EPOCH};
/// Added at the start of `.hg/dirstate` when the "v2" format is used.
/// This a redundant sanity check more than an actual "magic number" since
......@@ -119,5 +119,5 @@
}
/// Duration since the Unix epoch
#[derive(BytesCast, Copy, Clone, PartialEq)]
#[derive(BytesCast, Copy, Clone)]
#[repr(C)]
......@@ -123,5 +123,5 @@
#[repr(C)]
pub(super) struct Timestamp {
struct PackedTimestamp {
seconds: I64Be,
/// In `0 .. 1_000_000_000`.
......@@ -316,10 +316,10 @@
) -> Result<dirstate_map::NodeData, DirstateV2ParseError> {
if self.has_entry() {
Ok(dirstate_map::NodeData::Entry(self.assume_entry()))
} else if let Some(&mtime) = self.cached_directory_mtime() {
} else if let Some(mtime) = self.cached_directory_mtime() {
Ok(dirstate_map::NodeData::CachedDirectory { mtime })
} else {
Ok(dirstate_map::NodeData::None)
}
}
......@@ -320,10 +320,10 @@
Ok(dirstate_map::NodeData::CachedDirectory { mtime })
} else {
Ok(dirstate_map::NodeData::None)
}
}
pub(super) fn cached_directory_mtime(&self) -> Option<&Timestamp> {
pub(super) fn cached_directory_mtime(&self) -> Option<Timestamp> {
if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
Some(self.data.as_timestamp())
} else {
......@@ -423,8 +423,12 @@
}
fn from_timestamp(timestamp: Timestamp) -> Self {
let packed = PackedTimestamp {
seconds: timestamp.seconds().into(),
nanoseconds: timestamp.nanoseconds().into(),
};
// Safety: both types implement the `ByteCast` trait, so we could
// safely use `as_bytes` and `from_bytes` to do this conversion. Using
// `transmute` instead makes the compiler check that the two types
// have the same size, which eliminates the error case of
// `from_bytes`.
......@@ -426,8 +430,8 @@
// Safety: both types implement the `ByteCast` trait, so we could
// safely use `as_bytes` and `from_bytes` to do this conversion. Using
// `transmute` instead makes the compiler check that the two types
// have the same size, which eliminates the error case of
// `from_bytes`.
unsafe { std::mem::transmute::<Timestamp, Entry>(timestamp) }
unsafe { std::mem::transmute::<PackedTimestamp, Entry>(packed) }
}
......@@ -432,4 +436,4 @@
}
fn as_timestamp(&self) -> &Timestamp {
fn as_timestamp(self) -> Timestamp {
// Safety: same as above in `from_timestamp`
......@@ -435,46 +439,7 @@
// Safety: same as above in `from_timestamp`
unsafe { &*(self as *const Entry as *const Timestamp) }
}
}
impl Timestamp {
pub fn seconds(&self) -> i64 {
self.seconds.get()
}
}
impl From<SystemTime> for Timestamp {
fn from(system_time: SystemTime) -> Self {
// On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
// https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
// We want to effectively access its fields, but the Rust standard
// library does not expose them. The best we can do is:
let (secs, nanos) = match system_time.duration_since(UNIX_EPOCH) {
Ok(duration) => {
(duration.as_secs() as i64, duration.subsec_nanos())
}
Err(error) => {
// `system_time` is before `UNIX_EPOCH`.
// We need to undo this algorithm:
// https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
let negative = error.duration();
let negative_secs = negative.as_secs() as i64;
let negative_nanos = negative.subsec_nanos();
if negative_nanos == 0 {
(-negative_secs, 0)
} else {
// For example if `system_time` was 4.3 seconds before
// the Unix epoch we get a Duration that represents
// `(-4, -0.3)` but we want `(-5, +0.7)`:
const NSEC_PER_SEC: u32 = 1_000_000_000;
(-1 - negative_secs, NSEC_PER_SEC - negative_nanos)
}
}
};
Timestamp {
seconds: secs.into(),
nanoseconds: nanos.into(),
}
let packed =
unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) };
Timestamp::new(packed.seconds.get(), packed.nanoseconds.get())
}
}
......
use crate::dirstate::entry::Timestamp;
use crate::dirstate::status::IgnoreFnType;
use crate::dirstate_tree::dirstate_map::BorrowedPath;
use crate::dirstate_tree::dirstate_map::ChildNodesRef;
......@@ -5,7 +6,6 @@
use crate::dirstate_tree::dirstate_map::NodeData;
use crate::dirstate_tree::dirstate_map::NodeRef;
use crate::dirstate_tree::on_disk::DirstateV2ParseError;
use crate::dirstate_tree::on_disk::Timestamp;
use crate::matchers::get_ignore_function;
use crate::matchers::Matcher;
use crate::utils::files::get_bytes_from_os_string;
......@@ -182,7 +182,7 @@
fn can_skip_fs_readdir(
&self,
directory_metadata: Option<&std::fs::Metadata>,
cached_directory_mtime: Option<&Timestamp>,
cached_directory_mtime: Option<Timestamp>,
) -> bool {
if !self.options.list_unknown && !self.options.list_ignored {
// All states that we care about listing have corresponding
......@@ -200,7 +200,7 @@
if let Some(meta) = directory_metadata {
if let Ok(current_mtime) = meta.modified() {
let current_mtime = Timestamp::from(current_mtime);
if current_mtime == *cached_mtime {
if current_mtime == cached_mtime {
// The mtime of that directory has not changed
// since then, which means that the results of
// `read_dir` should also be unchanged.
......@@ -222,7 +222,7 @@
directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
directory_fs_path: &Path,
directory_metadata: Option<&std::fs::Metadata>,
cached_directory_mtime: Option<&Timestamp>,
cached_directory_mtime: Option<Timestamp>,
is_at_repo_root: bool,
) -> Result<bool, DirstateV2ParseError> {
if self.can_skip_fs_readdir(directory_metadata, cached_directory_mtime)
......@@ -468,7 +468,7 @@
// unlikely enough in practice.
let timestamp = directory_mtime.into();
let cached = dirstate_node.cached_directory_mtime();
if cached != Some(&timestamp) {
if cached != Some(timestamp) {
let hg_path = dirstate_node
.full_path_borrowed(self.dmap.on_disk)?
.detach_from_tree();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment