commit 945b3fc34aaeca2019e8045bb23d2c1c5fe62539 from: Oliver Lowe date: Wed Jun 11 03:52:42 2025 UTC jira: cache dir entries The cache assumes files will never be removed which is incorrect. But clients - the Jira program - can just restart to clear the cache 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