Skip to content

Commit 7b31338

Browse files
committed
memfs: rewrite memfs implementation
1 parent 614f916 commit 7b31338

File tree

2 files changed

+245
-177
lines changed

2 files changed

+245
-177
lines changed

memfs/memory.go

Lines changed: 60 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import (
66
"fmt"
77
"io"
88
"os"
9-
"path"
109
"path/filepath"
11-
"strings"
1210
"time"
1311

1412
"gopkg.in/src-d/go-billy.v2"
@@ -18,16 +16,17 @@ const separator = '/'
1816

1917
// Memory a very convenient filesystem based on memory files
2018
type Memory struct {
21-
base string
22-
s *storage
19+
base string
20+
s *storage
21+
2322
tempCount int
2423
}
2524

2625
//New returns a new Memory filesystem
2726
func New() *Memory {
2827
return &Memory{
29-
base: "/",
30-
s: &storage{make(map[string]*file, 0)},
28+
base: string(separator),
29+
s: newStorage(),
3130
}
3231
}
3332

@@ -43,102 +42,63 @@ func (fs *Memory) Open(filename string) (billy.File, error) {
4342

4443
// OpenFile returns the file from a given name with given flag and permits.
4544
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)
4846

49-
if !ok {
47+
f, has := fs.s.Get(path)
48+
if !has {
5049
if !isCreate(flag) {
5150
return nil, os.ErrNotExist
5251
}
5352

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+
}
5658
}
5759

58-
if f.isDir {
60+
if f.mode.IsDir() {
5961
return nil, fmt.Errorf("cannot open directory: %s", filename)
6062
}
6163

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
7167
}
7268

73-
return n, nil
69+
return f.Duplicate(filename, perm, flag), nil
7470
}
7571

7672
// Stat returns a billy.FileInfo with the information of the requested file.
7773
func (fs *Memory) Stat(filename string) (billy.FileInfo, error) {
7874
fullpath := fs.Join(fs.base, filename)
7975

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
9079
}
9180

92-
return nil, os.ErrNotExist
81+
return f.Stat(), nil
9382
}
9483

9584
// 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)
9887

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())
12391
}
12492

125-
return
93+
return entries, nil
12694
}
12795

12896
// MkdirAll creates a directory.
12997
func (fs *Memory) MkdirAll(path string, perm os.FileMode) error {
13098
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-
}
13699

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
142102
}
143103

144104
var maxTempFiles = 1024 * 4
@@ -171,33 +131,16 @@ func (fs *Memory) Rename(from, to string) error {
171131
from = fs.Join(fs.base, from)
172132
to = fs.Join(fs.base, to)
173133

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)
183135
}
184136

185137
// Remove deletes a given file from storage.
186138
func (fs *Memory) Remove(filename string) error {
187139
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)
198141
}
199142

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.
201144
func (fs *Memory) Join(elem ...string) string {
202145
return filepath.Join(elem...)
203146
}
@@ -216,35 +159,13 @@ func (fs *Memory) Base() string {
216159
return fs.base
217160
}
218161

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-
229162
type file struct {
230163
billy.BaseFile
231164

232165
content *content
233166
position int64
234167
flag int
235168
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-
}
248169
}
249170

250171
func (f *file) Read(b []byte) (int, error) {
@@ -312,28 +233,39 @@ func (f *file) Close() error {
312233
return nil
313234
}
314235

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+
}
319243

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+
}
326251

327-
func newFileInfo(base, fullpath string, mode os.FileMode, size int) *fileInfo {
328-
filename, _ := filepath.Rel(base, fullpath)
252+
return new
253+
}
329254

255+
func (f *file) Stat() billy.FileInfo {
330256
return &fileInfo{
331-
name: filename,
332-
mode: mode,
333-
size: size,
257+
name: f.Filename(),
258+
mode: f.mode,
259+
size: f.content.Len(),
334260
}
335261
}
336262

263+
type fileInfo struct {
264+
name string
265+
size int
266+
mode os.FileMode
267+
}
268+
337269
func (fi *fileInfo) Name() string {
338270
return fi.name
339271
}
@@ -351,46 +283,13 @@ func (*fileInfo) ModTime() time.Time {
351283
}
352284

353285
func (fi *fileInfo) IsDir() bool {
354-
return fi.isDir
286+
return fi.mode.IsDir()
355287
}
356288

357289
func (*fileInfo) Sys() interface{} {
358290
return nil
359291
}
360292

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-
394293
func (c *content) Truncate() {
395294
c.bytes = make([]byte, 0)
396295
}
@@ -422,19 +321,3 @@ func isReadOnly(flag int) bool {
422321
func isWriteOnly(flag int) bool {
423322
return flag&os.O_WRONLY != 0
424323
}
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

Comments
 (0)