summaryrefslogtreecommitdiffstats
path: root/vendor/gix-index/src/entry
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-index/src/entry
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-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.rs130
-rw-r--r--vendor/gix-index/src/entry/mod.rs109
-rw-r--r--vendor/gix-index/src/entry/mode.rs24
-rw-r--r--vendor/gix-index/src/entry/write.rs39
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")
+ }
+}