1
1
// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
2
2
// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.
3
3
4
+ #[ cfg( test) ]
5
+ mod tests;
6
+
4
7
pub use self :: imp:: read2;
5
- use std:: io;
8
+ use std:: io:: { self , Write } ;
9
+ use std:: mem:: replace;
6
10
use std:: process:: { Child , Output } ;
7
11
8
- pub fn read2_abbreviated ( mut child : Child ) -> io:: Result < Output > {
9
- use io:: Write ;
10
- use std:: mem:: replace;
11
-
12
- const HEAD_LEN : usize = 160 * 1024 ;
13
- const TAIL_LEN : usize = 256 * 1024 ;
14
-
15
- enum ProcOutput {
16
- Full ( Vec < u8 > ) ,
17
- Abbreviated { head : Vec < u8 > , skipped : usize , tail : Box < [ u8 ] > } ,
18
- }
19
-
20
- impl ProcOutput {
21
- fn extend ( & mut self , data : & [ u8 ] ) {
22
- let new_self = match * self {
23
- ProcOutput :: Full ( ref mut bytes) => {
24
- bytes. extend_from_slice ( data) ;
25
- let new_len = bytes. len ( ) ;
26
- if new_len <= HEAD_LEN + TAIL_LEN {
27
- return ;
28
- }
29
- let tail = bytes. split_off ( new_len - TAIL_LEN ) . into_boxed_slice ( ) ;
30
- let head = replace ( bytes, Vec :: new ( ) ) ;
31
- let skipped = new_len - HEAD_LEN - TAIL_LEN ;
32
- ProcOutput :: Abbreviated { head, skipped, tail }
33
- }
34
- ProcOutput :: Abbreviated { ref mut skipped, ref mut tail, .. } => {
35
- * skipped += data. len ( ) ;
36
- if data. len ( ) <= TAIL_LEN {
37
- tail[ ..data. len ( ) ] . copy_from_slice ( data) ;
38
- tail. rotate_left ( data. len ( ) ) ;
39
- } else {
40
- tail. copy_from_slice ( & data[ ( data. len ( ) - TAIL_LEN ) ..] ) ;
41
- }
42
- return ;
43
- }
44
- } ;
45
- * self = new_self;
46
- }
47
-
48
- fn into_bytes ( self ) -> Vec < u8 > {
49
- match self {
50
- ProcOutput :: Full ( bytes) => bytes,
51
- ProcOutput :: Abbreviated { mut head, skipped, tail } => {
52
- write ! ( & mut head, "\n \n <<<<<< SKIPPED {} BYTES >>>>>>\n \n " , skipped) . unwrap ( ) ;
53
- head. extend_from_slice ( & tail) ;
54
- head
55
- }
56
- }
57
- }
58
- }
59
-
60
- let mut stdout = ProcOutput :: Full ( Vec :: new ( ) ) ;
61
- let mut stderr = ProcOutput :: Full ( Vec :: new ( ) ) ;
12
+ pub fn read2_abbreviated ( mut child : Child , filter_paths_from_len : & [ String ] ) -> io:: Result < Output > {
13
+ let mut stdout = ProcOutput :: new ( ) ;
14
+ let mut stderr = ProcOutput :: new ( ) ;
62
15
63
16
drop ( child. stdin . take ( ) ) ;
64
17
read2 (
65
18
child. stdout . take ( ) . unwrap ( ) ,
66
19
child. stderr . take ( ) . unwrap ( ) ,
67
20
& mut |is_stdout, data, _| {
68
- if is_stdout { & mut stdout } else { & mut stderr } . extend ( data) ;
21
+ if is_stdout { & mut stdout } else { & mut stderr } . extend ( data, filter_paths_from_len ) ;
69
22
data. clear ( ) ;
70
23
} ,
71
24
) ?;
@@ -74,6 +27,98 @@ pub fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
74
27
Ok ( Output { status, stdout : stdout. into_bytes ( ) , stderr : stderr. into_bytes ( ) } )
75
28
}
76
29
30
+ const HEAD_LEN : usize = 160 * 1024 ;
31
+ const TAIL_LEN : usize = 256 * 1024 ;
32
+
33
+ // Whenever a path is filtered when counting the length of the output, we need to add some
34
+ // placeholder length to ensure a compiler emitting only filtered paths doesn't cause a OOM.
35
+ //
36
+ // 32 was chosen semi-arbitrarily: it was the highest power of two that still allowed the test
37
+ // suite to pass at the moment of implementing path filtering.
38
+ const FILTERED_PATHS_PLACEHOLDER_LEN : usize = 32 ;
39
+
40
+ enum ProcOutput {
41
+ Full { bytes : Vec < u8 > , filtered_len : usize } ,
42
+ Abbreviated { head : Vec < u8 > , skipped : usize , tail : Box < [ u8 ] > } ,
43
+ }
44
+
45
+ impl ProcOutput {
46
+ fn new ( ) -> Self {
47
+ ProcOutput :: Full { bytes : Vec :: new ( ) , filtered_len : 0 }
48
+ }
49
+
50
+ fn extend ( & mut self , data : & [ u8 ] , filter_paths_from_len : & [ String ] ) {
51
+ let new_self = match * self {
52
+ ProcOutput :: Full { ref mut bytes, ref mut filtered_len } => {
53
+ let old_len = bytes. len ( ) ;
54
+ bytes. extend_from_slice ( data) ;
55
+ * filtered_len += data. len ( ) ;
56
+
57
+ // We had problems in the past with tests failing only in some environments,
58
+ // due to the length of the base path pushing the output size over the limit.
59
+ //
60
+ // To make those failures deterministic across all environments we ignore known
61
+ // paths when calculating the string length, while still including the full
62
+ // path in the output. This could result in some output being larger than the
63
+ // threshold, but it's better than having nondeterministic failures.
64
+ //
65
+ // The compiler emitting only excluded strings is addressed by adding a
66
+ // placeholder size for each excluded segment, which will eventually reach
67
+ // the configured threshold.
68
+ for path in filter_paths_from_len {
69
+ let path_bytes = path. as_bytes ( ) ;
70
+ // We start matching `path_bytes - 1` into the previously loaded data,
71
+ // to account for the fact a path_bytes might be included across multiple
72
+ // `extend` calls. Starting from `- 1` avoids double-counting paths.
73
+ let matches = ( & bytes[ ( old_len. saturating_sub ( path_bytes. len ( ) - 1 ) ) ..] )
74
+ . windows ( path_bytes. len ( ) )
75
+ . filter ( |window| window == & path_bytes)
76
+ . count ( ) ;
77
+ * filtered_len -= matches * path_bytes. len ( ) ;
78
+
79
+ // We can't just remove the length of the filtered path from the output lenght,
80
+ // otherwise a compiler emitting only filtered paths would OOM compiletest. Add
81
+ // a fixed placeholder length for each path to prevent that.
82
+ * filtered_len += matches * FILTERED_PATHS_PLACEHOLDER_LEN ;
83
+ }
84
+
85
+ let new_len = bytes. len ( ) ;
86
+ if * filtered_len <= HEAD_LEN + TAIL_LEN {
87
+ return ;
88
+ }
89
+
90
+ let mut head = replace ( bytes, Vec :: new ( ) ) ;
91
+ let mut middle = head. split_off ( HEAD_LEN ) ;
92
+ let tail = middle. split_off ( middle. len ( ) - TAIL_LEN ) . into_boxed_slice ( ) ;
93
+ let skipped = new_len - HEAD_LEN - TAIL_LEN ;
94
+ ProcOutput :: Abbreviated { head, skipped, tail }
95
+ }
96
+ ProcOutput :: Abbreviated { ref mut skipped, ref mut tail, .. } => {
97
+ * skipped += data. len ( ) ;
98
+ if data. len ( ) <= TAIL_LEN {
99
+ tail[ ..data. len ( ) ] . copy_from_slice ( data) ;
100
+ tail. rotate_left ( data. len ( ) ) ;
101
+ } else {
102
+ tail. copy_from_slice ( & data[ ( data. len ( ) - TAIL_LEN ) ..] ) ;
103
+ }
104
+ return ;
105
+ }
106
+ } ;
107
+ * self = new_self;
108
+ }
109
+
110
+ fn into_bytes ( self ) -> Vec < u8 > {
111
+ match self {
112
+ ProcOutput :: Full { bytes, .. } => bytes,
113
+ ProcOutput :: Abbreviated { mut head, skipped, tail } => {
114
+ write ! ( & mut head, "\n \n <<<<<< SKIPPED {} BYTES >>>>>>\n \n " , skipped) . unwrap ( ) ;
115
+ head. extend_from_slice ( & tail) ;
116
+ head
117
+ }
118
+ }
119
+ }
120
+ }
121
+
77
122
#[ cfg( not( any( unix, windows) ) ) ]
78
123
mod imp {
79
124
use std:: io:: { self , Read } ;
0 commit comments