1use std::sync::{Arc, Mutex};
2use std::fmt;
3use file_utils;
4use misc_utils;
5use misc_utils::RequiredLenAndSeekTo;
6
7use cli_utils::report_ref_block_info;
8use cli_utils::setup_ctrlc_handler;
9
10use std::io::SeekFrom;
11
12use json_printer::BracketType;
13
14use progress_report::*;
15
16use file_reader::{FileReader,
17 FileReaderParam};
18
19use multihash::*;
20
21use general_error::Error;
22
23use sbx_block::Block;
24use sbx_specs::SBX_LARGEST_BLOCK_SIZE;
25use sbx_specs::ver_to_usize;
26use sbx_specs::ver_uses_rs;
27
28use time_utils;
29use block_utils;
30
31use block_utils::RefBlockChoice;
32use sbx_block::BlockType;
33
34use json_printer::JSONPrinter;
35
36#[derive(Clone, Debug)]
37pub struct Stats {
38 pub bytes_processed : u64,
39 pub total_bytes : u64,
40 meta_block_count : u64,
41 start_time : f64,
42 end_time : f64,
43 json_printer : Arc<JSONPrinter>,
44}
45
46impl Stats {
47 pub fn new(file_size : u64,
48 json_printer : &Arc<JSONPrinter>) -> Stats {
49 Stats {
50 bytes_processed : 0,
51 total_bytes : file_size,
52 meta_block_count : 0,
53 start_time : 0.,
54 end_time : 0.,
55 json_printer : Arc::clone(json_printer),
56 }
57 }
58}
59
60impl ProgressReport for Stats {
61 fn start_time_mut(&mut self) -> &mut f64 { &mut self.start_time }
62
63 fn end_time_mut(&mut self) -> &mut f64 { &mut self.end_time }
64
65 fn units_so_far(&self) -> u64 { self.bytes_processed }
66
67 fn total_units(&self) -> u64 { self.total_bytes }
68}
69
70impl fmt::Display for Stats {
71 fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
72 if self.meta_block_count == 0 {
73 write_if!(not_json => f, self.json_printer => "No metadata blocks found";)
74 } else {
75 Ok(())
76 }
77 }
78}
79
80#[derive(Clone, Debug)]
81pub struct Param {
82 show_all : bool,
83 guess_burst : bool,
84 force_misalign : bool,
85 json_printer : Arc<JSONPrinter>,
86 from_pos : Option<u64>,
87 to_pos : Option<u64>,
88 in_file : String,
89 pr_verbosity_level : PRVerbosityLevel
90}
91
92impl Param {
93 pub fn new(show_all : bool,
94 guess_burst : bool,
95 force_misalign : bool,
96 json_printer : &Arc<JSONPrinter>,
97 from_pos : Option<u64>,
98 to_pos : Option<u64>,
99 in_file : &str,
100 pr_verbosity_level : PRVerbosityLevel) -> Param {
101 Param {
102 show_all,
103 guess_burst,
104 force_misalign,
105 json_printer : Arc::clone(json_printer),
106 from_pos,
107 to_pos,
108 in_file : String::from(in_file),
109 pr_verbosity_level,
110 }
111 }
112}
113
114pub fn show_file(param : &Param)
115 -> Result<Stats, Error> {
116 let ctrlc_stop_flag = setup_ctrlc_handler(param.json_printer.json_enabled());
117
118 let json_printer = ¶m.json_printer;
119
120 if param.guess_burst {
121 print_if!(not_json => json_printer => "Guessing burst error resistance level";);
122 print_if!(not_json => json_printer => "";);
123
124 let (ref_block_pos, ref_block) =
125 match block_utils::get_ref_block(¶m.in_file,
126 RefBlockChoice::MustBe(BlockType::Meta),
127 param.pr_verbosity_level,
128 param.json_printer.json_enabled(),
129 &ctrlc_stop_flag)? {
130 None => { return Err(Error::with_message("Failed to find reference block")); },
131 Some(x) => x,
132 };
133
134 report_ref_block_info(json_printer, ref_block_pos, &ref_block);
135
136 print_if!(not_json => json_printer => "";);
137
138 if ver_uses_rs(ref_block.get_version()) {
139 match block_utils::guess_burst_err_resistance_level(¶m.in_file,
140 ref_block_pos,
141 &ref_block) {
142 Err(e) => { return Err(Error::with_message(&format!("Error encountered when guessing : {}", e))) },
143 Ok(None) => print_if!(not_json => json_printer => "Failed to guess level";),
144 Ok(Some(x)) => print_maybe_json!(json_printer, "Best guess for burst error resistance level : {}", x => skip_quotes),
145 }
146 } else {
147 print_if!(not_json => json_printer => "Reference block version does not use Reed-Solomon erasure code";);
148 print_field_if_json!(json_printer, "Best guess for burst error resistance level : null" => skip_quotes);
149 }
150
151 print_if!(not_json => json_printer => "";);
152 print_if!(not_json => json_printer => "========================================";);
153 print_if!(not_json => json_printer => "";);
154 }
155
156 let file_size = file_utils::get_file_size(¶m.in_file)?;
157
158 let stats = Arc::new(Mutex::new(Stats::new(file_size,
159 ¶m.json_printer)));
160
161 let reporter = ProgressReporter::new(&stats,
162 "Metadata block scanning progress",
163 "bytes",
164 param.pr_verbosity_level,
165 param.json_printer.json_enabled());
166
167 let mut block = Block::dummy();
168 let mut buffer : [u8; SBX_LARGEST_BLOCK_SIZE] =
169 [0; SBX_LARGEST_BLOCK_SIZE];
170
171 let mut reader = FileReader::new(¶m.in_file,
172 FileReaderParam { write : false,
173 buffered : true })?;
174
175 let RequiredLenAndSeekTo { required_len, seek_to } =
177 misc_utils::calc_required_len_and_seek_to_from_byte_range_inc(param.from_pos,
178 param.to_pos,
179 param.force_misalign,
180 stats.lock().unwrap().bytes_processed,
181 file_size);
182
183 reader.seek(SeekFrom::Start(seek_to))?;
185
186 reporter.start();
187
188 let mut meta_block_count : u64 = 0;
189
190 let mut block_pos : u64;
191 let mut bytes_processed : u64 = 0;
192
193 json_printer.print_open_bracket(Some("blocks"), BracketType::Square);
194
195 loop {
196 break_if_atomic_bool!(ctrlc_stop_flag);
197
198 break_if_reached_required_len!(bytes_processed,
199 required_len);
200
201 let lazy_read_res = block_utils::read_block_lazily(&mut block,
202 &mut buffer,
203 &mut reader)?;
204
205 block_pos = bytes_processed;
206 bytes_processed += lazy_read_res.len_read as u64;
207
208 stats.lock().unwrap().bytes_processed = bytes_processed;
209
210 break_if_eof_seen!(lazy_read_res);
211
212 if !lazy_read_res.usable { continue; }
213
214 if block.is_meta() {
215 reporter.pause();
216
217 json_printer.print_open_bracket(None, BracketType::Curly);
218
219 if param.show_all {
220 if meta_block_count > 0 {
221 print_if!(not_json => json_printer => "";);
222 }
223 print_maybe_json!(json_printer, "Metadata block number : {}", meta_block_count => skip_quotes);
224 print_if!(not_json => json_printer => "========================================";);
225 } else {
226 print_field_if_json!(json_printer, "Metadata block number : {}", meta_block_count => skip_quotes);
227 }
228
229 print_if!(not_json => json_printer => "Found at byte : {} (0x{:X})",
230 block_pos + seek_to,
231 block_pos + seek_to;);
232 print_field_if_json!(json_printer, "Found at byte : {}",
233 block_pos + seek_to => skip_quotes);
234
235 print_if!(not_json => json_printer => "";);
236
237 print_maybe_json!(json_printer, "File UID : {}",
238 misc_utils::bytes_to_upper_hex_string(&block.get_uid()));
239 print_maybe_json!(json_printer, "File name : {}",
240 block.get_FNM().unwrap().unwrap_or("N/A".to_string()));
241 print_maybe_json!(json_printer, "SBX container name : {}",
242 block.get_SNM().unwrap().unwrap_or("N/A".to_string()));
243 print_maybe_json!(json_printer, "SBX container version : {}",
244 if ver_uses_rs(block.get_version()) && !json_printer.json_enabled() {
245 format!("{} (0x{:X})",
246 ver_to_usize(block.get_version()),
247 ver_to_usize(block.get_version()))
248 } else {
249 ver_to_usize(block.get_version()).to_string()
250 });
251 print_maybe_json!(json_printer, "RS data shard count : {}",
252 if ver_uses_rs(block.get_version()) {
253 match block.get_RSD().unwrap() {
254 None => null_if_json_else!(json_printer, "N/A").to_string(),
255 Some(x) => x.to_string(),
256 }
257 } else {
258 null_if_json_else!(json_printer, "version does not use RS").to_string()
259 } => skip_quotes);
260 print_maybe_json!(json_printer, "RS parity shard count : {}",
261 if ver_uses_rs(block.get_version()) {
262 match block.get_RSP().unwrap() {
263 None => null_if_json_else!(json_printer, "N/A").to_string(),
264 Some(x) => x.to_string(),
265 }
266 } else {
267 null_if_json_else!(json_printer, "version does not use RS").to_string()
268 } => skip_quotes);
269 print_maybe_json!(json_printer, "File size : {}", match block.get_FSZ().unwrap() {
270 None => null_if_json_else!(json_printer, "N/A").to_string(),
271 Some(x) => x.to_string()
272 } => skip_quotes);
273 print_maybe_json!(json_printer, "File modification time : {}", match block.get_FDT().unwrap() {
274 None => null_if_json_else!(json_printer, "N/A").to_string(),
275 Some(x) => match (time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
276 time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local)) {
277 (Some(u), Some(l)) => format!("{} (UTC) {} (Local)", u, l),
278 _ => "Invalid recorded date time".to_string(),
279 }
280 });
281 print_maybe_json!(json_printer, "SBX encoding time : {}", match block.get_SDT().unwrap() {
282 None => null_if_json_else!(json_printer, "N/A").to_string(),
283 Some(x) => match (time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::UTC),
284 time_utils::i64_secs_to_date_time_string(x, time_utils::TimeMode::Local)) {
285 (Some(u), Some(l)) => format!("{} (UTC) {} (Local)", u, l),
286 _ => "Invalid recorded date time".to_string(),
287 }
288 });
289 print_maybe_json!(json_printer, "Hash : {}", match block.get_HSH().unwrap() {
290 None => null_if_json_else!(json_printer, "N/A").to_string(),
291 Some(h) => format!("{} - {}",
292 hash_type_to_string(h.0),
293 misc_utils::bytes_to_lower_hex_string(&h.1))
294 });
295
296 meta_block_count += 1;
297
298 reporter.resume();
299
300 json_printer.print_close_bracket();
301
302 if !param.show_all { break; }
303 }
304 }
305
306 json_printer.print_close_bracket();
307
308 reporter.stop();
309
310 stats.lock().unwrap().meta_block_count = meta_block_count;
311
312 let stats = stats.lock().unwrap().clone();
313
314 Ok(stats)
315}