1use std::{mem::size_of, path::Path};
2
3use crate::index::{self, Version, FAN_LEN, V2_SIGNATURE};
4
5#[derive(thiserror::Error, Debug)]
7#[allow(missing_docs)]
8pub enum Error {
9 #[error("Could not open pack index file at '{path}'")]
10 Io {
11 source: std::io::Error,
12 path: std::path::PathBuf,
13 },
14 #[error("{message}")]
15 Corrupt { message: String },
16 #[error("Unsupported index version: {version})")]
17 UnsupportedVersion { version: u32 },
18}
19
20const N32_SIZE: usize = size_of::<u32>();
21
22impl index::File {
24 pub fn at(path: impl AsRef<Path>, object_hash: gix_hash::Kind) -> Result<index::File, Error> {
29 Self::at_inner(path.as_ref(), object_hash)
30 }
31
32 fn at_inner(path: &Path, object_hash: gix_hash::Kind) -> Result<index::File, Error> {
33 let data = crate::mmap::read_only(path).map_err(|source| Error::Io {
34 source,
35 path: path.to_owned(),
36 })?;
37 let idx_len = data.len();
38 let hash_len = object_hash.len_in_bytes();
39
40 let footer_size = hash_len * 2;
41 if idx_len < FAN_LEN * N32_SIZE + footer_size {
42 return Err(Error::Corrupt {
43 message: format!("Pack index of size {idx_len} is too small for even an empty index"),
44 });
45 }
46 let (kind, fan, num_objects) = {
47 let (kind, d) = {
48 let (sig, d) = data.split_at(V2_SIGNATURE.len());
49 if sig == V2_SIGNATURE {
50 (Version::V2, d)
51 } else {
52 (Version::V1, &data[..])
53 }
54 };
55 let d = {
56 if let Version::V2 = kind {
57 let (vd, dr) = d.split_at(N32_SIZE);
58 let version = crate::read_u32(vd);
59 if version != Version::V2 as u32 {
60 return Err(Error::UnsupportedVersion { version });
61 }
62 dr
63 } else {
64 d
65 }
66 };
67 let (fan, bytes_read) = read_fan(d);
68 let (_, _d) = d.split_at(bytes_read);
69 let num_objects = fan[FAN_LEN - 1];
70
71 (kind, fan, num_objects)
72 };
73 Ok(index::File {
74 data,
75 path: path.to_owned(),
76 version: kind,
77 num_objects,
78 fan,
79 hash_len,
80 object_hash,
81 })
82 }
83}
84
85fn read_fan(d: &[u8]) -> ([u32; FAN_LEN], usize) {
86 assert!(d.len() >= FAN_LEN * N32_SIZE);
87
88 let mut fan = [0; FAN_LEN];
89 for (c, f) in d.chunks_exact(N32_SIZE).zip(fan.iter_mut()) {
90 *f = crate::read_u32(c);
91 }
92 (fan, FAN_LEN * N32_SIZE)
93}