diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-index/src/entry | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-index/src/entry')
-rw-r--r-- | vendor/gix-index/src/entry/flags.rs | 130 | ||||
-rw-r--r-- | vendor/gix-index/src/entry/mod.rs | 109 | ||||
-rw-r--r-- | vendor/gix-index/src/entry/mode.rs | 24 | ||||
-rw-r--r-- | vendor/gix-index/src/entry/write.rs | 39 |
4 files changed, 302 insertions, 0 deletions
diff --git a/vendor/gix-index/src/entry/flags.rs b/vendor/gix-index/src/entry/flags.rs new file mode 100644 index 000000000..ec00a78db --- /dev/null +++ b/vendor/gix-index/src/entry/flags.rs @@ -0,0 +1,130 @@ +use bitflags::bitflags; + +use crate::entry::Stage; + +bitflags! { + /// In-memory flags + pub struct Flags: u32 { + /// The mask to apply to obtain the stage number of an entry. + const STAGE_MASK = 0x3000; + /// If set, additional bits need to be written to storage. + const EXTENDED = 0x4000; + // TODO: could we use the pathlen ourselves to save 8 bytes? And how to handle longer paths than that? 0 as sentinel maybe? + /// The mask to obtain the length of the path associated with this entry. + const PATH_LEN = 0x0fff; + /// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()` checks. + const ASSUME_VALID = 1 << 15; + /// Indicates that an entry needs to be updated as it's in-memory representation doesn't match what's on disk. + const UPDATE = 1 << 16; + /// Indicates an entry should be removed - this typically happens during writing, by simply skipping over them. + const REMOVE = 1 << 17; + /// Indicates that an entry is known to be uptodate. + const UPTODATE = 1 << 18; + /// Only temporarily used by unpack_trees() (in C) + const ADDED = 1 << 19; + + /// Whether an up-to-date object hash exists for the entry. + const HASHED = 1 << 20; + /// Set if the filesystem monitor is valid. + const FSMONITOR_VALID = 1 << 21; + /// Remove in work directory + const WORKTREE_REMOVE = 1 << 22; + /// Set to indicate the entry exists in multiple stages at once due to conflicts. + const CONFLICTED = 1 << 23; + + /// Indicates that the entry was already turned into a tree. + const UNPACKED = 1 << 24; + /// Only temporarily used by unpack_trees() (in C) + const NEW_SKIP_WORKTREE = 1 << 25; + + /// temporarily mark paths matched by a path spec + const PATHSPEC_MATCHED = 1 << 26; + + /// When the index is split, this indicates the entry is up-to-date in the shared portion of the index. + const UPDATE_IN_BASE = 1 << 27; + /// Indicates the entry name is present in the base/shared index, and thus doesn't have to be stored in this one. + const STRIP_NAME = 1 << 28; + + /// + /// stored at rest, see at_rest::FlagsExtended + const INTENT_TO_ADD = 1 << 29; + /// Stored at rest + const SKIP_WORKTREE = 1 << 30; + + /// For future extension + const EXTENDED_2 = 1 << 31; + } +} + +impl Flags { + /// Return the stage as extracted from the bits of this instance. + pub fn stage(&self) -> Stage { + (*self & Flags::STAGE_MASK).bits >> 12 + } + + /// Transform ourselves to a storage representation to keep all flags which are to be persisted, + /// skipping all extended flags. Note that the caller has to check for the `EXTENDED` bit to be present + /// and write extended flags as well if so. + pub fn to_storage(mut self) -> at_rest::Flags { + at_rest::Flags::from_bits( + { + self.remove(Self::PATH_LEN); + self + } + .bits() as u16, + ) + .unwrap() + } +} + +pub(crate) mod at_rest { + use bitflags::bitflags; + + bitflags! { + /// Flags how they are serialized to a storage location + pub struct Flags: u16 { + /// A portion of a the flags that encodes the length of the path that follows. + const PATH_LEN = 0x0fff; + const STAGE_MASK = 0x3000; + /// If set, there is more extended flags past this one + const EXTENDED = 0x4000; + /// If set, the entry be assumed to match with the version on the working tree, as a way to avoid `lstat()` checks. + const ASSUME_VALID = 0x8000; + } + } + + impl Flags { + pub fn to_memory(self) -> super::Flags { + super::Flags::from_bits(self.bits as u32).expect("PATHLEN is part of memory representation") + } + } + + bitflags! { + /// Extended flags - add flags for serialization here and offset them down to u16. + pub struct FlagsExtended: u16 { + const INTENT_TO_ADD = 1 << (29 - 16); + const SKIP_WORKTREE = 1 << (30 - 16); + } + } + + impl FlagsExtended { + pub fn from_flags(flags: super::Flags) -> Self { + Self::from_bits(((flags & (super::Flags::INTENT_TO_ADD | super::Flags::SKIP_WORKTREE)).bits >> 16) as u16) + .expect("valid") + } + pub fn to_flags(self) -> Option<super::Flags> { + super::Flags::from_bits((self.bits as u32) << 16) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn flags_from_bits_with_conflict() { + let input = 0b1110_0010_1000_1011; + assert_eq!(Flags::from_bits(input).unwrap().bits, input); + } + } +} diff --git a/vendor/gix-index/src/entry/mod.rs b/vendor/gix-index/src/entry/mod.rs new file mode 100644 index 000000000..165df801e --- /dev/null +++ b/vendor/gix-index/src/entry/mod.rs @@ -0,0 +1,109 @@ +/// The stage of an entry, one of 0 = base, 1 = ours, 2 = theirs +pub type Stage = u32; + +mod mode; +pub use mode::Mode; + +mod flags; +pub(crate) use flags::at_rest; +pub use flags::Flags; + +mod write; + +/// The time component in a [`Stat`] struct. +#[derive(Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct Time { + /// The amount of seconds elapsed since EPOCH + pub secs: u32, + /// The amount of nanoseconds elapsed in the current second, ranging from 0 to 999.999.999 . + pub nsecs: u32, +} + +/// An entry's filesystem stat information. +#[derive(Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub struct Stat { + /// Modification time + pub mtime: Time, + /// Creation time + pub ctime: Time, + /// Device number + pub dev: u32, + /// Inode number + pub ino: u32, + /// User id of the owner + pub uid: u32, + /// Group id of the owning group + pub gid: u32, + /// The size of bytes on disk. Capped to u32 so files bigger than that will need thorough additional checking + pub size: u32, +} + +mod access { + use bstr::{BStr, ByteSlice}; + + use crate::{entry, Entry, State}; + + impl Entry { + /// Return an entry's path, relative to the repository, which is extracted from its owning `state`. + pub fn path<'a>(&self, state: &'a State) -> &'a BStr { + state.path_backing[self.path.clone()].as_bstr() + } + + /// Return an entry's path using the given `backing`. + pub fn path_in<'backing>(&self, backing: &'backing crate::PathStorageRef) -> &'backing BStr { + backing[self.path.clone()].as_bstr() + } + + /// Return an entry's stage. + pub fn stage(&self) -> entry::Stage { + self.flags.stage() + } + } +} + +mod _impls { + use std::{cmp::Ordering, ops::Add, time::SystemTime}; + + use bstr::BStr; + + use crate::{entry::Time, Entry, State}; + + impl From<SystemTime> for Time { + fn from(s: SystemTime) -> Self { + let d = s + .duration_since(std::time::UNIX_EPOCH) + .expect("system time is not before unix epoch!"); + Time { + secs: d.as_secs() as u32, + nsecs: d.subsec_nanos(), + } + } + } + + impl From<Time> for SystemTime { + fn from(s: Time) -> Self { + std::time::UNIX_EPOCH.add(std::time::Duration::new(s.secs.into(), s.nsecs)) + } + } + + impl Entry { + /// Compare one entry to another by their path, by comparing only their common path portion byte by byte, then resorting to + /// entry length and stage. + pub fn cmp(&self, other: &Self, state: &State) -> Ordering { + let lhs = self.path(state); + let rhs = other.path(state); + Entry::cmp_filepaths(lhs, rhs).then_with(|| self.stage().cmp(&other.stage())) + } + + /// Compare one entry to another by their path, by comparing only their common path portion byte by byte, then resorting to + /// entry length. + pub fn cmp_filepaths(a: &BStr, b: &BStr) -> Ordering { + let common_len = a.len().min(b.len()); + a[..common_len] + .cmp(&b[..common_len]) + .then_with(|| a.len().cmp(&b.len())) + } + } +} diff --git a/vendor/gix-index/src/entry/mode.rs b/vendor/gix-index/src/entry/mode.rs new file mode 100644 index 000000000..1045dfd5b --- /dev/null +++ b/vendor/gix-index/src/entry/mode.rs @@ -0,0 +1,24 @@ +use bitflags::bitflags; +bitflags! { + /// The kind of file of an entry. + pub struct Mode: u32 { + /// directory (only used for sparse checkouts), equivalent to a tree, which is _excluded_ from the index via + /// cone-mode. + const DIR = 0o040000; + /// regular file + const FILE = 0o100644; + /// regular file, executable + const FILE_EXECUTABLE = 0o100755; + /// Symbolic link + const SYMLINK = 0o120000; + /// A git commit for submodules + const COMMIT = 0o160000; + } +} + +impl Mode { + /// Return true if this is a sparse entry, as it points to a directory which usually isn't what an unsparse index tracks. + pub fn is_sparse(&self) -> bool { + *self == Self::DIR + } +} diff --git a/vendor/gix-index/src/entry/write.rs b/vendor/gix-index/src/entry/write.rs new file mode 100644 index 000000000..2a6ad5588 --- /dev/null +++ b/vendor/gix-index/src/entry/write.rs @@ -0,0 +1,39 @@ +use std::convert::TryInto; + +use crate::{entry, Entry, State}; + +impl Entry { + /// Serialize ourselves to `out` with path access via `state`, without padding. + pub fn write_to(&self, mut out: impl std::io::Write, state: &State) -> std::io::Result<()> { + let stat = self.stat; + out.write_all(&stat.ctime.secs.to_be_bytes())?; + out.write_all(&stat.ctime.nsecs.to_be_bytes())?; + out.write_all(&stat.mtime.secs.to_be_bytes())?; + out.write_all(&stat.mtime.nsecs.to_be_bytes())?; + out.write_all(&stat.dev.to_be_bytes())?; + out.write_all(&stat.ino.to_be_bytes())?; + out.write_all(&self.mode.bits().to_be_bytes())?; + out.write_all(&stat.uid.to_be_bytes())?; + out.write_all(&stat.gid.to_be_bytes())?; + out.write_all(&stat.size.to_be_bytes())?; + out.write_all(self.id.as_bytes())?; + let path = self.path(state); + let path_len: u16 = if path.len() >= entry::Flags::PATH_LEN.bits() as usize { + entry::Flags::PATH_LEN.bits() as u16 + } else { + path.len() + .try_into() + .expect("we just checked that the length is smaller than 0xfff") + }; + out.write_all(&(self.flags.to_storage().bits() | path_len).to_be_bytes())?; + if self.flags.contains(entry::Flags::EXTENDED) { + out.write_all( + &entry::at_rest::FlagsExtended::from_flags(self.flags) + .bits() + .to_be_bytes(), + )?; + } + out.write_all(path)?; + out.write_all(b"\0") + } +} |