Mercurial > hg
diff rust/hg-core/src/dirstate_tree/on_disk.rs @ 48219:308d9c245337
dirstate-v2: Add storage space for nanoseconds precision in file mtimes
For now the sub-second component is always set to zero for tracked files and
symlinks. (The mtime of directories for the `readdir`-skipping optimization
is a different code path and already uses the full precision available.)
This extra storage uses the space previously freed by replacing the 32-bit
`mode` field by two bits in the existing `flags` field, so the overall size
of nodes is unchanged. (This space had been left as padding for this purpose.)
Also move things around in the node layout and documentation to have less
duplication. Now that they have the same representation, directory mtime and
file mtime are kept in the same field. (Only either one can exist for a given
node.)
Differential Revision: https://phab.mercurial-scm.org/D11655
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 13 Oct 2021 17:32:52 +0200 |
parents | 47fabca85457 |
children | a32a96079e2d |
line wrap: on
line diff
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs Wed Oct 13 16:21:39 2021 +0200 +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs Wed Oct 13 17:32:52 2021 +0200 @@ -97,7 +97,8 @@ pub(super) descendants_with_entry_count: Size, pub(super) tracked_descendants_count: Size, flags: Flags, - data: Entry, + size: U32Be, + mtime: PackedTruncatedTimestamp, } bitflags! { @@ -110,23 +111,14 @@ const HAS_MODE_AND_SIZE = 1 << 3; const HAS_MTIME = 1 << 4; const MODE_EXEC_PERM = 1 << 5; - const MODE_IS_SYMLINK = 1 << 7; + const MODE_IS_SYMLINK = 1 << 6; } } -#[derive(BytesCast, Copy, Clone, Debug)] -#[repr(C)] -struct Entry { - _padding: U32Be, - size: U32Be, - mtime: U32Be, -} - /// Duration since the Unix epoch #[derive(BytesCast, Copy, Clone)] #[repr(C)] -struct PackedTimestamp { - _padding: U32Be, +struct PackedTruncatedTimestamp { truncated_seconds: U32Be, nanoseconds: U32Be, } @@ -329,7 +321,7 @@ ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> { Ok( if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() { - Some(self.data.as_timestamp()?) + Some(self.mtime.try_into()?) } else { None }, @@ -356,12 +348,12 @@ let p1_tracked = self.flags.contains(Flags::P1_TRACKED); let p2_info = self.flags.contains(Flags::P2_INFO); let mode_size = if self.flags.contains(Flags::HAS_MODE_AND_SIZE) { - Some((self.synthesize_unix_mode(), self.data.size.into())) + Some((self.synthesize_unix_mode(), self.size.into())) } else { None }; let mtime = if self.flags.contains(Flags::HAS_MTIME) { - Some(self.data.mtime.into()) + Some(self.mtime.truncated_seconds.into()) } else { None }; @@ -407,10 +399,10 @@ tracked_descendants_count: self.tracked_descendants_count.get(), }) } -} -impl Entry { - fn from_dirstate_entry(entry: &DirstateEntry) -> (Flags, Self) { + fn from_dirstate_entry( + entry: &DirstateEntry, + ) -> (Flags, U32Be, PackedTruncatedTimestamp) { let (wdir_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt) = entry.v2_data(); // TODO: convert throug raw flag bits instead? @@ -418,53 +410,26 @@ flags.set(Flags::WDIR_TRACKED, wdir_tracked); flags.set(Flags::P1_TRACKED, p1_tracked); flags.set(Flags::P2_INFO, p2_info); - let (size, mtime); - if let Some((m, s)) = mode_size_opt { + let size = if let Some((m, s)) = mode_size_opt { let exec_perm = m & libc::S_IXUSR != 0; let is_symlink = m & libc::S_IFMT == libc::S_IFLNK; flags.set(Flags::MODE_EXEC_PERM, exec_perm); flags.set(Flags::MODE_IS_SYMLINK, is_symlink); - size = s; - flags.insert(Flags::HAS_MODE_AND_SIZE) + flags.insert(Flags::HAS_MODE_AND_SIZE); + s.into() } else { - size = 0; - } - if let Some(m) = mtime_opt { - mtime = m; - flags.insert(Flags::HAS_MTIME); - } else { - mtime = 0; - } - let raw_entry = Entry { - _padding: 0.into(), - size: size.into(), - mtime: mtime.into(), + 0.into() }; - (flags, raw_entry) - } - - fn from_timestamp(timestamp: TruncatedTimestamp) -> Self { - let packed = PackedTimestamp { - _padding: 0.into(), - truncated_seconds: timestamp.truncated_seconds().into(), - nanoseconds: timestamp.nanoseconds().into(), + let mtime = if let Some(m) = mtime_opt { + flags.insert(Flags::HAS_MTIME); + PackedTruncatedTimestamp { + truncated_seconds: m.into(), + nanoseconds: 0.into(), + } + } else { + PackedTruncatedTimestamp::null() }; - // 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::<PackedTimestamp, Entry>(packed) } - } - - fn as_timestamp(self) -> Result<TruncatedTimestamp, DirstateV2ParseError> { - // Safety: same as above in `from_timestamp` - let packed = - unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) }; - TruncatedTimestamp::from_already_truncated( - packed.truncated_seconds.get(), - packed.nanoseconds.get(), - ) + (flags, size, mtime) } } @@ -610,20 +575,17 @@ }; on_disk_nodes.push(match node { NodeRef::InMemory(path, node) => { - let (flags, data) = match &node.data { + let (flags, size, mtime) = match &node.data { dirstate_map::NodeData::Entry(entry) => { - Entry::from_dirstate_entry(entry) + Node::from_dirstate_entry(entry) } dirstate_map::NodeData::CachedDirectory { mtime } => { - (Flags::HAS_MTIME, Entry::from_timestamp(*mtime)) + (Flags::HAS_MTIME, 0.into(), (*mtime).into()) } dirstate_map::NodeData::None => ( Flags::empty(), - Entry { - _padding: 0.into(), - size: 0.into(), - mtime: 0.into(), - }, + 0.into(), + PackedTruncatedTimestamp::null(), ), }; Node { @@ -641,7 +603,8 @@ .tracked_descendants_count .into(), flags, - data, + size, + mtime, } } NodeRef::OnDisk(node) => Node { @@ -725,3 +688,33 @@ .expect("dirstate-v2 path length overflow") .into() } + +impl From<TruncatedTimestamp> for PackedTruncatedTimestamp { + fn from(timestamp: TruncatedTimestamp) -> Self { + Self { + truncated_seconds: timestamp.truncated_seconds().into(), + nanoseconds: timestamp.nanoseconds().into(), + } + } +} + +impl TryFrom<PackedTruncatedTimestamp> for TruncatedTimestamp { + type Error = DirstateV2ParseError; + + fn try_from( + timestamp: PackedTruncatedTimestamp, + ) -> Result<Self, Self::Error> { + Self::from_already_truncated( + timestamp.truncated_seconds.get(), + timestamp.nanoseconds.get(), + ) + } +} +impl PackedTruncatedTimestamp { + fn null() -> Self { + Self { + truncated_seconds: 0.into(), + nanoseconds: 0.into(), + } + } +}