Commit Diff


commit - e5a62a4140f5672204ab4c7774683b13c6b61b3c
commit + 945b3fc34aaeca2019e8045bb23d2c1c5fe62539
blob - 3a531ebc795d1e6953d190f8568f80d575231fb7
blob + ea3453f98d1c04f44361e39ac177fbc6f0036798
--- jira/fs.go
+++ jira/fs.go
@@ -7,12 +7,14 @@ import (
 	"os"
 	"path"
 	"strings"
+	"sync"
 	"time"
 )
 
 type FS struct {
 	Client *Client
 	root   *fid
+	cache  *dirCache
 }
 
 const (
@@ -250,6 +252,15 @@ func (fsys *FS) Open(name string) (fs.File, error) {
 			return nil, fmt.Errorf("make root file: %w", err)
 		}
 	}
+	if fsys.cache == nil {
+		var err error
+		fsys.cache = &dirCache{}
+		fsys.cache.root, err = makeRoot(fsys.Client)
+		if err != nil {
+			return nil, fmt.Errorf("make cache root file: %w", err)
+		}
+	}
+
 	if fsys.Client.Debug {
 		fmt.Fprintln(os.Stderr, "open", name)
 	}
@@ -264,13 +275,23 @@ func (fsys *FS) Open(name string) (fs.File, error) {
 		elems = elems[1:]
 	}
 
+	if f := fsys.cache.lookup(elems); f != nil {
+		if fsys.Client.Debug {
+			fmt.Fprintln(os.Stderr, "cachehit", name)
+		}
+		g := *f
+		g.children = nil
+		return &g, nil
+	}
+
 	f := fsys.root
-	for _, elem := range elems {
+	for i, elem := range elems {
 		dir, err := find(f, elem)
 		if err != nil {
 			return nil, &fs.PathError{"open", name, err}
 		}
 		f = dir
+		fsys.cache.put(elems[:i], *dir)
 	}
 	g := *f
 	return &g, nil
@@ -297,6 +318,50 @@ func makeRoot(client *Client) (*fid, error) {
 	return root, nil
 }
 
+type dirCache struct {
+	mu   sync.Mutex
+	root *fid
+}
+
+func (cache *dirCache) lookup(wnames []string) *fid {
+	cache.mu.Lock()
+	defer cache.mu.Unlock()
+
+	next := cache.root
+	for _, name := range wnames {
+		match := qfind(next, name)
+		if match == nil {
+			return nil
+		}
+		next = match
+	}
+	return next
+}
+
+func (cache *dirCache) put(wnames []string, f fid) (ok bool) {
+	parent := cache.lookup(wnames)
+	if parent == nil {
+		return false
+	}
+
+	cache.mu.Lock()
+	defer cache.mu.Unlock()
+	parent.children = append(parent.children, &f)
+	return true
+}
+
+func qfind(dir *fid, name string) *fid {
+	if !dir.IsDir() {
+		return nil
+	}
+	for _, d := range dir.children {
+		if d.Name() == name {
+			return d.(*fid)
+		}
+	}
+	return nil
+}
+
 func find(dir *fid, name string) (*fid, error) {
 	if !dir.IsDir() {
 		return nil, fs.ErrNotExist