1use super::error::{e_exit, e_println, ok_println};
2use memmap2::Mmap;
3use rayon::prelude::*;
4use std::fs::File;
5use std::io::Write;
6use std::path::PathBuf;
7use std::sync::{Arc, Mutex};
8
9pub struct ConvertCombine;
10
11const LARGE_FILE_THRESHOLD: u64 = 1_073_741_824;
12const CHUNK_SIZE: usize = 8 * 1024 * 1024;
13
14impl ConvertCombine {
15
16 pub fn combine_all(paths: Vec<PathBuf>, output: PathBuf) {
17
18 let output = match File::create(&output) {
19 Ok(f) => Arc::new(Mutex::new(f)),
20 Err(e) => e_exit("FILE_CREATE", &format!("Failed to create file: {}", e), 1),
21 };
22
23 paths.par_iter().for_each(|path| {
24 let process_result = || -> std::io::Result<()> {
26 let file = File::open(path)
27 .map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("Failed open: {}", e)))?;
28
29 let mmap = unsafe { Mmap::map(&file)? };
31
32 if file.metadata()?.len() > LARGE_FILE_THRESHOLD {
33 Self::process_large(&mmap, &output)
34 } else {
35 Self::process_small(&mmap, &output)
36 }
37 };
38
39 match process_result() {
40 Ok(_) => ok_println("Merge", &format!("{}", path.display()) ),
41 Err(e) => e_println("PROCESS_ERROR", &format!("Failed to process file [{}]: {}", path.display(), e)),
42 }
43 });
44
45 ok_println("MERGE_COMPLETE", "");
46 }
47
48 fn process_small(data: &Mmap, output: &Arc<Mutex<File>>) -> std::io::Result<()> {
50 let mut writer = output.lock()
51 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Lock failed: {}", e)))?;
52
53 writer.write_all(data)
54 .map_err(|e| std::io::Error::new(std::io::ErrorKind::WriteZero, format!("Write failed: {}", e)))?;
55
56 Ok(())
57 }
58
59 fn process_large(data: &Mmap, output: &Arc<Mutex<File>>) -> std::io::Result<()> {
61 let mut pos = 0;
62
63 while pos < data.len() {
64 let end = std::cmp::min(pos + CHUNK_SIZE, data.len());
65 let chunk = &data[pos..end];
66
67 let mut writer = output.lock()
68 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Lock failed: {}", e)))?;
69
70 writer.write_all(chunk)
71 .map_err(|e| std::io::Error::new(std::io::ErrorKind::WriteZero, format!("Write by blocks failed: {}", e)))?;
72
73 pos = end;
74 ok_println("PROGRESS", &format!("Wrote already {} MB", pos / 1024 / 1024));
75 }
76
77 Ok(())
78 }
79}