@@ -27,10 +27,26 @@ pub struct FileAttr {
2727
2828pub struct ReadDir {
2929 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 ,
3450}
3551
3652struct ReadDirInner {
@@ -147,11 +163,8 @@ impl FileType {
147163impl ReadDir {
148164 fn new ( dir : File , root : PathBuf ) -> ReadDir {
149165 ReadDir {
150- cookie : Some ( 0 ) ,
151- buf : vec ! [ 0 ; 128 ] ,
152- offset : 0 ,
153- cap : 0 ,
154166 inner : Arc :: new ( ReadDirInner { dir, root } ) ,
167+ state : ReadDirState :: FillBuffer { next_read_offset : 0 , buf : vec ! [ 0 ; 128 ] } ,
155168 }
156169 }
157170}
@@ -162,81 +175,99 @@ impl fmt::Debug for ReadDir {
162175 }
163176}
164177
178+ impl core:: iter:: FusedIterator for ReadDir { }
179+
165180impl Iterator for ReadDir {
166181 type Item = io:: Result < DirEntry > ;
167182
168183 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 } ;
182193 } 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+ } ;
184200 }
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) ) ;
188206 }
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) ) ;
217207 }
218- assert ! ( self . cookie. is_some( ) ) ;
219- self . offset = self . cap ;
220- continue ;
221208 }
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 ) ] ;
226238
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+ }
228244
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 ( )
233256 }
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+ }
234267
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 ,
240271 }
241272 }
242273}
0 commit comments