summaryrefslogtreecommitdiffstats
path: root/vendor/gix-index/src/extension/untracked_cache.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-index/src/extension/untracked_cache.rs')
-rw-r--r--vendor/gix-index/src/extension/untracked_cache.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/vendor/gix-index/src/extension/untracked_cache.rs b/vendor/gix-index/src/extension/untracked_cache.rs
new file mode 100644
index 000000000..9f72e0775
--- /dev/null
+++ b/vendor/gix-index/src/extension/untracked_cache.rs
@@ -0,0 +1,156 @@
+use std::convert::TryInto;
+
+use bstr::BString;
+use gix_hash::ObjectId;
+
+use crate::{
+ entry,
+ extension::{Signature, UntrackedCache},
+ util::{read_u32, split_at_byte_exclusive, split_at_pos, var_int},
+};
+
+/// A structure to track filesystem stat information along with an object id, linking a worktree file with what's in our ODB.
+#[derive(Clone)]
+pub struct OidStat {
+ /// The file system stat information
+ pub stat: entry::Stat,
+ /// The id of the file in our ODB.
+ pub id: ObjectId,
+}
+
+/// A directory with information about its untracked files, and its sub-directories
+#[derive(Clone)]
+pub struct Directory {
+ /// The directories name, or an empty string if this is the root directory.
+ pub name: BString,
+ /// Untracked files and directory names
+ pub untracked_entries: Vec<BString>,
+ /// indices for sub-directories similar to this one.
+ pub sub_directories: Vec<usize>,
+
+ /// The directories stat data, if available or valid // TODO: or is it the exclude file?
+ pub stat: Option<entry::Stat>,
+ /// The oid of a .gitignore file, if it exists
+ pub exclude_file_oid: Option<ObjectId>,
+ /// TODO: figure out what this really does
+ pub check_only: bool,
+}
+
+/// Only used as an indicator
+pub const SIGNATURE: Signature = *b"UNTR";
+
+// #[allow(unused)]
+/// Decode an untracked cache extension from `data`, assuming object hashes are of type `object_hash`.
+pub fn decode(data: &[u8], object_hash: gix_hash::Kind) -> Option<UntrackedCache> {
+ if !data.last().map(|b| *b == 0).unwrap_or(false) {
+ return None;
+ }
+ let (identifier_len, data) = var_int(data)?;
+ let (identifier, data) = split_at_pos(data, identifier_len.try_into().ok()?)?;
+
+ let hash_len = object_hash.len_in_bytes();
+ let (info_exclude, data) = decode_oid_stat(data, hash_len)?;
+ let (excludes_file, data) = decode_oid_stat(data, hash_len)?;
+ let (dir_flags, data) = read_u32(data)?;
+ let (exclude_filename_per_dir, data) = split_at_byte_exclusive(data, 0)?;
+
+ let (num_directory_blocks, data) = var_int(data)?;
+
+ let mut res = UntrackedCache {
+ identifier: identifier.into(),
+ info_exclude: (!info_exclude.id.is_null()).then_some(info_exclude),
+ excludes_file: (!excludes_file.id.is_null()).then_some(excludes_file),
+ exclude_filename_per_dir: exclude_filename_per_dir.into(),
+ dir_flags,
+ directories: Vec::new(),
+ };
+ if num_directory_blocks == 0 {
+ return data.is_empty().then_some(res);
+ }
+
+ let num_directory_blocks = num_directory_blocks.try_into().ok()?;
+ let directories = &mut res.directories;
+ directories.reserve(num_directory_blocks);
+
+ let data = decode_directory_block(data, directories)?;
+ if directories.len() != num_directory_blocks {
+ return None;
+ }
+ let (valid, data) = gix_bitmap::ewah::decode(data).ok()?;
+ let (check_only, data) = gix_bitmap::ewah::decode(data).ok()?;
+ let (hash_valid, mut data) = gix_bitmap::ewah::decode(data).ok()?;
+
+ if valid.num_bits() > num_directory_blocks
+ || check_only.num_bits() > num_directory_blocks
+ || hash_valid.num_bits() > num_directory_blocks
+ {
+ return None;
+ }
+
+ check_only.for_each_set_bit(|index| {
+ directories[index].check_only = true;
+ Some(())
+ })?;
+ valid.for_each_set_bit(|index| {
+ let (stat, rest) = crate::decode::stat(data)?;
+ directories[index].stat = stat.into();
+ data = rest;
+ Some(())
+ });
+ hash_valid.for_each_set_bit(|index| {
+ let (hash, rest) = split_at_pos(data, hash_len)?;
+ data = rest;
+ directories[index].exclude_file_oid = ObjectId::from(hash).into();
+ Some(())
+ });
+
+ // null-byte checked in the beginning
+ if data.len() != 1 {
+ return None;
+ }
+ res.into()
+}
+
+fn decode_directory_block<'a>(data: &'a [u8], directories: &mut Vec<Directory>) -> Option<&'a [u8]> {
+ let (num_untracked, data) = var_int(data)?;
+ let (num_dirs, data) = var_int(data)?;
+ let (name, mut data) = split_at_byte_exclusive(data, 0)?;
+ let mut untracked_entries = Vec::<BString>::with_capacity(num_untracked.try_into().ok()?);
+ for _ in 0..num_untracked {
+ let (name, rest) = split_at_byte_exclusive(data, 0)?;
+ data = rest;
+ untracked_entries.push(name.into());
+ }
+
+ let index = directories.len();
+ directories.push(Directory {
+ name: name.into(),
+ untracked_entries,
+ sub_directories: Vec::with_capacity(num_dirs.try_into().ok()?),
+ // the following are set later through their bitmaps
+ stat: None,
+ exclude_file_oid: None,
+ check_only: false,
+ });
+
+ for _ in 0..num_dirs {
+ let subdir_index = directories.len();
+ let rest = decode_directory_block(data, directories)?;
+ data = rest;
+ directories[index].sub_directories.push(subdir_index);
+ }
+
+ data.into()
+}
+
+fn decode_oid_stat(data: &[u8], hash_len: usize) -> Option<(OidStat, &[u8])> {
+ let (stat, data) = crate::decode::stat(data)?;
+ let (hash, data) = split_at_pos(data, hash_len)?;
+ Some((
+ OidStat {
+ stat,
+ id: ObjectId::from(hash),
+ },
+ data,
+ ))
+}