mirror of
https://github.com/junegunn/fzf.git
synced 2026-04-12 19:04:42 +08:00
Replace the per-chunk query cache from []Result slices to fixed-size bitmaps (ChunkBitmap: [16]uint64 = 128 bytes per entry). Each bit indicates whether the corresponding item in the chunk matched. This reduces cache memory by 86 times in testing: - Old []Result cache: ~22KB per chunk per query (for 500 matches) - New bitmap cache: ~262 bytes per chunk per query (fixed) With the reduced per-entry cost, queryCacheMax is raised from chunkSize/5 to chunkSize/2, allowing broader queries (up to 50% match rate) to be cached while still using far less memory.
99 lines
2.1 KiB
Go
99 lines
2.1 KiB
Go
package fzf
|
|
|
|
import "sync"
|
|
|
|
// ChunkBitmap is a bitmap with one bit per item in a chunk.
|
|
type ChunkBitmap [chunkBitWords]uint64
|
|
|
|
// queryCache associates query strings to bitmaps of matching items
|
|
type queryCache map[string]ChunkBitmap
|
|
|
|
// ChunkCache associates Chunk and query string to bitmaps
|
|
type ChunkCache struct {
|
|
mutex sync.Mutex
|
|
cache map[*Chunk]*queryCache
|
|
}
|
|
|
|
// NewChunkCache returns a new ChunkCache
|
|
func NewChunkCache() *ChunkCache {
|
|
return &ChunkCache{sync.Mutex{}, make(map[*Chunk]*queryCache)}
|
|
}
|
|
|
|
func (cc *ChunkCache) Clear() {
|
|
cc.mutex.Lock()
|
|
cc.cache = make(map[*Chunk]*queryCache)
|
|
cc.mutex.Unlock()
|
|
}
|
|
|
|
func (cc *ChunkCache) retire(chunk ...*Chunk) {
|
|
cc.mutex.Lock()
|
|
for _, c := range chunk {
|
|
delete(cc.cache, c)
|
|
}
|
|
cc.mutex.Unlock()
|
|
}
|
|
|
|
// Add stores the bitmap for the given chunk and key
|
|
func (cc *ChunkCache) Add(chunk *Chunk, key string, bitmap ChunkBitmap, matchCount int) {
|
|
if len(key) == 0 || !chunk.IsFull() || matchCount > queryCacheMax {
|
|
return
|
|
}
|
|
|
|
cc.mutex.Lock()
|
|
defer cc.mutex.Unlock()
|
|
|
|
qc, ok := cc.cache[chunk]
|
|
if !ok {
|
|
cc.cache[chunk] = &queryCache{}
|
|
qc = cc.cache[chunk]
|
|
}
|
|
(*qc)[key] = bitmap
|
|
}
|
|
|
|
// Lookup returns the bitmap for the exact key
|
|
func (cc *ChunkCache) Lookup(chunk *Chunk, key string) *ChunkBitmap {
|
|
if len(key) == 0 || !chunk.IsFull() {
|
|
return nil
|
|
}
|
|
|
|
cc.mutex.Lock()
|
|
defer cc.mutex.Unlock()
|
|
|
|
qc, ok := cc.cache[chunk]
|
|
if ok {
|
|
if bm, ok := (*qc)[key]; ok {
|
|
return &bm
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Search finds the bitmap for the longest prefix or suffix of the key
|
|
func (cc *ChunkCache) Search(chunk *Chunk, key string) *ChunkBitmap {
|
|
if len(key) == 0 || !chunk.IsFull() {
|
|
return nil
|
|
}
|
|
|
|
cc.mutex.Lock()
|
|
defer cc.mutex.Unlock()
|
|
|
|
qc, ok := cc.cache[chunk]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
for idx := 1; idx < len(key); idx++ {
|
|
// [---------| ] | [ |---------]
|
|
// [--------| ] | [ |--------]
|
|
// [-------| ] | [ |-------]
|
|
prefix := key[:len(key)-idx]
|
|
suffix := key[idx:]
|
|
for _, substr := range [2]string{prefix, suffix} {
|
|
if bm, found := (*qc)[substr]; found {
|
|
return &bm
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|