@@ -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
@@ -48,6 +52,16 @@ impl CompressionFormat {
48
52
. encoder ( ) ?;
49
53
Box :: new ( XzEncoder :: new_stream ( file, stream) )
50
54
}
55
+ CompressionFormat :: Zstd => {
56
+ // zstd's default compression level is 3, which is on par with gzip but much faster
57
+ let mut enc = ZstdEncoder :: new ( file, 3 ) . context ( "failed to initialize zstd encoder" ) ?;
58
+ // Long-distance matching provides a substantial benefit for our tarballs
59
+ enc. long_distance_matching ( true ) . context ( "zst long_distance_matching" ) ?;
60
+ // Enable multithreaded mode. zstd seems to be faster when using the number of
61
+ // physical CPU cores rather than logical/SMT threads.
62
+ enc. multithread ( num_cpus:: get_physical ( ) as u32 ) . context ( "zst multithreaded" ) ?;
63
+ Box :: new ( enc)
64
+ }
51
65
} )
52
66
}
53
67
@@ -56,6 +70,7 @@ impl CompressionFormat {
56
70
Ok ( match self {
57
71
CompressionFormat :: Gz => Box :: new ( GzDecoder :: new ( file) ) ,
58
72
CompressionFormat :: Xz => Box :: new ( XzDecoder :: new ( file) ) ,
73
+ CompressionFormat :: Zstd => Box :: new ( ZstdDecoder :: new ( file) ?) ,
59
74
} )
60
75
}
61
76
}
@@ -73,6 +88,7 @@ impl TryFrom<&'_ str> for CompressionFormats {
73
88
match format. trim ( ) {
74
89
"gz" => parsed. push ( CompressionFormat :: Gz ) ,
75
90
"xz" => parsed. push ( CompressionFormat :: Xz ) ,
91
+ "zst" => parsed. push ( CompressionFormat :: Zstd ) ,
76
92
other => anyhow:: bail!( "unknown compression format: {}" , other) ,
77
93
}
78
94
}
@@ -97,6 +113,7 @@ impl fmt::Display for CompressionFormats {
97
113
fmt:: Display :: fmt ( match format {
98
114
CompressionFormat :: Xz => "xz" ,
99
115
CompressionFormat :: Gz => "gz" ,
116
+ CompressionFormat :: Zstd => "zst" ,
100
117
} , f) ?;
101
118
}
102
119
Ok ( ( ) )
@@ -113,6 +130,10 @@ impl CompressionFormats {
113
130
pub ( crate ) fn iter ( & self ) -> impl Iterator < Item = CompressionFormat > + ' _ {
114
131
self . 0 . iter ( ) . map ( |i| * i)
115
132
}
133
+
134
+ pub ( crate ) fn len ( & self ) -> usize {
135
+ self . 0 . len ( )
136
+ }
116
137
}
117
138
118
139
pub ( crate ) trait Encoder : Send + Write {
@@ -133,6 +154,13 @@ impl<W: Send + Write> Encoder for XzEncoder<W> {
133
154
}
134
155
}
135
156
157
+ impl < W : Send + Write > Encoder for ZstdEncoder < ' _ , W > {
158
+ fn finish ( mut self : Box < Self > ) -> Result < ( ) , Error > {
159
+ ZstdEncoder :: do_finish ( self . as_mut ( ) ) . context ( "failed to finish .zst file" ) ?;
160
+ Ok ( ( ) )
161
+ }
162
+ }
163
+
136
164
pub ( crate ) struct CombinedEncoder {
137
165
encoders : Vec < Box < dyn Encoder > > ,
138
166
}
0 commit comments