@@ -5,12 +5,13 @@ use std::io::{self, Write};
5
5
use std:: marker:: PhantomData ;
6
6
use std:: ops:: Range ;
7
7
use std:: path:: Path ;
8
+ use std:: path:: PathBuf ;
8
9
9
10
// -----------------------------------------------------------------------------
10
11
// Encoder
11
12
// -----------------------------------------------------------------------------
12
13
13
- pub type FileEncodeResult = Result < usize , io:: Error > ;
14
+ pub type FileEncodeResult = Result < usize , ( PathBuf , io:: Error ) > ;
14
15
15
16
/// The size of the buffer in `FileEncoder`.
16
17
const BUF_SIZE : usize = 8192 ;
@@ -34,21 +35,28 @@ pub struct FileEncoder {
34
35
// This is used to implement delayed error handling, as described in the
35
36
// comment on `trait Encoder`.
36
37
res : Result < ( ) , io:: Error > ,
38
+ path : PathBuf ,
39
+ #[ cfg( debug_assertions) ]
40
+ finished : bool ,
37
41
}
38
42
39
43
impl FileEncoder {
40
44
pub fn new < P : AsRef < Path > > ( path : P ) -> io:: Result < Self > {
41
45
// File::create opens the file for writing only. When -Zmeta-stats is enabled, the metadata
42
46
// encoder rewinds the file to inspect what was written. So we need to always open the file
43
47
// for reading and writing.
44
- let file = File :: options ( ) . read ( true ) . write ( true ) . create ( true ) . truncate ( true ) . open ( path) ?;
48
+ let file =
49
+ File :: options ( ) . read ( true ) . write ( true ) . create ( true ) . truncate ( true ) . open ( & path) ?;
45
50
46
51
Ok ( FileEncoder {
47
52
buf : vec ! [ 0u8 ; BUF_SIZE ] . into_boxed_slice ( ) . try_into ( ) . unwrap ( ) ,
53
+ path : path. as_ref ( ) . into ( ) ,
48
54
buffered : 0 ,
49
55
flushed : 0 ,
50
56
file,
51
57
res : Ok ( ( ) ) ,
58
+ #[ cfg( debug_assertions) ]
59
+ finished : false ,
52
60
} )
53
61
}
54
62
@@ -63,6 +71,10 @@ impl FileEncoder {
63
71
#[ cold]
64
72
#[ inline( never) ]
65
73
pub fn flush ( & mut self ) {
74
+ #[ cfg( debug_assertions) ]
75
+ {
76
+ self . finished = false ;
77
+ }
66
78
if self . res . is_ok ( ) {
67
79
self . res = self . file . write_all ( & self . buf [ ..self . buffered ] ) ;
68
80
}
@@ -74,6 +86,10 @@ impl FileEncoder {
74
86
& self . file
75
87
}
76
88
89
+ pub fn path ( & self ) -> & Path {
90
+ & self . path
91
+ }
92
+
77
93
#[ inline]
78
94
fn buffer_empty ( & mut self ) -> & mut [ u8 ] {
79
95
// SAFETY: self.buffered is inbounds as an invariant of the type
@@ -97,6 +113,10 @@ impl FileEncoder {
97
113
98
114
#[ inline]
99
115
fn write_all ( & mut self , buf : & [ u8 ] ) {
116
+ #[ cfg( debug_assertions) ]
117
+ {
118
+ self . finished = false ;
119
+ }
100
120
if let Some ( dest) = self . buffer_empty ( ) . get_mut ( ..buf. len ( ) ) {
101
121
dest. copy_from_slice ( buf) ;
102
122
self . buffered += buf. len ( ) ;
@@ -121,6 +141,10 @@ impl FileEncoder {
121
141
/// with one instruction, so while this does in some sense do wasted work, we come out ahead.
122
142
#[ inline]
123
143
pub fn write_with < const N : usize > ( & mut self , visitor : impl FnOnce ( & mut [ u8 ; N ] ) -> usize ) {
144
+ #[ cfg( debug_assertions) ]
145
+ {
146
+ self . finished = false ;
147
+ }
124
148
let flush_threshold = const { BUF_SIZE . checked_sub ( N ) . unwrap ( ) } ;
125
149
if std:: intrinsics:: unlikely ( self . buffered > flush_threshold) {
126
150
self . flush ( ) ;
@@ -152,20 +176,25 @@ impl FileEncoder {
152
176
} )
153
177
}
154
178
155
- pub fn finish ( mut self ) -> Result < usize , io :: Error > {
179
+ pub fn finish ( & mut self ) -> FileEncodeResult {
156
180
self . flush ( ) ;
181
+ #[ cfg( debug_assertions) ]
182
+ {
183
+ self . finished = true ;
184
+ }
157
185
match std:: mem:: replace ( & mut self . res , Ok ( ( ) ) ) {
158
186
Ok ( ( ) ) => Ok ( self . position ( ) ) ,
159
- Err ( e) => Err ( e ) ,
187
+ Err ( e) => Err ( ( self . path . clone ( ) , e ) ) ,
160
188
}
161
189
}
162
190
}
163
191
192
+ #[ cfg( debug_assertions) ]
164
193
impl Drop for FileEncoder {
165
194
fn drop ( & mut self ) {
166
- // Likely to be a no-op, because `finish` should have been called and
167
- // it also flushes. But do it just in case.
168
- self . flush ( ) ;
195
+ if !std :: thread :: panicking ( ) {
196
+ assert ! ( self . finished ) ;
197
+ }
169
198
}
170
199
}
171
200
0 commit comments