tiger_pkg/manager/
lookup_cache.rs1use ahash::HashMap;
2use itertools::MultiUnzip;
3use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
4use tracing::{error, info};
5
6use super::{PackageManager, TagLookupIndex};
7use crate::{manager::HashTableEntryShort, package::Redaction, TagHash64, Version};
8
9impl PackageManager {
10 const LOOKUP_CACHE_MAGIC: [u8; 4] = *b"TLI0";
11
12 #[cfg(feature = "ignore_lookup_cache")]
13 pub(super) fn read_lookup_cache(&self) -> Option<TagLookupIndex> {
14 info!("Not loading tag cache: ignore_lookup_cache feature flag is set");
15 None
16 }
17
18 #[cfg(feature = "ignore_lookup_cache")]
19 pub(super) fn write_lookup_cache(&self) -> anyhow::Result<()> {
20 Ok(())
21 }
22
23 #[cfg(not(feature = "ignore_lookup_cache"))]
24 pub(super) fn read_lookup_cache(&self) -> Option<TagLookupIndex> {
25 use std::io::Read;
26
27 use crate::manager::path_cache::exe_relative_path;
28
29 let mut file = std::fs::File::open(exe_relative_path(&format!(
30 "lookup_cache_{}.bin",
31 self.cache_key()
32 )))
33 .ok()?;
34
35 let mut cache_data = Vec::new();
36 file.read_to_end(&mut cache_data).ok()?;
37 if cache_data[..4] != Self::LOOKUP_CACHE_MAGIC {
38 error!("Invalid lookup cache magic");
39 return None;
40 }
41
42 info!("Loading index cache");
43
44 let cache: Option<TagLookupIndex> =
45 bincode::decode_from_slice(&cache_data[4..], bincode::config::legacy())
46 .map(|(v, _)| v)
47 .ok();
48
49 cache
50 }
51
52 #[cfg(not(feature = "ignore_lookup_cache"))]
53 pub(super) fn write_lookup_cache(&self) -> anyhow::Result<()> {
54 use super::path_cache::exe_relative_path;
55
56 let mut data = Vec::new();
57 data.extend_from_slice(&Self::LOOKUP_CACHE_MAGIC);
58 data.extend_from_slice(&bincode::encode_to_vec(
59 &self.lookup,
60 bincode::config::legacy(),
61 )?);
62
63 Ok(std::fs::write(
64 exe_relative_path(&format!("lookup_cache_{}.bin", self.cache_key())),
65 data,
66 )?)
67 }
68
69 pub fn build_lookup_tables(&mut self) {
70 let start = std::time::Instant::now();
71 let tables: Vec<_> = self
72 .package_paths
73 .par_iter()
74 .filter_map(|(_, p)| {
75 let pkg = match self.version.open(&p.path) {
76 Ok(package) => package,
77 Err(e) => {
78 error!("Failed to open package '{}': {e}", p.filename);
79 return None;
80 }
81 };
82
83 let entries = (pkg.pkg_id(), pkg.entries().to_vec());
84 let redaction_level = (pkg.pkg_id(), pkg.redaction_level());
85
86 let collect = pkg
87 .hash64_table()
88 .iter()
89 .map(|h| {
90 (
91 h.hash64,
92 HashTableEntryShort {
93 hash32: h.hash32,
94 reference: h.reference,
95 },
96 )
97 })
98 .collect::<Vec<(u64, HashTableEntryShort)>>();
99 let hashes = collect;
100
101 let named_tags = pkg.named_tags();
102
103 Some((entries, hashes, named_tags, redaction_level))
104 })
105 .collect();
106
107 let (entries, hashes, named_tags, redaction_levels): (
108 _,
109 Vec<_>,
110 Vec<_>,
111 HashMap<u16, Redaction>,
112 ) = tables.into_iter().multiunzip();
113
114 self.lookup = TagLookupIndex {
115 tag32_entries_by_pkg: entries,
116 tag32_to_tag64: hashes
117 .iter()
118 .flatten()
119 .map(|(h64, entry)| (entry.hash32, TagHash64(*h64)))
120 .collect(),
121 tag64_entries: hashes.into_iter().flatten().collect(),
122 named_tags: named_tags.into_iter().flatten().collect(),
123 redaction_levels: redaction_levels.into_iter().collect(),
124 };
125
126 info!(
127 "Built lookup table for {} packages in {:?}",
128 self.lookup.tag32_entries_by_pkg.len(),
129 start.elapsed()
130 );
131 }
132}