Files
zzet--gortex/internal/indexer/cpp_include_cache.go
T
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

132 lines
4.1 KiB
Go

package indexer
import (
"container/list"
"os"
"strconv"
"strings"
"sync"
)
// cppIncludeDirCacheT is a memory-bounded LRU over per-repo-root compile-DB
// include-dir sets. Unlike the resolver's per-pass maps (cleared between passes
// and intentionally uncapped — bounded by the pass work set), this cache is
// long-lived: one entry per tracked repo root, surviving across reindexes. It
// is bounded by GORTEX_RESOLVER_CACHE_MAX_MB; a zero / unset budget (the
// default) leaves it unbounded, preserving the prior plain-map behavior.
type cppIncludeDirCacheT struct {
mu sync.Mutex
ll *list.List // front = most-recently-used
m map[string]*list.Element
maxBytes int64 // 0 = unbounded
curBytes int64
}
type cppIncludeCacheEntry struct {
key string
tus map[string]cppTU
bytes int64
// mtime is the newest modtime (unix nanoseconds) across the compile_commands.json
// files this entry was built from. A lookup whose on-disk files are newer than
// this is treated as a miss, so an isolated edit to compile_commands.json is
// picked up on the next load without a full reindex.
mtime int64
}
// newCppIncludeDirCache builds the cache, reading GORTEX_RESOLVER_CACHE_MAX_MB
// (MiB; <= 0 / unset = unbounded) for its memory budget.
func newCppIncludeDirCache() *cppIncludeDirCacheT {
c := &cppIncludeDirCacheT{ll: list.New(), m: map[string]*list.Element{}}
if v := strings.TrimSpace(os.Getenv("GORTEX_RESOLVER_CACHE_MAX_MB")); v != "" {
if n, err := strconv.Atoi(v); err == nil && n > 0 {
c.maxBytes = int64(n) << 20
}
}
return c
}
// get returns the cached include-dir set for root, promoting it to
// most-recently-used. diskMtime is the newest modtime currently on disk across
// the compile_commands.json files for root; an entry whose recorded mtime is
// older is treated as stale (a miss) so an edited compile_commands.json is
// re-read on the next load without a full reindex. The second return is false on
// a miss.
func (c *cppIncludeDirCacheT) get(root string, diskMtime int64) (map[string]cppTU, bool) {
c.mu.Lock()
defer c.mu.Unlock()
el, ok := c.m[root]
if !ok {
return nil, false
}
e := el.Value.(*cppIncludeCacheEntry)
if diskMtime > e.mtime {
return nil, false
}
c.ll.MoveToFront(el)
return e.tus, true
}
// put stores the include-dir set for root along with the modtime it was built
// from, evicting least-recently-used entries until within the memory budget (at
// least the just-stored entry is retained).
func (c *cppIncludeDirCacheT) put(root string, tus map[string]cppTU, mtime int64) {
sz := cppTUMapBytes(tus)
c.mu.Lock()
defer c.mu.Unlock()
if el, ok := c.m[root]; ok {
e := el.Value.(*cppIncludeCacheEntry)
c.curBytes += sz - e.bytes
e.tus = tus
e.bytes = sz
e.mtime = mtime
c.ll.MoveToFront(el)
c.evictLocked()
return
}
el := c.ll.PushFront(&cppIncludeCacheEntry{key: root, tus: tus, bytes: sz, mtime: mtime})
c.m[root] = el
c.curBytes += sz
c.evictLocked()
}
// clear drops the cached include-dir set for root so the next load re-reads
// compile_commands.json.
func (c *cppIncludeDirCacheT) clear(root string) {
c.mu.Lock()
defer c.mu.Unlock()
if el, ok := c.m[root]; ok {
c.ll.Remove(el)
delete(c.m, root)
c.curBytes -= el.Value.(*cppIncludeCacheEntry).bytes
}
}
// evictLocked drops least-recently-used entries until within the byte budget,
// always retaining the most-recently-used entry (so a freshly-loaded set stays
// available to its caller). A zero budget disables eviction. Caller holds c.mu.
func (c *cppIncludeDirCacheT) evictLocked() {
if c.maxBytes <= 0 {
return
}
for c.curBytes > c.maxBytes && c.ll.Len() > 1 {
back := c.ll.Back()
e := back.Value.(*cppIncludeCacheEntry)
c.ll.Remove(back)
delete(c.m, e.key)
c.curBytes -= e.bytes
}
}
// cppTUMapBytes estimates the retained size of one repo's include-dir set for
// the cache's byte accounting (string content plus per-entry map overhead).
func cppTUMapBytes(tus map[string]cppTU) int64 {
var n int64
for k, tu := range tus {
n += int64(len(k)) + int64(len(tu.file)) + 64
for _, d := range tu.includeDirs {
n += int64(len(d)) + 16
}
}
return n
}