|
@@ -386,7 +386,6 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
|
|
|
}
|
|
|
|
|
|
type defaultRealCaser struct {
|
|
|
- fs Filesystem
|
|
|
cache caseCache
|
|
|
}
|
|
|
|
|
@@ -396,13 +395,12 @@ func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
|
|
|
if err != nil {
|
|
|
panic(err)
|
|
|
}
|
|
|
- caser := &defaultRealCaser{
|
|
|
- fs: fs,
|
|
|
+ return &defaultRealCaser{
|
|
|
cache: caseCache{
|
|
|
+ fs: fs,
|
|
|
TwoQueueCache: cache,
|
|
|
},
|
|
|
}
|
|
|
- return caser
|
|
|
}
|
|
|
|
|
|
func (r *defaultRealCaser) realCase(name string) (string, error) {
|
|
@@ -414,27 +412,6 @@ func (r *defaultRealCaser) realCase(name string) (string, error) {
|
|
|
for _, comp := range PathComponents(name) {
|
|
|
node := r.cache.getExpireAdd(realName)
|
|
|
|
|
|
- node.once.Do(func() {
|
|
|
- dirNames, err := r.fs.DirNames(realName)
|
|
|
- if err != nil {
|
|
|
- r.cache.Remove(realName)
|
|
|
- node.err = err
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- num := len(dirNames)
|
|
|
- node.children = make(map[string]struct{}, num)
|
|
|
- node.lowerToReal = make(map[string]string, num)
|
|
|
- lastLower := ""
|
|
|
- for _, n := range dirNames {
|
|
|
- node.children[n] = struct{}{}
|
|
|
- lower := UnicodeLowercaseNormalized(n)
|
|
|
- if lower != lastLower {
|
|
|
- node.lowerToReal[lower] = n
|
|
|
- lastLower = n
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
if node.err != nil {
|
|
|
return "", node.err
|
|
|
}
|
|
@@ -457,10 +434,29 @@ func (r *defaultRealCaser) dropCache() {
|
|
|
r.cache.Purge()
|
|
|
}
|
|
|
|
|
|
-func newCaseNode() *caseNode {
|
|
|
- return &caseNode{
|
|
|
- expires: time.Now().Add(caseCacheTimeout),
|
|
|
+type caseCache struct {
|
|
|
+ *lru.TwoQueueCache
|
|
|
+ fs Filesystem
|
|
|
+ mut sync.Mutex
|
|
|
+}
|
|
|
+
|
|
|
+// getExpireAdd gets an entry for the given key. If no entry exists, or it is
|
|
|
+// expired a new one is created and added to the cache.
|
|
|
+func (c *caseCache) getExpireAdd(key string) *caseNode {
|
|
|
+ c.mut.Lock()
|
|
|
+ defer c.mut.Unlock()
|
|
|
+ v, ok := c.Get(key)
|
|
|
+ if !ok {
|
|
|
+ node := newCaseNode(key, c.fs)
|
|
|
+ c.Add(key, node)
|
|
|
+ return node
|
|
|
+ }
|
|
|
+ node := v.(*caseNode)
|
|
|
+ if node.expires.Before(time.Now()) {
|
|
|
+ node = newCaseNode(key, c.fs)
|
|
|
+ c.Add(key, node)
|
|
|
}
|
|
|
+ return node
|
|
|
}
|
|
|
|
|
|
// The keys to children are "real", case resolved names of the path
|
|
@@ -474,30 +470,32 @@ type caseNode struct {
|
|
|
expires time.Time
|
|
|
lowerToReal map[string]string
|
|
|
children map[string]struct{}
|
|
|
- once sync.Once
|
|
|
err error
|
|
|
}
|
|
|
|
|
|
-type caseCache struct {
|
|
|
- *lru.TwoQueueCache
|
|
|
- mut sync.Mutex
|
|
|
-}
|
|
|
-
|
|
|
-// getExpireAdd gets an entry for the given key. If no entry exists, or it is
|
|
|
-// expired a new one is created and added to the cache.
|
|
|
-func (c *caseCache) getExpireAdd(key string) *caseNode {
|
|
|
- c.mut.Lock()
|
|
|
- defer c.mut.Unlock()
|
|
|
- v, ok := c.Get(key)
|
|
|
- if !ok {
|
|
|
- node := newCaseNode()
|
|
|
- c.Add(key, node)
|
|
|
+func newCaseNode(name string, filesystem Filesystem) *caseNode {
|
|
|
+ node := new(caseNode)
|
|
|
+ dirNames, err := filesystem.DirNames(name)
|
|
|
+ // Set expiry after calling DirNames in case this is super-slow
|
|
|
+ // (e.g. dirs with many children on android)
|
|
|
+ node.expires = time.Now().Add(caseCacheTimeout)
|
|
|
+ if err != nil {
|
|
|
+ node.err = err
|
|
|
return node
|
|
|
}
|
|
|
- node := v.(*caseNode)
|
|
|
- if node.expires.Before(time.Now()) {
|
|
|
- node = newCaseNode()
|
|
|
- c.Add(key, node)
|
|
|
+
|
|
|
+ num := len(dirNames)
|
|
|
+ node.children = make(map[string]struct{}, num)
|
|
|
+ node.lowerToReal = make(map[string]string, num)
|
|
|
+ lastLower := ""
|
|
|
+ for _, n := range dirNames {
|
|
|
+ node.children[n] = struct{}{}
|
|
|
+ lower := UnicodeLowercaseNormalized(n)
|
|
|
+ if lower != lastLower {
|
|
|
+ node.lowerToReal[lower] = n
|
|
|
+ lastLower = n
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return node
|
|
|
}
|