@@ -3,18 +3,21 @@ use flate2::{read::GzDecoder, write::GzEncoder};
3
3
use rayon:: prelude:: * ;
4
4
use std:: { convert:: TryFrom , fmt, io:: Read , io:: Write , path:: Path , str:: FromStr } ;
5
5
use xz2:: { read:: XzDecoder , write:: XzEncoder } ;
6
+ use zstd:: stream:: { read:: Decoder as ZstdDecoder , write:: Encoder as ZstdEncoder } ;
6
7
7
8
#[ derive( Debug , Copy , Clone ) ]
8
9
pub enum CompressionFormat {
9
10
Gz ,
10
11
Xz ,
12
+ Zstd ,
11
13
}
12
14
13
15
impl CompressionFormat {
14
16
pub ( crate ) fn detect_from_path ( path : impl AsRef < Path > ) -> Option < Self > {
15
17
match path. as_ref ( ) . extension ( ) . and_then ( |e| e. to_str ( ) ) {
16
18
Some ( "gz" ) => Some ( CompressionFormat :: Gz ) ,
17
19
Some ( "xz" ) => Some ( CompressionFormat :: Xz ) ,
20
+ Some ( "zst" ) => Some ( CompressionFormat :: Zstd ) ,
18
21
_ => None ,
19
22
}
20
23
}
@@ -23,6 +26,7 @@ impl CompressionFormat {
23
26
match self {
24
27
CompressionFormat :: Gz => "gz" ,
25
28
CompressionFormat :: Xz => "xz" ,
29
+ CompressionFormat :: Zstd => "zst" ,
26
30
}
27
31
}
28
32
@@ -71,6 +75,16 @@ impl CompressionFormat {
71
75
) ;
72
76
Box :: new ( compressor)
73
77
}
78
+ CompressionFormat :: Zstd => {
79
+ // zstd's default compression level is 3, which is on par with gzip but much faster
80
+ let mut enc = ZstdEncoder :: new ( file, 3 ) . context ( "failed to initialize zstd encoder" ) ?;
81
+ // Long-distance matching provides a substantial benefit for our tarballs
82
+ enc. long_distance_matching ( true ) . context ( "zst long_distance_matching" ) ?;
83
+ // Enable multithreaded mode. zstd seems to be faster when using the number of
84
+ // physical CPU cores rather than logical/SMT threads.
85
+ enc. multithread ( num_cpus:: get_physical ( ) as u32 ) . context ( "zst multithreaded" ) ?;
86
+ Box :: new ( enc)
87
+ }
74
88
} )
75
89
}
76
90
@@ -79,6 +93,7 @@ impl CompressionFormat {
79
93
Ok ( match self {
80
94
CompressionFormat :: Gz => Box :: new ( GzDecoder :: new ( file) ) ,
81
95
CompressionFormat :: Xz => Box :: new ( XzDecoder :: new ( file) ) ,
96
+ CompressionFormat :: Zstd => Box :: new ( ZstdDecoder :: new ( file) ?) ,
82
97
} )
83
98
}
84
99
}
@@ -96,6 +111,7 @@ impl TryFrom<&'_ str> for CompressionFormats {
96
111
match format. trim ( ) {
97
112
"gz" => parsed. push ( CompressionFormat :: Gz ) ,
98
113
"xz" => parsed. push ( CompressionFormat :: Xz ) ,
114
+ "zst" => parsed. push ( CompressionFormat :: Zstd ) ,
99
115
other => anyhow:: bail!( "unknown compression format: {}" , other) ,
100
116
}
101
117
}
@@ -121,6 +137,7 @@ impl fmt::Display for CompressionFormats {
121
137
match format {
122
138
CompressionFormat :: Xz => "xz" ,
123
139
CompressionFormat :: Gz => "gz" ,
140
+ CompressionFormat :: Zstd => "zst" ,
124
141
} ,
125
142
f,
126
143
) ?;
@@ -139,6 +156,10 @@ impl CompressionFormats {
139
156
pub ( crate ) fn iter ( & self ) -> impl Iterator < Item = CompressionFormat > + ' _ {
140
157
self . 0 . iter ( ) . map ( |i| * i)
141
158
}
159
+
160
+ pub ( crate ) fn len ( & self ) -> usize {
161
+ self . 0 . len ( )
162
+ }
142
163
}
143
164
144
165
pub ( crate ) trait Encoder : Send + Write {
@@ -159,6 +180,13 @@ impl<W: Send + Write> Encoder for XzEncoder<W> {
159
180
}
160
181
}
161
182
183
+ impl < W : Send + Write > Encoder for ZstdEncoder < ' _ , W > {
184
+ fn finish ( mut self : Box < Self > ) -> Result < ( ) , Error > {
185
+ ZstdEncoder :: do_finish ( self . as_mut ( ) ) . context ( "failed to finish .zst file" ) ?;
186
+ Ok ( ( ) )
187
+ }
188
+ }
189
+
162
190
pub ( crate ) struct CombinedEncoder {
163
191
encoders : Vec < Box < dyn Encoder > > ,
164
192
}
0 commit comments