casc_storage/index/
group_index.rs1use crate::error::{CascError, Result};
4use crate::types::{ArchiveLocation, EKey, IndexEntry};
5use byteorder::{LittleEndian, ReadBytesExt};
6use std::collections::HashMap;
7use std::fs::File;
8use std::io::{BufReader, Read};
9use std::path::Path;
10use tracing::{debug, trace};
11
12#[derive(Debug)]
14struct GroupIndexHeader {
15 version: u16,
16 bucket_index: u8,
17 #[allow(dead_code)]
18 extra_bytes: u8,
19 span_size_bytes: u8,
20 span_offset_bytes: u8,
21 ekey_bytes: u8,
22 archive_bytes: u8,
23 archive_total_size: u64,
24}
25
26pub struct GroupIndex {
28 entries: HashMap<EKey, ArchiveLocation>,
29 header: GroupIndexHeader,
30}
31
32impl GroupIndex {
33 pub fn parse_file(path: &Path) -> Result<Self> {
35 let file = File::open(path)?;
36 let mut reader = BufReader::new(file);
37 Self::parse(&mut reader)
38 }
39
40 pub fn parse<R: Read>(reader: &mut R) -> Result<Self> {
42 let header = Self::read_header(reader)?;
44
45 debug!(
46 "Parsing group index: version={}, bucket={:02x}, ekey_size={}",
47 header.version, header.bucket_index, header.ekey_bytes
48 );
49
50 let entry_size = (header.ekey_bytes as usize)
52 + (header.archive_bytes as usize)
53 + (header.span_offset_bytes as usize)
54 + (header.span_size_bytes as usize);
55
56 let mut data = Vec::new();
58 reader.read_to_end(&mut data)?;
59
60 let entry_count = data.len() / entry_size;
62 debug!(
63 "Parsing {} entries of {} bytes each",
64 entry_count, entry_size
65 );
66
67 let mut entries = HashMap::new();
68 let mut offset = 0;
69
70 for i in 0..entry_count {
71 let entry = Self::parse_entry(&data[offset..], &header)?;
72 offset += entry_size;
73
74 if i < 5 {
75 trace!(
76 "Entry {}: ekey={}, archive={}, offset={:x}, size={}",
77 i,
78 entry.ekey,
79 entry.location.archive_id,
80 entry.location.offset,
81 entry.location.size
82 );
83 }
84
85 entries.insert(entry.ekey, entry.location);
86 }
87
88 debug!("Parsed {} entries", entries.len());
89
90 Ok(Self { entries, header })
91 }
92
93 fn read_header<R: Read>(reader: &mut R) -> Result<GroupIndexHeader> {
94 Ok(GroupIndexHeader {
95 version: reader.read_u16::<LittleEndian>()?,
96 bucket_index: reader.read_u8()?,
97 extra_bytes: reader.read_u8()?,
98 span_size_bytes: reader.read_u8()?,
99 span_offset_bytes: reader.read_u8()?,
100 ekey_bytes: reader.read_u8()?,
101 archive_bytes: reader.read_u8()?,
102 archive_total_size: reader.read_u64::<LittleEndian>()?,
103 })
104 }
105
106 fn parse_entry(data: &[u8], header: &GroupIndexHeader) -> Result<IndexEntry> {
107 let mut offset = 0;
108
109 let ekey = if header.ekey_bytes == 9 {
111 let mut full_key = [0u8; 16];
112 full_key[0..9].copy_from_slice(&data[offset..offset + 9]);
113 offset += 9;
114 EKey::new(full_key)
115 } else if header.ekey_bytes == 16 {
116 let key_bytes = &data[offset..offset + 16];
117 offset += 16;
118 EKey::from_slice(key_bytes)
119 .ok_or_else(|| CascError::InvalidIndexFormat("Invalid key size".into()))?
120 } else {
121 return Err(CascError::InvalidIndexFormat(format!(
122 "Unsupported ekey size: {}",
123 header.ekey_bytes
124 )));
125 };
126
127 let archive_id = match header.archive_bytes {
129 1 => {
130 let id = data[offset];
131 offset += 1;
132 id as u16
133 }
134 2 => {
135 let id = u16::from_le_bytes([data[offset], data[offset + 1]]);
136 offset += 2;
137 id
138 }
139 _ => {
140 return Err(CascError::InvalidIndexFormat(format!(
141 "Unsupported archive bytes: {}",
142 header.archive_bytes
143 )));
144 }
145 };
146
147 let file_offset = match header.span_offset_bytes {
149 4 => {
150 let bytes = [
151 data[offset],
152 data[offset + 1],
153 data[offset + 2],
154 data[offset + 3],
155 ];
156 offset += 4;
157 u32::from_le_bytes(bytes) as u64
158 }
159 5 => {
160 let mut bytes = [0u8; 8];
161 bytes[0..5].copy_from_slice(&data[offset..offset + 5]);
162 offset += 5;
163 u64::from_le_bytes(bytes)
164 }
165 6 => {
166 let mut bytes = [0u8; 8];
167 bytes[0..6].copy_from_slice(&data[offset..offset + 6]);
168 offset += 6;
169 u64::from_le_bytes(bytes)
170 }
171 _ => {
172 return Err(CascError::InvalidIndexFormat(format!(
173 "Unsupported offset bytes: {}",
174 header.span_offset_bytes
175 )));
176 }
177 };
178
179 let size = match header.span_size_bytes {
181 4 => {
182 let bytes = [
183 data[offset],
184 data[offset + 1],
185 data[offset + 2],
186 data[offset + 3],
187 ];
188 u32::from_le_bytes(bytes)
189 }
190 3 => {
191 let mut bytes = [0u8; 4];
192 bytes[0..3].copy_from_slice(&data[offset..offset + 3]);
193 u32::from_le_bytes(bytes)
194 }
195 2 => {
196 let bytes = [data[offset], data[offset + 1]];
197 u16::from_le_bytes(bytes) as u32
198 }
199 _ => {
200 return Err(CascError::InvalidIndexFormat(format!(
201 "Unsupported size bytes: {}",
202 header.span_size_bytes
203 )));
204 }
205 };
206
207 Ok(IndexEntry {
208 ekey,
209 location: ArchiveLocation {
210 archive_id,
211 offset: file_offset,
212 size,
213 },
214 })
215 }
216
217 pub fn bucket_index(&self) -> u8 {
219 self.header.bucket_index
220 }
221
222 pub fn version(&self) -> u16 {
224 self.header.version
225 }
226
227 pub fn lookup(&self, ekey: &EKey) -> Option<&ArchiveLocation> {
229 self.entries.get(ekey)
230 }
231
232 pub fn len(&self) -> usize {
234 self.entries.len()
235 }
236
237 pub fn is_empty(&self) -> bool {
239 self.entries.is_empty()
240 }
241
242 pub fn entries(&self) -> impl Iterator<Item = (&EKey, &ArchiveLocation)> {
244 self.entries.iter()
245 }
246
247 pub fn archive_total_size(&self) -> u64 {
249 self.header.archive_total_size
250 }
251}