Patchwork D11661: dirstate-v2: Extend node flags to 16 bits

login
register
mail settings
Submitter phabricator
Date Oct. 14, 2021, 2:07 p.m.
Message ID <differential-rev-PHID-DREV-ldmupso3ftl2rov6ftes-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/49985/
State Superseded
Headers show

Comments

phabricator - Oct. 14, 2021, 2:07 p.m.
SimonSapin created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  Only 7 out of 8 available bits are used right now. Reserve some more.
  
  Future versions of Mercurial may assign meaning to some of these bits,
  with the limitation that then-older versions will always reset those bits to
  unset when writing nodes.
  (A new node is written for any mutation in its subtree, leaving the bytes of
  the old node unreachable until the data file is rewritten entirely.)

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11661

AFFECTED FILES
  mercurial/dirstateutils/v2.py
  mercurial/helptext/internals/dirstate-v2.txt
  rust/hg-core/src/dirstate_tree/on_disk.rs

CHANGE DETAILS




To: SimonSapin, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs
@@ -33,7 +33,7 @@ 
 
 /// Must match constants of the same names in `mercurial/dirstateutils/v2.py`
 const TREE_METADATA_SIZE: usize = 44;
-const NODE_SIZE: usize = 43;
+const NODE_SIZE: usize = 44;
 
 /// Make sure that size-affecting changes are made knowingly
 #[allow(unused)]
@@ -94,15 +94,14 @@ 
     children: ChildNodes,
     pub(super) descendants_with_entry_count: Size,
     pub(super) tracked_descendants_count: Size,
-    flags: Flags,
+    flags: U16Be,
     size: U32Be,
     mtime: PackedTruncatedTimestamp,
 }
 
 bitflags! {
-    #[derive(BytesCast)]
     #[repr(C)]
-    struct Flags: u8 {
+    struct Flags: u16 {
         const WDIR_TRACKED = 1 << 0;
         const P1_TRACKED = 1 << 1;
         const P2_INFO = 1 << 2;
@@ -296,8 +295,12 @@ 
         })
     }
 
+    fn flags(&self) -> Flags {
+        Flags::from_bits_truncate(self.flags.get())
+    }
+
     fn has_entry(&self) -> bool {
-        self.flags.intersects(
+        self.flags().intersects(
             Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
         )
     }
@@ -318,7 +321,7 @@ 
         &self,
     ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
         Ok(
-            if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
+            if self.flags().contains(Flags::HAS_MTIME) && !self.has_entry() {
                 Some(self.mtime.try_into()?)
             } else {
                 None
@@ -327,12 +330,12 @@ 
     }
 
     fn synthesize_unix_mode(&self) -> u32 {
-        let file_type = if self.flags.contains(Flags::MODE_IS_SYMLINK) {
+        let file_type = if self.flags().contains(Flags::MODE_IS_SYMLINK) {
             libc::S_IFLNK
         } else {
             libc::S_IFREG
         };
-        let permisions = if self.flags.contains(Flags::MODE_EXEC_PERM) {
+        let permisions = if self.flags().contains(Flags::MODE_EXEC_PERM) {
             0o755
         } else {
             0o644
@@ -342,15 +345,15 @@ 
 
     fn assume_entry(&self) -> DirstateEntry {
         // TODO: convert through raw bits instead?
-        let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
-        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) {
+        let wdir_tracked = self.flags().contains(Flags::WDIR_TRACKED);
+        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.size.into()))
         } else {
             None
         };
-        let mtime = if self.flags.contains(Flags::HAS_MTIME) {
+        let mtime = if self.flags().contains(Flags::HAS_MTIME) {
             Some(self.mtime.truncated_seconds.into())
         } else {
             None
@@ -600,7 +603,7 @@ 
                         tracked_descendants_count: node
                             .tracked_descendants_count
                             .into(),
-                        flags,
+                        flags: flags.bits().into(),
                         size,
                         mtime,
                     }
diff --git a/mercurial/helptext/internals/dirstate-v2.txt b/mercurial/helptext/internals/dirstate-v2.txt
--- a/mercurial/helptext/internals/dirstate-v2.txt
+++ b/mercurial/helptext/internals/dirstate-v2.txt
@@ -372,7 +372,7 @@ 
   This counter is used to implement `has_tracked_dir`.
 
 * Offset 30:
-  A single `flags` byte that packs some boolean values as bits.
+  A `flags` fields  that packs some boolean values as bits of a 16-bit integer.
   Starting from least-significant, bit masks are::
 
     WDIR_TRACKED = 1 << 0
@@ -384,22 +384,29 @@ 
     MODE_IS_SYMLINK = 1 << 6
 
   The meaning of each bit is described below.
-  Other bits are unset.
 
-* Offset 31:
+  Other bits are unset.
+  They may be assigned meaning if the future,
+  with the limitation that Mercurial versions that pre-date such meaning
+  will always reset those bits to unset when writing nodes.
+  (A new node is written for any mutation in its subtree,
+  leaving the bytes of the old node unreachable
+  until the data file is rewritten entirely.)
+
+* Offset 32:
   A `size` field described below, as a 32-bit integer.
   Unlike in dirstate-v1, negative values are not used.
 
-* Offset 35:
+* Offset 36:
   The seconds component of an `mtime` field described below,
   as a 32-bit integer.
   Unlike in dirstate-v1, negative values are not used.
 
-* Offset 39:
+* Offset 40:
   The nanoseconds component of an `mtime` field described below,
   as a 32-bit integer.
 
-* (Offset 43: end of this node)
+* (Offset 44: end of this node)
 
 The meaning of the boolean values packed in `flags` is:
 
diff --git a/mercurial/dirstateutils/v2.py b/mercurial/dirstateutils/v2.py
--- a/mercurial/dirstateutils/v2.py
+++ b/mercurial/dirstateutils/v2.py
@@ -18,7 +18,7 @@ 
 # Must match the constant of the same name in
 # `rust/hg-core/src/dirstate_tree/on_disk.rs`
 TREE_METADATA_SIZE = 44
-NODE_SIZE = 43
+NODE_SIZE = 44
 
 
 # Must match the `TreeMetadata` Rust struct in
@@ -50,7 +50,7 @@ 
 # * 4 bytes: expected size
 # * 4 bytes: mtime seconds
 # * 4 bytes: mtime nanoseconds
-NODE = struct.Struct('>LHHLHLLLLBlll')
+NODE = struct.Struct('>LHHLHLLLLHlll')
 
 
 assert TREE_METADATA_SIZE == TREE_METADATA.size