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/extension/untracked_cache.rs | |
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/extension/untracked_cache.rs')
-rw-r--r-- | vendor/gix-index/src/extension/untracked_cache.rs | 156 |
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, + )) +} |