|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use std::fs::File;
|
12 |
| -use std::io::Write; |
| 12 | +use std::io::{self, Write, BufWriter}; |
13 | 13 | use std::path::Path;
|
14 |
| -use std::sync::Arc; |
15 |
| -use std::thread; |
16 | 14 |
|
17 | 15 | use flate2;
|
18 | 16 | use flate2::write::GzEncoder;
|
| 17 | +use rayon; |
19 | 18 | use tar::{Builder, Header};
|
20 | 19 | use walkdir::WalkDir;
|
21 | 20 | use xz2::write::XzEncoder;
|
@@ -57,41 +56,42 @@ impl Tarballer {
|
57 | 56 | .chain_err(|| "failed to collect file paths")?;
|
58 | 57 | files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
|
59 | 58 |
|
60 |
| - // Write the tar into both encoded files. We write all directories |
61 |
| - // first, so files may be directly created. (see rustup.rs#1092) |
62 |
| - let mut builder = Builder::new(Vec::new()); |
63 |
| - for path in dirs { |
64 |
| - let src = Path::new(&self.work_dir).join(&path); |
65 |
| - builder.append_dir(&path, &src) |
66 |
| - .chain_err(|| format!("failed to tar dir '{}'", src.display()))?; |
67 |
| - } |
68 |
| - for path in files { |
69 |
| - let src = Path::new(&self.work_dir).join(&path); |
70 |
| - let file = open_file(&src)?; |
71 |
| - builder.append_data(&mut header(&src, &file)?, &path, &file) |
72 |
| - .chain_err(|| format!("failed to tar file '{}'", src.display()))?; |
73 |
| - } |
74 |
| - let contents = builder.into_inner() |
75 |
| - .chain_err(|| "failed to finish writing .tar stream")?; |
76 |
| - let contents = Arc::new(contents); |
77 |
| - |
78 | 59 | // Prepare the .tar.gz file
|
79 |
| - let contents2 = contents.clone(); |
80 |
| - let t = thread::spawn(move || { |
81 |
| - let mut gz = GzEncoder::new(create_new_file(tar_gz)?, |
82 |
| - flate2::Compression::best()); |
83 |
| - gz.write_all(&contents2).chain_err(|| "failed to write .gz")?; |
84 |
| - gz.finish().chain_err(|| "failed to finish .gz") |
85 |
| - }); |
| 60 | + let gz = GzEncoder::new(create_new_file(tar_gz)?, flate2::Compression::best()); |
86 | 61 |
|
87 | 62 | // Prepare the .tar.xz file
|
88 |
| - let mut xz = XzEncoder::new(create_new_file(tar_xz)?, 9); |
89 |
| - xz.write_all(&contents).chain_err(|| "failed to write .xz")?; |
90 |
| - xz.finish().chain_err(|| "failed to finish .xz")?; |
| 63 | + let xz = XzEncoder::new(create_new_file(tar_xz)?, 9); |
91 | 64 |
|
92 |
| - t.join().unwrap()?; |
93 |
| - |
94 |
| - Ok(()) |
| 65 | + // Write the tar into both encoded files. We write all directories |
| 66 | + // first, so files may be directly created. (see rustup.rs#1092) |
| 67 | + let tee = RayonTee(xz, gz); |
| 68 | + let buf = BufWriter::with_capacity(1024 * 1024, tee); |
| 69 | + let mut builder = Builder::new(buf); |
| 70 | + |
| 71 | + let pool = rayon::Configuration::new().num_threads(2).build().unwrap(); |
| 72 | + pool.install(move || { |
| 73 | + for path in dirs { |
| 74 | + let src = Path::new(&self.work_dir).join(&path); |
| 75 | + builder.append_dir(&path, &src) |
| 76 | + .chain_err(|| format!("failed to tar dir '{}'", src.display()))?; |
| 77 | + } |
| 78 | + for path in files { |
| 79 | + let src = Path::new(&self.work_dir).join(&path); |
| 80 | + let file = open_file(&src)?; |
| 81 | + builder.append_data(&mut header(&src, &file)?, &path, &file) |
| 82 | + .chain_err(|| format!("failed to tar file '{}'", src.display()))?; |
| 83 | + } |
| 84 | + let RayonTee(xz, gz) = builder.into_inner() |
| 85 | + .chain_err(|| "failed to finish writing .tar stream")? |
| 86 | + .into_inner().ok().unwrap(); |
| 87 | + |
| 88 | + // Finish both encoded files |
| 89 | + let (rxz, rgz) = rayon::join( |
| 90 | + || xz.finish().chain_err(|| "failed to finish .tar.xz file"), |
| 91 | + || gz.finish().chain_err(|| "failed to finish .tar.gz file"), |
| 92 | + ); |
| 93 | + rxz.and(rgz).and(Ok(())) |
| 94 | + }) |
95 | 95 | }
|
96 | 96 | }
|
97 | 97 |
|
@@ -138,3 +138,24 @@ fn get_recursive_paths<P, Q>(root: P, name: Q) -> Result<(Vec<String>, Vec<Strin
|
138 | 138 | }
|
139 | 139 | Ok((dirs, files))
|
140 | 140 | }
|
| 141 | + |
| 142 | +struct RayonTee<A, B>(A, B); |
| 143 | + |
| 144 | +impl<A: Write + Send, B: Write + Send> Write for RayonTee<A, B> { |
| 145 | + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| 146 | + self.write_all(buf)?; |
| 147 | + Ok(buf.len()) |
| 148 | + } |
| 149 | + |
| 150 | + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
| 151 | + let (a, b) = (&mut self.0, &mut self.1); |
| 152 | + let (ra, rb) = rayon::join(|| a.write_all(buf), || b.write_all(buf)); |
| 153 | + ra.and(rb) |
| 154 | + } |
| 155 | + |
| 156 | + fn flush(&mut self) -> io::Result<()> { |
| 157 | + let (a, b) = (&mut self.0, &mut self.1); |
| 158 | + let (ra, rb) = rayon::join(|| a.flush(), || b.flush()); |
| 159 | + ra.and(rb) |
| 160 | + } |
| 161 | +} |
0 commit comments