This repository has no description
1use std::path::{Path, PathBuf};
2
3use knot_types::Oid;
4
5use crate::objects::{Haves, Wants};
6
7use super::reader;
8use super::revindex::{Order, OrderTable};
9use super::writer;
10use crate::error::GitError;
11use crate::repo::Repo;
12
13// hashtag easter egg
14const RIDX_SIGNATURE: u32 = 0x5249_4458;
15const MIDX_ALLOC_LIMIT_BYTES: usize = 16 * 1024 * 1024;
16
17fn midx_path(objects_dir: &Path) -> PathBuf {
18 objects_dir.join("pack").join("multi-pack-index")
19}
20
21fn sidecar(objects_dir: &Path, checksum: &gix_hash::ObjectId, ext: &str) -> PathBuf {
22 objects_dir
23 .join("pack")
24 .join(format!("multi-pack-index-{}.{ext}", checksum.to_hex()))
25}
26
27pub(super) fn write(repo: &Repo) -> Result<bool, GitError> {
28 let kind = repo.object_format().kind();
29 let objects_dir = repo.objects_dir();
30 let file = match gix_pack::multi_index::File::at(
31 midx_path(&objects_dir),
32 Some(MIDX_ALLOC_LIMIT_BYTES),
33 ) {
34 Ok(file) => file,
35 Err(_) => return Ok(false),
36 };
37 let order = OrderTable::from_file(&file);
38 if order.len() == 0 {
39 return Ok(false);
40 }
41
42 let _boost = knot_resource::saturate();
43 let types = writer::type_index_bits(repo, &order)?;
44 let selected = writer::selected_entries(repo, &order)?;
45 if selected.is_empty() {
46 return Ok(false);
47 }
48
49 let checksum = file.checksum();
50 let bytes = writer::assemble(kind, &checksum, &types, &selected)?;
51
52 write_rev(
53 &sidecar(&objects_dir, &checksum, "rev"),
54 &order,
55 kind,
56 &checksum,
57 )?;
58 writer::install(&sidecar(&objects_dir, &checksum, "bitmap"), &bytes)?;
59 Ok(true)
60}
61
62fn write_rev(
63 path: &Path,
64 order: &OrderTable,
65 kind: gix::hash::Kind,
66 checksum: &gix_hash::ObjectId,
67) -> Result<(), GitError> {
68 let hash_id: u32 = match kind {
69 gix::hash::Kind::Sha256 => 2,
70 _ => 1,
71 };
72 let mut out = Vec::new();
73 out.extend_from_slice(&RIDX_SIGNATURE.to_be_bytes());
74 out.extend_from_slice(&1u32.to_be_bytes());
75 out.extend_from_slice(&hash_id.to_be_bytes());
76 order
77 .index_positions_in_bit_order()
78 .iter()
79 .for_each(|position| out.extend_from_slice(&position.get().to_be_bytes()));
80 out.extend_from_slice(checksum.as_slice());
81 let mut hasher = gix_hash::hasher(kind);
82 hasher.update(&out);
83 let digest = hasher
84 .try_finalize()
85 .map_err(|error| GitError::Backend(format!("midx revindex checksum: {error}")))?;
86 out.extend_from_slice(digest.as_slice());
87 writer::install(path, &out)
88}
89
90pub(super) fn reachable(
91 repo: &Repo,
92 wants: Wants<'_>,
93 haves: Haves<'_>,
94) -> Result<Option<Vec<Oid>>, GitError> {
95 let kind = repo.object_format().kind();
96 let objects_dir = repo.objects_dir();
97 let file = match gix_pack::multi_index::File::at(
98 midx_path(&objects_dir),
99 Some(MIDX_ALLOC_LIMIT_BYTES),
100 ) {
101 Ok(file) => file,
102 Err(_) => return Ok(None),
103 };
104 let Ok(bytes) = std::fs::read(sidecar(&objects_dir, &file.checksum(), "bitmap")) else {
105 return Ok(None);
106 };
107 let order = OrderTable::from_file(&file);
108 let maps = reader::parse(&bytes, kind, order.len())?;
109 super::resolve(repo, &order, &maps, wants, haves)
110}