|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package downloader
-
- import (
- "sort"
-
- lru "github.com/hashicorp/golang-lru/v2"
- "gitlink.org.cn/cloudream/common/utils/lo2"
- )
-
- type LRUID int64
-
- type cacheFile struct {
- Segments []*fileSegment
- }
-
- type fileSegment struct {
- LRUID LRUID
- Offset int64
- Data []byte
- }
-
- type lruEntry struct {
- FileHash string
- Offset int64
- }
-
- type Cache struct {
- lru *lru.Cache[LRUID, lruEntry]
- nextLRUID LRUID
- files map[string]*cacheFile
- }
-
- func NewCache(size int) *Cache {
- c := &Cache{
- files: make(map[string]*cacheFile),
- }
-
- lru, _ := lru.NewWithEvict(size, c.onEvict)
- c.lru = lru
-
- return c
- }
-
- func (c *Cache) Put(fileHash string, offset int64, data []byte) {
- file, ok := c.files[fileHash]
- if !ok {
- file = &cacheFile{}
- c.files[fileHash] = file
- }
-
- idx := sort.Search(len(file.Segments), upperBound(file.Segments, offset))
-
- // 允许不同Segment之间有重叠,只在Offset相等时替换数据
- if idx < len(file.Segments) && file.Segments[idx].Offset == offset {
- file.Segments[idx].Data = data
- // Get一下更新LRU
- c.lru.Get(file.Segments[idx].LRUID)
- } else {
- file.Segments = lo2.Insert(file.Segments, idx, &fileSegment{
- LRUID: c.nextLRUID,
- Offset: offset,
- Data: data,
- })
- c.lru.Add(c.nextLRUID, lruEntry{
- FileHash: fileHash,
- Offset: offset,
- })
- c.nextLRUID++
- }
- }
-
- func (c *Cache) Get(fileHash string, offset int64) []byte {
- file, ok := c.files[fileHash]
- if !ok {
- return nil
- }
-
- idx := sort.Search(len(file.Segments), upperBound(file.Segments, offset))
- if idx == 0 {
- return nil
- }
- seg := file.Segments[idx-1]
- // Get一下更新LRU
- c.lru.Get(seg.LRUID)
-
- return seg.Data[offset-seg.Offset:]
- }
-
- func (c *Cache) onEvict(key LRUID, value lruEntry) {
- // 不应该找不到文件或者分片
- file := c.files[value.FileHash]
- idx := sort.Search(len(file.Segments), upperBound(file.Segments, value.Offset))
- file.Segments = lo2.RemoveAt(file.Segments, idx)
- if len(file.Segments) == 0 {
- delete(c.files, value.FileHash)
- }
- }
-
- // 使用此函数会找到第一个大于等于 target 的索引,如果找不到,则返回 len(seg)
- func upperBound(seg []*fileSegment, target int64) func(int) bool {
- return func(i int) bool {
- return seg[i].Offset >= target
- }
- }
|