Expand description
Parses the first 12 bytes of a pack file, returning the pack version as well as the number of objects contained in the pack.
Examples found in repository?
src/data/file/init.rs (line 31)
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
fn at_inner(path: &Path, object_hash: git_hash::Kind) -> Result<data::File, data::header::decode::Error> {
use crate::data::header::N32_SIZE;
let hash_len = object_hash.len_in_bytes();
let data = crate::mmap::read_only(path).map_err(|e| data::header::decode::Error::Io {
source: e,
path: path.to_owned(),
})?;
let pack_len = data.len();
if pack_len < N32_SIZE * 3 + hash_len {
return Err(data::header::decode::Error::Corrupt(format!(
"Pack data of size {} is too small for even an empty pack with shortest hash",
pack_len
)));
}
let (kind, num_objects) =
data::header::decode(&data[..12].try_into().expect("enough data after previous check"))?;
Ok(data::File {
data,
path: path.to_owned(),
id: git_features::hash::crc32(path.as_os_str().to_string_lossy().as_bytes()),
version: kind,
num_objects,
hash_len,
object_hash,
})
}More examples
src/data/input/bytes_to_entries.rs (line 61)
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
pub fn new_from_header(
mut read: BR,
mode: input::Mode,
compressed: input::EntryDataMode,
object_hash: git_hash::Kind,
) -> Result<BytesToEntriesIter<BR>, input::Error> {
let mut header_data = [0u8; 12];
read.read_exact(&mut header_data)?;
let (version, num_objects) = crate::data::header::decode(&header_data)?;
assert_eq!(
version,
crate::data::Version::V2,
"let's stop here if we see undocumented pack formats"
);
Ok(BytesToEntriesIter {
read,
decompressor: None,
compressed,
offset: 12,
had_error: false,
version,
objects_left: num_objects,
hash: (mode != input::Mode::AsIs).then(|| {
let mut hash = git_features::hash::hasher(object_hash);
hash.update(&header_data);
hash
}),
mode,
compressed_buf: None,
hash_len: object_hash.len_in_bytes(),
object_hash,
})
}src/cache/delta/from_offsets.rs (line 76)
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
pub fn from_offsets_in_pack(
pack_path: impl AsRef<std::path::Path>,
data_sorted_by_offsets: impl Iterator<Item = T>,
get_pack_offset: impl Fn(&T) -> data::Offset,
resolve_in_pack_id: impl Fn(&git_hash::oid) -> Option<data::Offset>,
mut progress: impl Progress,
should_interrupt: &AtomicBool,
object_hash: git_hash::Kind,
) -> Result<Self, Error> {
let mut r = io::BufReader::with_capacity(
8192 * 8, // this value directly corresponds to performance, 8k (default) is about 4x slower than 64k
fs::File::open(pack_path).map_err(|err| Error::Io {
source: err,
message: "open pack path",
})?,
);
let anticpiated_num_objects = if let Some(num_objects) = data_sorted_by_offsets.size_hint().1 {
progress.init(Some(num_objects), progress::count("objects"));
num_objects
} else {
0
};
let mut tree = Tree::with_capacity(anticpiated_num_objects)?;
{
// safety check - assure ourselves it's a pack we can handle
let mut buf = [0u8; PACK_HEADER_LEN];
r.read_exact(&mut buf).map_err(|err| Error::Io {
source: err,
message: "reading header buffer with at least 12 bytes failed - pack file truncated?",
})?;
crate::data::header::decode(&buf)?;
}
let then = Instant::now();
let mut previous_cursor_position = None::<u64>;
let hash_len = object_hash.len_in_bytes();
for (idx, data) in data_sorted_by_offsets.enumerate() {
let pack_offset = get_pack_offset(&data);
if let Some(previous_offset) = previous_cursor_position {
Self::advance_cursor_to_pack_offset(&mut r, pack_offset, previous_offset)?;
};
let entry = crate::data::Entry::from_read(&mut r, pack_offset, hash_len).map_err(|err| Error::Io {
source: err,
message: "EOF while parsing header",
})?;
previous_cursor_position = Some(pack_offset + entry.header_size() as u64);
use crate::data::entry::Header::*;
match entry.header {
Tree | Blob | Commit | Tag => {
tree.add_root(pack_offset, data)?;
}
RefDelta { base_id } => {
resolve_in_pack_id(base_id.as_ref())
.ok_or(Error::UnresolvedRefDelta { id: base_id })
.and_then(|base_pack_offset| {
tree.add_child(base_pack_offset, pack_offset, data).map_err(Into::into)
})?;
}
OfsDelta { base_distance } => {
let base_pack_offset = pack_offset
.checked_sub(base_distance)
.expect("in bound distance for deltas");
tree.add_child(base_pack_offset, pack_offset, data)?;
}
};
progress.inc();
if idx % 10_000 == 0 && should_interrupt.load(Ordering::SeqCst) {
return Err(Error::Interrupted);
}
}
progress.show_throughput(then);
Ok(tree)
}