6
6
"fmt"
7
7
"io"
8
8
"os"
9
- "path"
10
9
"path/filepath"
11
- "strings"
12
10
"time"
13
11
14
12
"gopkg.in/src-d/go-billy.v2"
@@ -18,16 +16,17 @@ const separator = '/'
18
16
19
17
// Memory a very convenient filesystem based on memory files
20
18
type Memory struct {
21
- base string
22
- s * storage
19
+ base string
20
+ s * storage
21
+
23
22
tempCount int
24
23
}
25
24
26
25
//New returns a new Memory filesystem
27
26
func New () * Memory {
28
27
return & Memory {
29
- base : "/" ,
30
- s : & storage { make ( map [ string ] * file , 0 )} ,
28
+ base : string ( separator ) ,
29
+ s : newStorage () ,
31
30
}
32
31
}
33
32
@@ -43,102 +42,63 @@ func (fs *Memory) Open(filename string) (billy.File, error) {
43
42
44
43
// OpenFile returns the file from a given name with given flag and permits.
45
44
func (fs * Memory ) OpenFile (filename string , flag int , perm os.FileMode ) (billy.File , error ) {
46
- fullpath := fs .Join (fs .base , filename )
47
- f , ok := fs .s .files [fullpath ]
45
+ path := fs .Join (fs .base , filename )
48
46
49
- if ! ok {
47
+ f , has := fs .s .Get (path )
48
+ if ! has {
50
49
if ! isCreate (flag ) {
51
50
return nil , os .ErrNotExist
52
51
}
53
52
54
- fs .s .files [fullpath ] = newFile (fs .base , fullpath , perm , flag )
55
- return fs .s .files [fullpath ], nil
53
+ var err error
54
+ f , err = fs .s .New (path , perm , flag )
55
+ if err != nil {
56
+ return nil , err
57
+ }
56
58
}
57
59
58
- if f .isDir {
60
+ if f .mode . IsDir () {
59
61
return nil , fmt .Errorf ("cannot open directory: %s" , filename )
60
62
}
61
63
62
- n := newFile (fs .base , fullpath , perm , flag )
63
- n .content = f .content
64
-
65
- if isAppend (flag ) {
66
- n .position = int64 (n .content .Len ())
67
- }
68
-
69
- if isTruncate (flag ) {
70
- n .content .Truncate ()
64
+ filename , err := filepath .Rel (fs .base , path )
65
+ if err != nil {
66
+ return nil , err
71
67
}
72
68
73
- return n , nil
69
+ return f . Duplicate ( filename , perm , flag ) , nil
74
70
}
75
71
76
72
// Stat returns a billy.FileInfo with the information of the requested file.
77
73
func (fs * Memory ) Stat (filename string ) (billy.FileInfo , error ) {
78
74
fullpath := fs .Join (fs .base , filename )
79
75
80
- f , ok := fs .s .files [fullpath ]
81
- if ok && ! f .isDir {
82
- return newFileInfo (fs .base , fullpath , f .mode , fs .s .files [fullpath ].content .Len ()), nil
83
- }
84
-
85
- info , err := fs .ReadDir (fullpath )
86
- if err == nil && len (info ) != 0 || f != nil && f .isDir {
87
- fi := newFileInfo (fs .base , fullpath , 0 , len (info ))
88
- fi .isDir = true
89
- return fi , nil
76
+ f , has := fs .s .Get (fullpath )
77
+ if ! has {
78
+ return nil , os .ErrNotExist
90
79
}
91
80
92
- return nil , os . ErrNotExist
81
+ return f . Stat (), nil
93
82
}
94
83
95
84
// ReadDir returns a list of billy.FileInfo in the given directory.
96
- func (fs * Memory ) ReadDir (base string ) (entries []billy.FileInfo , err error ) {
97
- base = fs .Join (fs .base , base )
85
+ func (fs * Memory ) ReadDir (path string ) ([]billy.FileInfo , error ) {
86
+ path = fs .Join (fs .base , path )
98
87
99
- appendedDirs := make (map [string ]bool , 0 )
100
- for fullpath , f := range fs .s .files {
101
- if ! isInDir (base , fullpath ) {
102
- continue
103
- }
104
-
105
- fullpath , _ = filepath .Rel (base , fullpath )
106
- parts := strings .Split (fullpath , string (separator ))
107
-
108
- if len (parts ) == 1 {
109
- if f .isDir {
110
- entries = append (entries , & fileInfo {name : parts [0 ], isDir : true })
111
- }
112
-
113
- entries = append (entries , & fileInfo {name : parts [0 ], mode : f .mode , size : f .content .Len ()})
114
- continue
115
- }
116
-
117
- if _ , ok := appendedDirs [parts [0 ]]; ok {
118
- continue
119
- }
120
-
121
- entries = append (entries , & fileInfo {name : parts [0 ], mode : f .mode , isDir : true })
122
- appendedDirs [parts [0 ]] = true
88
+ var entries []billy.FileInfo
89
+ for _ , f := range fs .s .Children (path ) {
90
+ entries = append (entries , f .Stat ())
123
91
}
124
92
125
- return
93
+ return entries , nil
126
94
}
127
95
128
96
// MkdirAll creates a directory.
129
97
func (fs * Memory ) MkdirAll (path string , perm os.FileMode ) error {
130
98
fullpath := fs .Join (fs .base , path )
131
- f , ok := fs .s .files [fullpath ]
132
- if ok {
133
- if ! f .isDir {
134
- return fmt .Errorf ("%s is a file" , path )
135
- }
136
99
137
- return nil
138
- }
139
-
140
- fs .s .files [fullpath ] = & file {isDir : true }
141
- return nil
100
+ _ , err := fs .s .New (fullpath , perm | os .ModeDir , 0 )
101
+ return err
142
102
}
143
103
144
104
var maxTempFiles = 1024 * 4
@@ -171,33 +131,16 @@ func (fs *Memory) Rename(from, to string) error {
171
131
from = fs .Join (fs .base , from )
172
132
to = fs .Join (fs .base , to )
173
133
174
- if _ , ok := fs .s .files [from ]; ! ok {
175
- return os .ErrNotExist
176
- }
177
-
178
- fs .s .files [to ] = fs .s .files [from ]
179
- fs .s .files [to ].BaseFilename = to
180
- delete (fs .s .files , from )
181
-
182
- return nil
134
+ return fs .s .Rename (from , to )
183
135
}
184
136
185
137
// Remove deletes a given file from storage.
186
138
func (fs * Memory ) Remove (filename string ) error {
187
139
fullpath := fs .Join (fs .base , filename )
188
- if _ , ok := fs .s .files [fullpath ]; ! ok {
189
- if fs .isDir (fullpath ) {
190
- return fmt .Errorf ("directory not empty: %s" , filename )
191
- }
192
-
193
- return os .ErrNotExist
194
- }
195
-
196
- delete (fs .s .files , fullpath )
197
- return nil
140
+ return fs .s .Remove (fullpath )
198
141
}
199
142
200
- // Join concatenatess part of a path together .
143
+ // Join joins any number of path elements into a single path, adding a Separator if necessary .
201
144
func (fs * Memory ) Join (elem ... string ) string {
202
145
return filepath .Join (elem ... )
203
146
}
@@ -216,35 +159,13 @@ func (fs *Memory) Base() string {
216
159
return fs .base
217
160
}
218
161
219
- func (fs * Memory ) isDir (path string ) bool {
220
- for fpath := range fs .s .files {
221
- if isInDir (path , fpath ) {
222
- return true
223
- }
224
- }
225
-
226
- return false
227
- }
228
-
229
162
type file struct {
230
163
billy.BaseFile
231
164
232
165
content * content
233
166
position int64
234
167
flag int
235
168
mode os.FileMode
236
- isDir bool
237
- }
238
-
239
- func newFile (base , fullpath string , mode os.FileMode , flag int ) * file {
240
- filename , _ := filepath .Rel (base , fullpath )
241
-
242
- return & file {
243
- BaseFile : billy.BaseFile {BaseFilename : filename },
244
- content : & content {},
245
- mode : mode ,
246
- flag : flag ,
247
- }
248
169
}
249
170
250
171
func (f * file ) Read (b []byte ) (int , error ) {
@@ -312,28 +233,39 @@ func (f *file) Close() error {
312
233
return nil
313
234
}
314
235
315
- func (f * file ) Open () error {
316
- f .Closed = false
317
- return nil
318
- }
236
+ func (f * file ) Duplicate (filename string , mode os.FileMode , flag int ) billy.File {
237
+ new := & file {
238
+ BaseFile : billy.BaseFile {BaseFilename : filename },
239
+ content : f .content ,
240
+ mode : mode ,
241
+ flag : flag ,
242
+ }
319
243
320
- type fileInfo struct {
321
- name string
322
- size int
323
- mode os.FileMode
324
- isDir bool
325
- }
244
+ if isAppend (flag ) {
245
+ new .position = int64 (new .content .Len ())
246
+ }
247
+
248
+ if isTruncate (flag ) {
249
+ new .content .Truncate ()
250
+ }
326
251
327
- func newFileInfo ( base , fullpath string , mode os. FileMode , size int ) * fileInfo {
328
- filename , _ := filepath . Rel ( base , fullpath )
252
+ return new
253
+ }
329
254
255
+ func (f * file ) Stat () billy.FileInfo {
330
256
return & fileInfo {
331
- name : filename ,
332
- mode : mode ,
333
- size : size ,
257
+ name : f . Filename () ,
258
+ mode : f . mode ,
259
+ size : f . content . Len () ,
334
260
}
335
261
}
336
262
263
+ type fileInfo struct {
264
+ name string
265
+ size int
266
+ mode os.FileMode
267
+ }
268
+
337
269
func (fi * fileInfo ) Name () string {
338
270
return fi .name
339
271
}
@@ -351,46 +283,13 @@ func (*fileInfo) ModTime() time.Time {
351
283
}
352
284
353
285
func (fi * fileInfo ) IsDir () bool {
354
- return fi .isDir
286
+ return fi .mode . IsDir ()
355
287
}
356
288
357
289
func (* fileInfo ) Sys () interface {} {
358
290
return nil
359
291
}
360
292
361
- type storage struct {
362
- files map [string ]* file
363
- }
364
-
365
- type content struct {
366
- bytes []byte
367
- }
368
-
369
- func (c * content ) WriteAt (p []byte , off int64 ) (int , error ) {
370
- prev := len (c .bytes )
371
- c .bytes = append (c .bytes [:off ], p ... )
372
- if len (c .bytes ) < prev {
373
- c .bytes = c .bytes [:prev ]
374
- }
375
-
376
- return len (p ), nil
377
- }
378
-
379
- func (c * content ) ReadAt (b []byte , off int64 ) (int , error ) {
380
- size := int64 (len (c .bytes ))
381
- if off >= size {
382
- return 0 , io .EOF
383
- }
384
-
385
- l := int64 (len (b ))
386
- if off + l > size {
387
- l = size - off
388
- }
389
-
390
- n := copy (b , c .bytes [off :off + l ])
391
- return n , nil
392
- }
393
-
394
293
func (c * content ) Truncate () {
395
294
c .bytes = make ([]byte , 0 )
396
295
}
@@ -422,19 +321,3 @@ func isReadOnly(flag int) bool {
422
321
func isWriteOnly (flag int ) bool {
423
322
return flag & os .O_WRONLY != 0
424
323
}
425
-
426
- func isInDir (dir , other string ) bool {
427
- dir = path .Clean (dir )
428
- dir = toTrailingSlash (dir )
429
- other = path .Clean (other )
430
-
431
- return strings .HasPrefix (other , dir )
432
- }
433
-
434
- func toTrailingSlash (p string ) string {
435
- if strings .HasSuffix (p , "/" ) {
436
- return p
437
- }
438
-
439
- return p + "/"
440
- }
0 commit comments