You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.go 2.2 kB

7 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package downloader
  2. import (
  3. "sort"
  4. lru "github.com/hashicorp/golang-lru/v2"
  5. "gitlink.org.cn/cloudream/common/utils/lo2"
  6. )
  7. type LRUID int64
  8. type cacheFile struct {
  9. Segments []*fileSegment
  10. }
  11. type fileSegment struct {
  12. LRUID LRUID
  13. Offset int64
  14. Data []byte
  15. }
  16. type lruEntry struct {
  17. FileHash string
  18. Offset int64
  19. }
  20. type Cache struct {
  21. lru *lru.Cache[LRUID, lruEntry]
  22. nextLRUID LRUID
  23. files map[string]*cacheFile
  24. }
  25. func NewCache(size int) *Cache {
  26. c := &Cache{
  27. files: make(map[string]*cacheFile),
  28. }
  29. lru, _ := lru.NewWithEvict(size, c.onEvict)
  30. c.lru = lru
  31. return c
  32. }
  33. func (c *Cache) Put(fileHash string, offset int64, data []byte) {
  34. file, ok := c.files[fileHash]
  35. if !ok {
  36. file = &cacheFile{}
  37. c.files[fileHash] = file
  38. }
  39. idx := sort.Search(len(file.Segments), upperBound(file.Segments, offset))
  40. // 允许不同Segment之间有重叠,只在Offset相等时替换数据
  41. if idx < len(file.Segments) && file.Segments[idx].Offset == offset {
  42. file.Segments[idx].Data = data
  43. // Get一下更新LRU
  44. c.lru.Get(file.Segments[idx].LRUID)
  45. } else {
  46. file.Segments = lo2.Insert(file.Segments, idx, &fileSegment{
  47. LRUID: c.nextLRUID,
  48. Offset: offset,
  49. Data: data,
  50. })
  51. c.lru.Add(c.nextLRUID, lruEntry{
  52. FileHash: fileHash,
  53. Offset: offset,
  54. })
  55. c.nextLRUID++
  56. }
  57. }
  58. func (c *Cache) Get(fileHash string, offset int64) []byte {
  59. file, ok := c.files[fileHash]
  60. if !ok {
  61. return nil
  62. }
  63. idx := sort.Search(len(file.Segments), upperBound(file.Segments, offset))
  64. if idx == 0 {
  65. return nil
  66. }
  67. seg := file.Segments[idx-1]
  68. // Get一下更新LRU
  69. c.lru.Get(seg.LRUID)
  70. return seg.Data[offset-seg.Offset:]
  71. }
  72. func (c *Cache) onEvict(key LRUID, value lruEntry) {
  73. // 不应该找不到文件或者分片
  74. file := c.files[value.FileHash]
  75. idx := sort.Search(len(file.Segments), upperBound(file.Segments, value.Offset))
  76. file.Segments = lo2.RemoveAt(file.Segments, idx)
  77. if len(file.Segments) == 0 {
  78. delete(c.files, value.FileHash)
  79. }
  80. }
  81. // 使用此函数会找到第一个大于等于 target 的索引,如果找不到,则返回 len(seg)
  82. func upperBound(seg []*fileSegment, target int64) func(int) bool {
  83. return func(i int) bool {
  84. return seg[i].Offset >= target
  85. }
  86. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。