Skip to content

Commit

Permalink
rename mem fs with descendants
Browse files Browse the repository at this point in the history
  • Loading branch information
hanagantig committed Jun 25, 2022
1 parent 100c9a6 commit 51b1016
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 11 deletions.
73 changes: 62 additions & 11 deletions memmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -87,6 +88,22 @@ func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
return pfile
}

func (m *MemMapFs) findDescendants(name string) []*mem.FileData {
fData := m.getData()
descendants := make([]*mem.FileData, 0, len(fData))
for p, dFile := range fData {
if strings.HasPrefix(p, name+FilePathSeparator) {
descendants = append(descendants, dFile)
}
}

sort.Slice(descendants, func(i, j int) bool {
return descendants[i].Name() < descendants[j].Name()
})

return descendants
}

func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {
if f == nil {
return
Expand Down Expand Up @@ -290,30 +307,64 @@ func (m *MemMapFs) RemoveAll(path string) error {
return nil
}

func (m *MemMapFs) Rename(oldname, newname string) error {
oldname = normalizePath(oldname)
newname = normalizePath(newname)
func (m *MemMapFs) Rename(oldName, newName string) error {
oldName = normalizePath(oldName)
newName = normalizePath(newName)

if oldname == newname {
if oldName == newName {
return nil
}

m.mu.RLock()
defer m.mu.RUnlock()
if _, ok := m.getData()[oldname]; ok {
if _, ok := m.getData()[oldName]; ok {
m.mu.RUnlock()
m.mu.Lock()
m.unRegisterWithParent(oldname)
fileData := m.getData()[oldname]
delete(m.getData(), oldname)
mem.ChangeFileName(fileData, newname)
m.getData()[newname] = fileData
err := m.unRegisterWithParent(oldName)
if err != nil {
return err
}

fileData := m.getData()[oldName]
mem.ChangeFileName(fileData, newName)
m.getData()[newName] = fileData

err = m.renameDescendants(oldName, newName)
if err != nil {
return err
}

delete(m.getData(), oldName)

m.registerWithParent(fileData, 0)
m.mu.Unlock()
m.mu.RLock()
} else {
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
return &os.PathError{Op: "rename", Path: oldName, Err: ErrFileNotFound}
}
return nil
}

func (m *MemMapFs) renameDescendants(oldName, newName string) error {
descendants := m.findDescendants(oldName)
removes := make([]string, 0, len(descendants))
for _, desc := range descendants {
descNewName := strings.Replace(desc.Name(), oldName, newName, 1)
err := m.unRegisterWithParent(desc.Name())
if err != nil {
return err
}

removes = append(removes, desc.Name())
mem.ChangeFileName(desc, descNewName)
m.getData()[descNewName] = desc

m.registerWithParent(desc, 0)
}
for _, r := range removes {
delete(m.getData(), r)
}

return nil
}

Expand Down
86 changes: 86 additions & 0 deletions memmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -692,3 +693,88 @@ func TestMemFsLstatIfPossible(t *testing.T) {
t.Fatalf("Function indicated lstat was called. This should never be true.")
}
}

func TestMemMapFsRename(t *testing.T) {
t.Parallel()

fs := &MemMapFs{}
tDir := testDir(fs)
rFrom := "/renamefrom"
rTo := "/renameto"
rExists := "/renameexists"

type test struct {
dirs []string
from string
to string
exists string
}

parts := strings.Split(tDir, "/")
root := "/"
if len(parts) > 1 {
root = filepath.Join("/", parts[1])
}

testData := make([]test, 0, len(parts))

i := len(parts)
for i > 0 {
prefix := strings.Join(parts[:i], "/")
suffix := strings.Join(parts[i:], "/")
testData = append(testData, test{
dirs: []string{
filepath.Join(prefix, rFrom, suffix),
filepath.Join(prefix, rExists, suffix),
},
from: filepath.Join(prefix, rFrom),
to: filepath.Join(prefix, rTo),
exists: filepath.Join(prefix, rExists),
})
i--
}

for _, data := range testData {
err := fs.RemoveAll(root)
if err != nil {
t.Fatalf("%s: RemoveAll %q failed: %v", fs.Name(), root, err)
}

for _, dir := range data.dirs {
err = fs.MkdirAll(dir, os.FileMode(0775))
if err != nil {
t.Fatalf("%s: MkdirAll %q failed: %v", fs.Name(), dir, err)
}
}

dataCnt := len(fs.getData())
err = fs.Rename(data.from, data.to)
if err != nil {
t.Fatalf("%s: rename %q, %q failed: %v", fs.Name(), data.from, data.to, err)
}
err = fs.Mkdir(data.from, os.FileMode(0775))
if err != nil {
t.Fatalf("%s: Mkdir %q failed: %v", fs.Name(), data.from, err)
}

err = fs.Rename(data.from, data.exists)
if err != nil {
t.Errorf("%s: rename %q, %q failed: %v", fs.Name(), data.from, data.exists, err)
}

for p, _ := range fs.getData() {
if strings.Contains(p, data.from) {
t.Errorf("File was not renamed to renameto: %v", p)
}
}

_, err = fs.Stat(data.to)
if err != nil {
t.Errorf("%s: stat %q failed: %v", fs.Name(), data.to, err)
}

if dataCnt != len(fs.getData()) {
t.Errorf("invalid data len: expected %v, get %v", dataCnt, len(fs.getData()))
}
}
}

0 comments on commit 51b1016

Please sign in to comment.