@@ -17,6 +17,7 @@ package tarfs
17
17
import (
18
18
"archive/tar"
19
19
"bufio"
20
+ "cmp"
20
21
"encoding/json"
21
22
"errors"
22
23
"fmt"
@@ -37,7 +38,7 @@ type Entry struct {
37
38
Offset int64
38
39
39
40
Filename string
40
- Dir string
41
+ dir string
41
42
fi fs.FileInfo
42
43
}
43
44
@@ -97,6 +98,7 @@ type FS struct {
97
98
ra io.ReaderAt
98
99
files []* Entry
99
100
index map [string ]int
101
+ dirs map [string ][]fs.DirEntry
100
102
}
101
103
102
104
func (fsys * FS ) Readlink (name string ) (string , error ) {
@@ -118,7 +120,7 @@ func (fsys *FS) Open(name string) (fs.File, error) {
118
120
if name == "." {
119
121
return & File {
120
122
Entry : & Entry {
121
- Dir : "." ,
123
+ dir : "." ,
122
124
Filename : "." ,
123
125
Header : tar.Header {
124
126
Name : "." ,
@@ -170,23 +172,12 @@ func (fsys *FS) Stat(name string) (fs.FileInfo, error) {
170
172
}
171
173
172
174
func (fsys * FS ) ReadDir (name string ) ([]fs.DirEntry , error ) {
173
- children := []fs.DirEntry {}
174
- for _ , f := range fsys .files {
175
- // This is load bearing for now.
176
- f := f
177
-
178
- if f .Dir != name {
179
- continue
180
- }
181
-
182
- children = append (children , f )
175
+ dirs , ok := fsys .dirs [name ]
176
+ if ! ok {
177
+ return []fs.DirEntry {}, nil
183
178
}
184
179
185
- slices .SortFunc (children , func (a , b fs.DirEntry ) int {
186
- return strings .Compare (a .Name (), b .Name ())
187
- })
188
-
189
- return children , nil
180
+ return dirs , nil
190
181
}
191
182
192
183
type countReader struct {
@@ -200,35 +191,22 @@ func (cr *countReader) Read(p []byte) (int, error) {
200
191
return n , err
201
192
}
202
193
203
- func New (ra io.ReaderAt ) (* FS , error ) {
194
+ func New (ra io.ReaderAt , size int64 ) (* FS , error ) {
204
195
fsys := & FS {
205
196
ra : ra ,
206
197
files : []* Entry {},
207
198
index : map [string ]int {},
199
+ dirs : map [string ][]fs.DirEntry {},
208
200
}
209
201
210
- var r io.Reader
211
- if reader , ok := ra .(io.Reader ); ok {
212
- r = reader
213
- } else {
214
- size := int64 (- 1 )
215
- if statter , ok := ra .(interface {
216
- Stat () (fs.FileInfo , error )
217
- }); ok {
218
- stat , err := statter .Stat ()
219
- if err != nil {
220
- return nil , err
221
- }
222
- size = stat .Size ()
223
- }
224
- r = io .NewSectionReader (ra , 0 , size )
225
- }
202
+ // Number of entries in a given directory, so we know how large of a slice to allocate.
203
+ dirCount := map [string ]int {}
226
204
205
+ r := io .NewSectionReader (ra , 0 , size )
227
206
cr := & countReader {bufio .NewReaderSize (r , 1 << 20 ), 0 }
228
207
tr := tar .NewReader (cr )
229
208
230
209
// TODO: Do this lazily.
231
- // TODO: Allow this to be saved and restored.
232
210
for {
233
211
hdr , err := tr .Next ()
234
212
if errors .Is (err , io .EOF ) {
@@ -239,15 +217,35 @@ func New(ra io.ReaderAt) (*FS, error) {
239
217
}
240
218
241
219
normalized := normalize (hdr .Name )
220
+ dir := path .Dir (normalized )
221
+
242
222
fsys .index [normalized ] = len (fsys .files )
243
223
244
224
fsys .files = append (fsys .files , & Entry {
245
225
Header : * hdr ,
246
226
Offset : cr .n ,
247
227
Filename : normalized ,
248
- Dir : path . Dir ( normalized ) ,
228
+ dir : dir ,
249
229
fi : hdr .FileInfo (),
250
230
})
231
+
232
+ dirCount [dir ]++
233
+ }
234
+
235
+ // Pre-generate the results of ReadDir so we don't allocate a ton if fs.WalkDir calls us.
236
+ // TODO: Consider doing this lazily in a sync.Once the first time we see a ReadDir.
237
+ for dir , count := range dirCount {
238
+ fsys .dirs [dir ] = make ([]fs.DirEntry , 0 , count )
239
+ }
240
+
241
+ for _ , f := range fsys .files {
242
+ fsys .dirs [f .dir ] = append (fsys .dirs [f .dir ], f )
243
+ }
244
+
245
+ for _ , files := range fsys .dirs {
246
+ slices .SortFunc (files , func (a , b fs.DirEntry ) int {
247
+ return cmp .Compare (a .Name (), b .Name ())
248
+ })
251
249
}
252
250
253
251
return fsys , nil
0 commit comments