1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
42
43
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
use std::io::{Read, Seek, SeekFrom};
use crate::{
Prefilter,
header::{TOABlockHeader, TOAHeader, is_trailer_after_ecc},
trailer::TOAFileTrailer,
};
/// Metadata information from an TOA file.
#[derive(Debug, Clone)]
pub struct TOAMetadata {
/// The prefilter used in the file
pub prefilter: Prefilter,
/// Blocksize used
pub block_size: u64,
/// LZMA literal context bits
pub lc: u8,
/// LZMA literal position bits
pub lp: u8,
/// LZMA position bits
pub pb: u8,
/// Dictionary size used
pub dict_size: u32,
/// Number of compressed blocks in the file
pub block_count: u64,
/// Total uncompressed size of all data
pub uncompressed_size: u64,
/// Total compressed size of all data (excluding header and trailer)
pub compressed_size: u64,
/// Blake3 hash of the uncompressed data
pub blake3_hash: [u8; 32],
/// Reed-Solomon parity data for hash protection
pub rs_parity: [u8; 24],
/// `True` if the hash could be validated
pub validated: bool,
/// `True` if the hash was corrupt but could be validated.
pub corrected: bool,
}
impl TOAMetadata {
/// Read TOA file metadata from a seekable decoder.
///
/// This function reads and validates the header and trailer of an TOA file,
/// returning metadata about the compression parameters and file sizes.
/// The decoder position is preserved.
#[cfg(feature = "std")]
pub fn parse<R: Read + Seek>(mut decoder: R) -> crate::Result<TOAMetadata> {
let original_pos = decoder.stream_position()?;
decoder.seek(SeekFrom::Start(0))?;
let mut header_buffer = [0u8; 32];
decoder.read_exact(&mut header_buffer)?;
let header = TOAHeader::parse(&header_buffer, true)?;
let mut compressed_size = 0u64;
let mut block_count = 0u64;
// Parse blocks until we find the final trailer.
loop {
let mut buffer = [0u8; 64];
decoder.read_exact(&mut buffer)?;
// Check bit 0 (MSB) to determine if this is a block header or final trailer.
if is_trailer_after_ecc(&buffer, true)? {
let trailer_result = TOAFileTrailer::parse(&buffer, true);
let (trailer, validated, corrected) = match trailer_result {
Ok(trailer) => (trailer, true, false),
Err(_) => {
// Try parsing without Reed-Solomon correction.
match TOAFileTrailer::parse(&buffer, false) {
Ok(trailer) => (trailer, false, false),
Err(e) => {
decoder.seek(SeekFrom::Start(original_pos))?;
return Err(e);
}
}
}
};
decoder.seek(SeekFrom::Start(original_pos))?;
return Ok(TOAMetadata {
prefilter: header.prefilter(),
block_size: header.block_size(),
lc: header.lc(),
lp: header.lp(),
pb: header.pb(),
dict_size: header.dict_size(),
block_count,
uncompressed_size: trailer.total_uncompressed_size(),
compressed_size,
blake3_hash: trailer.blake3_hash(),
rs_parity: trailer.rs_parity(),
validated,
corrected,
});
} else {
let block_header = TOABlockHeader::parse(&buffer, true)?;
let physical_size = block_header.physical_size();
compressed_size += physical_size;
block_count += 1;
decoder.seek(SeekFrom::Current(physical_size as i64))?;
}
}
}
}