package corebin import ( "strings" "sync" ) var ( mu sync.Mutex cache = map[string]string{} ) // CacheKey identifies a resolved core binary for a binDir + protocol. func CacheKey(binDir, proto string) string { return binDir + "\x00" + proto } // VirtualPath reports paths that are not on-disk binaries (e.g. embedded AWG). func VirtualPath(path string) bool { return strings.HasPrefix(path, "embedded-") } // Get returns a cached absolute path if present. func Get(key string) (string, bool) { mu.Lock() defer mu.Unlock() p, ok := cache[key] return p, ok } // Set stores a resolved path. func Set(key, path string) { if key == "" || path == "" { return } mu.Lock() cache[key] = path mu.Unlock() } // Invalidate clears all cached paths (call after EnsureCore / binDir change / failed Start). func Invalidate() { mu.Lock() cache = map[string]string{} mu.Unlock() } // Resolve caches the result of fn under key. // Hot-path polls trust the cache until Invalidate (no per-poll os.Stat). // Embedded/virtual paths never touch the filesystem. func Resolve(key string, fn func() (string, error)) (string, error) { if path, ok := Get(key); ok { return path, nil } path, err := fn() if err != nil { return "", err } Set(key, path) return path, nil } // ProtoKey normalizes protocol to the shared binary cache key (xray covers vless/vmess/trojan). func ProtoKey(proto string) string { switch proto { case "hysteria2": return "hysteria2" case "awg": return "awg" case "vless", "vmess", "trojan", "xray": return "xray" default: return "naive" } }