@@ -27,10 +27,26 @@ pub struct FileAttr {
27
27
28
28
pub struct ReadDir {
29
29
inner : Arc < ReadDirInner > ,
30
- cookie : Option < wasi:: Dircookie > ,
31
- buf : Vec < u8 > ,
32
- offset : usize ,
33
- cap : usize ,
30
+ state : ReadDirState ,
31
+ }
32
+
33
+ enum ReadDirState {
34
+ /// Next DirEntry should be read from contents of buf at `offset`
35
+ FillBuffer {
36
+ next_read_offset : wasi:: Dircookie ,
37
+ buf : Vec < u8 > ,
38
+ } ,
39
+ ProcessEntry {
40
+ buf : Vec < u8 > ,
41
+ next_read_offset : Option < wasi:: Dircookie > ,
42
+ offset : usize ,
43
+ } ,
44
+ /// Do not fetch any more entries, process all entries
45
+ RunUntilExhaustion {
46
+ buf : Vec < u8 > ,
47
+ offset : usize ,
48
+ } ,
49
+ Done ,
34
50
}
35
51
36
52
struct ReadDirInner {
@@ -147,11 +163,8 @@ impl FileType {
147
163
impl ReadDir {
148
164
fn new ( dir : File , root : PathBuf ) -> ReadDir {
149
165
ReadDir {
150
- cookie : Some ( 0 ) ,
151
- buf : vec ! [ 0 ; 128 ] ,
152
- offset : 0 ,
153
- cap : 0 ,
154
166
inner : Arc :: new ( ReadDirInner { dir, root } ) ,
167
+ state : ReadDirState :: FillBuffer { next_read_offset : 0 , buf : vec ! [ 0 ; 128 ] } ,
155
168
}
156
169
}
157
170
}
@@ -162,81 +175,99 @@ impl fmt::Debug for ReadDir {
162
175
}
163
176
}
164
177
178
+ impl core:: iter:: FusedIterator for ReadDir { }
179
+
165
180
impl Iterator for ReadDir {
166
181
type Item = io:: Result < DirEntry > ;
167
182
168
183
fn next ( & mut self ) -> Option < io:: Result < DirEntry > > {
169
- loop {
170
- // If we've reached the capacity of our buffer then we need to read
171
- // some more from the OS, otherwise we pick up at our old offset.
172
- let offset = if self . offset == self . cap {
173
- let cookie = self . cookie . take ( ) ?;
174
- match self . inner . dir . fd . readdir ( & mut self . buf , cookie) {
175
- Ok ( bytes) => {
176
- // No more entries if we read less than buffer size
177
- if bytes < self . buf . len ( ) {
178
- self . cookie = None ;
179
- if bytes == 0 {
180
- return None ;
181
- }
184
+ match & mut self . state {
185
+ ReadDirState :: FillBuffer { next_read_offset, ref mut buf } => {
186
+ let result = self . inner . dir . fd . readdir ( buf, * next_read_offset) ;
187
+ match result {
188
+ Ok ( read_bytes) => {
189
+ if read_bytes < buf. len ( ) {
190
+ buf. truncate ( read_bytes) ;
191
+ self . state =
192
+ ReadDirState :: RunUntilExhaustion { buf : mem:: take ( buf) , offset : 0 } ;
182
193
} else {
183
- self . cookie = Some ( cookie) ;
194
+ debug_assert_eq ! ( read_bytes, buf. len( ) ) ;
195
+ self . state = ReadDirState :: ProcessEntry {
196
+ buf : mem:: take ( buf) ,
197
+ offset : 0 ,
198
+ next_read_offset : Some ( * next_read_offset) ,
199
+ } ;
184
200
}
185
- self . cap = self . buf . len ( ) ;
186
- self . offset = 0 ;
187
- 0
201
+ self . next ( )
202
+ }
203
+ Err ( e) => {
204
+ self . state = ReadDirState :: Done ;
205
+ return Some ( Err ( e) ) ;
188
206
}
189
- Err ( e) => return Some ( Err ( e) ) ,
190
- }
191
- } else {
192
- self . offset
193
- } ;
194
- let data = & self . buf [ offset..self . cap ] ;
195
-
196
- // If we're not able to read a directory entry then that means it
197
- // must have been truncated at the end of the buffer, so reset our
198
- // offset so we can go back and reread into the buffer, picking up
199
- // where we last left off.
200
- let dirent_size = mem:: size_of :: < wasi:: Dirent > ( ) ;
201
- if data. len ( ) < dirent_size {
202
- assert ! ( self . buf. len( ) >= dirent_size) ;
203
- self . offset = self . cap ;
204
- continue ;
205
- }
206
- let ( dirent, data) = data. split_at ( dirent_size) ;
207
- let dirent = unsafe { ptr:: read_unaligned ( dirent. as_ptr ( ) as * const wasi:: Dirent ) } ;
208
-
209
- // If the file name was truncated, then we need to reinvoke
210
- // `readdir` so we truncate our buffer to start over and reread this
211
- // descriptor. Note that if our offset is 0 that means the file name
212
- // is massive and we need a bigger buffer.
213
- if data. len ( ) < dirent. d_namlen as usize {
214
- if offset == 0 {
215
- let amt_to_add = self . buf . capacity ( ) ;
216
- self . buf . extend ( iter:: repeat ( 0 ) . take ( amt_to_add) ) ;
217
207
}
218
- assert ! ( self . cookie. is_some( ) ) ;
219
- self . offset = self . cap ;
220
- continue ;
221
208
}
222
- self . cookie . as_mut ( ) . map ( |cookie| {
223
- * cookie = dirent. d_next ;
224
- } ) ;
225
- self . offset = offset + dirent_size + dirent. d_namlen as usize ;
209
+ ReadDirState :: ProcessEntry { ref mut buf, next_read_offset, offset } => {
210
+ let contents = & buf[ * offset..] ;
211
+ const DIRENT_SIZE : usize = crate :: mem:: size_of :: < wasi:: Dirent > ( ) ;
212
+ if contents. len ( ) >= DIRENT_SIZE {
213
+ let ( dirent, data) = contents. split_at ( DIRENT_SIZE ) ;
214
+ let dirent =
215
+ unsafe { ptr:: read_unaligned ( dirent. as_ptr ( ) as * const wasi:: Dirent ) } ;
216
+ // If the file name was truncated, then we need to reinvoke
217
+ // `readdir` so we truncate our buffer to start over and reread this
218
+ // descriptor.
219
+ if data. len ( ) < dirent. d_namlen as usize {
220
+ if buf. len ( ) < dirent. d_namlen as usize + DIRENT_SIZE {
221
+ buf. resize ( dirent. d_namlen as usize + DIRENT_SIZE , 0 ) ;
222
+ }
223
+ if let Some ( next_read_offset) = * next_read_offset {
224
+ self . state =
225
+ ReadDirState :: FillBuffer { next_read_offset, buf : mem:: take ( buf) } ;
226
+ } else {
227
+ self . state = ReadDirState :: Done ;
228
+ }
229
+
230
+ return self . next ( ) ;
231
+ }
232
+ next_read_offset. as_mut ( ) . map ( |cookie| {
233
+ * cookie = dirent. d_next ;
234
+ } ) ;
235
+ * offset = * offset + DIRENT_SIZE + dirent. d_namlen as usize ;
236
+
237
+ let name = & data[ ..( dirent. d_namlen as usize ) ] ;
226
238
227
- let name = & data[ ..( dirent. d_namlen as usize ) ] ;
239
+ // These names are skipped on all other platforms, so let's skip
240
+ // them here too
241
+ if name == b"." || name == b".." {
242
+ return self . next ( ) ;
243
+ }
228
244
229
- // These names are skipped on all other platforms, so let's skip
230
- // them here too
231
- if name == b"." || name == b".." {
232
- continue ;
245
+ return Some ( Ok ( DirEntry {
246
+ meta : dirent,
247
+ name : name. to_vec ( ) ,
248
+ inner : self . inner . clone ( ) ,
249
+ } ) ) ;
250
+ } else if let Some ( next_read_offset) = * next_read_offset {
251
+ self . state = ReadDirState :: FillBuffer { next_read_offset, buf : mem:: take ( buf) } ;
252
+ } else {
253
+ self . state = ReadDirState :: Done ;
254
+ }
255
+ self . next ( )
233
256
}
257
+ ReadDirState :: RunUntilExhaustion { buf, offset } => {
258
+ if * offset >= buf. len ( ) {
259
+ self . state = ReadDirState :: Done ;
260
+ } else {
261
+ self . state = ReadDirState :: ProcessEntry {
262
+ buf : mem:: take ( buf) ,
263
+ offset : * offset,
264
+ next_read_offset : None ,
265
+ } ;
266
+ }
234
267
235
- return Some ( Ok ( DirEntry {
236
- meta : dirent,
237
- name : name. to_vec ( ) ,
238
- inner : self . inner . clone ( ) ,
239
- } ) ) ;
268
+ self . next ( )
269
+ }
270
+ ReadDirState :: Done => None ,
240
271
}
241
272
}
242
273
}
0 commit comments