use git_hash::ObjectId; use crate::{ extension::{Signature, Tree}, util::split_at_byte_exclusive, }; pub const SIGNATURE: Signature = *b"TREE"; pub struct NodeId { /// The id of the directory tree of the associated tree object. id: git_hash::ObjectId, /// The amount of non-tree entries contained within, and definitely not zero. entry_count: u32, } /// A recursive data structure pub fn decode(data: &[u8], object_hash: git_hash::Kind) -> Option { let (tree, data) = one_recursive(data, object_hash.len_in_bytes())?; assert!( data.is_empty(), "BUG: should fully consume the entire tree extension chunk, got {} left", data.len() ); Some(tree) } pub fn one_recursive(data: &[u8], hash_len: usize) -> Option<(Tree, &[u8])> { let (path, data) = split_at_byte_exclusive(data, 0)?; let (entry_count, data) = split_at_byte_exclusive(data, b' ')?; let entry_count: u32 = atoi::atoi(entry_count)?; let (subtree_count, mut data) = split_at_byte_exclusive(data, b'\n')?; let subtree_count: usize = atoi::atoi(subtree_count)?; let node_id = (entry_count != 0) .then(|| { (data.len() >= hash_len).then(|| { let (hash, rest) = data.split_at(hash_len); data = rest; ObjectId::from(hash) }) }) .flatten() .map(|id| NodeId { id, entry_count }); let mut subtrees = Vec::with_capacity(subtree_count); for _ in 0..subtree_count { let (tree, rest) = one_recursive(data, hash_len)?; subtrees.push(tree); data = rest; } Some(( Tree { id: node_id, name: path.into(), children: subtrees, }, data, )) } #[cfg(test)] mod tests { use super::*; #[test] fn size_of_tree() { assert_eq!(std::mem::size_of::(), 88); } }