sing-box/experimental/clashapi/cachefile/cache.go

162 lines
3.7 KiB
Go
Raw Normal View History

2022-09-10 06:40:16 +00:00
package cachefile
import (
2023-07-08 08:08:46 +00:00
"net/netip"
2022-09-10 06:40:16 +00:00
"os"
2023-07-19 12:40:34 +00:00
"strings"
2023-07-08 08:08:46 +00:00
"sync"
2022-09-10 06:40:16 +00:00
"time"
"github.com/sagernet/sing-box/adapter"
"go.etcd.io/bbolt"
)
2023-08-12 11:33:43 +00:00
var (
bucketSelected = []byte("selected")
bucketExpand = []byte("group_expand")
)
2022-09-10 06:40:16 +00:00
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
type CacheFile struct {
2023-08-07 13:53:19 +00:00
DB *bbolt.DB
cacheID []byte
saveAccess sync.RWMutex
saveDomain map[netip.Addr]string
saveAddress4 map[string]netip.Addr
saveAddress6 map[string]netip.Addr
saveMetadataTimer *time.Timer
2022-09-10 06:40:16 +00:00
}
func Open(path string, cacheID string) (*CacheFile, error) {
2022-09-10 06:40:16 +00:00
const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second}
db, err := bbolt.Open(path, fileMode, &options)
switch err {
case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:
if err = os.Remove(path); err != nil {
break
}
db, err = bbolt.Open(path, 0o666, &options)
}
if err != nil {
return nil, err
}
var cacheIDBytes []byte
if cacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
}
2023-07-19 12:40:34 +00:00
err = db.Batch(func(tx *bbolt.Tx) error {
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
2023-07-24 08:50:26 +00:00
if name[0] == 0 {
return b.ForEachBucket(func(k []byte) error {
bucketName := string(k)
2023-08-12 11:33:43 +00:00
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand)) {
_ = b.DeleteBucket(name)
2023-07-24 08:50:26 +00:00
}
return nil
})
} else {
bucketName := string(name)
2023-08-12 11:33:43 +00:00
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
_ = tx.DeleteBucket(name)
2023-07-19 12:40:34 +00:00
}
}
return nil
})
})
2023-07-20 11:51:04 +00:00
if err != nil {
return nil, err
}
2023-07-08 08:08:46 +00:00
return &CacheFile{
2023-07-20 11:51:04 +00:00
DB: db,
cacheID: cacheIDBytes,
saveDomain: make(map[netip.Addr]string),
saveAddress4: make(map[string]netip.Addr),
saveAddress6: make(map[string]netip.Addr),
2023-07-08 08:08:46 +00:00
}, nil
}
func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
if c.cacheID == nil {
return t.Bucket(key)
}
bucket := t.Bucket(c.cacheID)
if bucket == nil {
return nil
}
return bucket.Bucket(key)
}
func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
if c.cacheID == nil {
return t.CreateBucketIfNotExists(key)
}
bucket, err := t.CreateBucketIfNotExists(c.cacheID)
if bucket == nil {
return nil, err
}
return bucket.CreateBucketIfNotExists(key)
2022-09-10 06:40:16 +00:00
}
func (c *CacheFile) LoadSelected(group string) string {
var selected string
c.DB.View(func(t *bbolt.Tx) error {
bucket := c.bucket(t, bucketSelected)
2022-09-10 06:40:16 +00:00
if bucket == nil {
return nil
}
selectedBytes := bucket.Get([]byte(group))
if len(selectedBytes) > 0 {
selected = string(selectedBytes)
}
return nil
})
return selected
}
func (c *CacheFile) StoreSelected(group, selected string) error {
return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := c.createBucket(t, bucketSelected)
2022-09-10 06:40:16 +00:00
if err != nil {
return err
}
return bucket.Put([]byte(group), []byte(selected))
})
}
2022-12-02 13:57:10 +00:00
2023-08-12 11:33:43 +00:00
func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
c.DB.View(func(t *bbolt.Tx) error {
bucket := c.bucket(t, bucketExpand)
if bucket == nil {
return nil
}
expandBytes := bucket.Get([]byte(group))
if len(expandBytes) == 1 {
isExpand = expandBytes[0] == 1
loaded = true
}
return nil
})
return
}
func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := c.createBucket(t, bucketExpand)
if err != nil {
return err
}
if isExpand {
return bucket.Put([]byte(group), []byte{1})
} else {
return bucket.Put([]byte(group), []byte{0})
}
})
}
2022-12-02 13:57:10 +00:00
func (c *CacheFile) Close() error {
return c.DB.Close()
}