github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/partial_ranges.go (about) 1 // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. 2 3 package caches 4 5 import ( 6 "bytes" 7 "encoding/json" 8 "github.com/iwind/TeaGo/types" 9 "strconv" 10 ) 11 12 // PartialRanges 内容分区范围定义 13 type PartialRanges struct { 14 Version int `json:"version"` // 版本号 15 Ranges [][2]int64 `json:"ranges"` // 范围 16 BodySize int64 `json:"bodySize"` // 总长度 17 } 18 19 // NewPartialRanges 获取新对象 20 func NewPartialRanges(expiresAt int64) *PartialRanges { 21 return &PartialRanges{ 22 Ranges: [][2]int64{}, 23 Version: 1, 24 } 25 } 26 27 // NewPartialRangesFromData 从数据中解析范围 28 func NewPartialRangesFromData(data []byte) (*PartialRanges, error) { 29 var rs = NewPartialRanges(0) 30 for { 31 var index = bytes.IndexRune(data, '\n') 32 if index < 0 { 33 break 34 } 35 var line = data[:index] 36 var colonIndex = bytes.IndexRune(line, ':') 37 if colonIndex > 0 { 38 switch string(line[:colonIndex]) { 39 case "v": // 版本号 40 rs.Version = types.Int(line[colonIndex+1:]) 41 case "b": // 总长度 42 rs.BodySize = types.Int64(line[colonIndex+1:]) 43 case "r": // 范围信息 44 var commaIndex = bytes.IndexRune(line, ',') 45 if commaIndex > 0 { 46 rs.Ranges = append(rs.Ranges, [2]int64{types.Int64(line[colonIndex+1 : commaIndex]), types.Int64(line[commaIndex+1:])}) 47 } 48 } 49 } 50 data = data[index+1:] 51 if len(data) == 0 { 52 break 53 } 54 } 55 return rs, nil 56 } 57 58 // NewPartialRangesFromJSON 从JSON中解析范围 59 func NewPartialRangesFromJSON(data []byte) (*PartialRanges, error) { 60 var rs = NewPartialRanges(0) 61 err := json.Unmarshal(data, &rs) 62 if err != nil { 63 return nil, err 64 } 65 rs.Version = 0 66 67 return rs, nil 68 } 69 70 // NewPartialRangesFromFile 从文件中加载范围信息 71 func NewPartialRangesFromFile(path string) (*PartialRanges, error) { 72 data, err := SharedPartialRangesQueue.Get(path) 73 if err != nil { 74 return nil, err 75 } 76 if len(data) == 0 { 77 return NewPartialRanges(0), nil 78 } 79 80 // 兼容老的JSON格式 81 if data[0] == '{' { 82 return NewPartialRangesFromJSON(data) 83 } 84 85 // 新的格式 86 return NewPartialRangesFromData(data) 87 } 88 89 // Add 添加新范围 90 func (this *PartialRanges) Add(begin int64, end int64) { 91 if begin > end { 92 begin, end = end, begin 93 } 94 95 var nr = [2]int64{begin, end} 96 97 var count = len(this.Ranges) 98 if count == 0 { 99 this.Ranges = [][2]int64{nr} 100 return 101 } 102 103 // insert 104 // TODO 将来使用二分法改进 105 var index = -1 106 for i, r := range this.Ranges { 107 if r[0] > begin || (r[0] == begin && r[1] >= end) { 108 index = i 109 this.Ranges = append(this.Ranges, [2]int64{}) 110 copy(this.Ranges[index+1:], this.Ranges[index:]) 111 this.Ranges[index] = nr 112 break 113 } 114 } 115 116 if index == -1 { 117 index = count 118 this.Ranges = append(this.Ranges, nr) 119 } 120 121 this.merge(index) 122 } 123 124 // Contains 检查是否包含某个范围 125 func (this *PartialRanges) Contains(begin int64, end int64) bool { 126 if len(this.Ranges) == 0 { 127 return false 128 } 129 130 // TODO 使用二分法查找改进性能 131 for _, r2 := range this.Ranges { 132 if r2[0] <= begin && r2[1] >= end { 133 return true 134 } 135 } 136 137 return false 138 } 139 140 // Nearest 查找最近的某个范围 141 func (this *PartialRanges) Nearest(begin int64, end int64) (r [2]int64, ok bool) { 142 if len(this.Ranges) == 0 { 143 return 144 } 145 146 // TODO 使用二分法查找改进性能 147 for _, r2 := range this.Ranges { 148 if r2[0] <= begin && r2[1] > begin { 149 r = [2]int64{begin, this.min(end, r2[1])} 150 ok = true 151 return 152 } 153 } 154 return 155 } 156 157 // 转换为字符串 158 func (this *PartialRanges) String() string { 159 var s = "v:" + strconv.Itoa(this.Version) + "\n" + // version 160 "b:" + this.formatInt64(this.BodySize) + "\n" // bodySize 161 for _, r := range this.Ranges { 162 s += "r:" + this.formatInt64(r[0]) + "," + this.formatInt64(r[1]) + "\n" // range 163 } 164 return s 165 } 166 167 // Bytes 将内容转换为字节 168 func (this *PartialRanges) Bytes() []byte { 169 return []byte(this.String()) 170 } 171 172 // WriteToFile 写入到文件中 173 func (this *PartialRanges) WriteToFile(path string) error { 174 SharedPartialRangesQueue.Put(path, this.Bytes()) 175 return nil 176 } 177 178 // Max 获取最大位置 179 func (this *PartialRanges) Max() int64 { 180 if len(this.Ranges) > 0 { 181 return this.Ranges[len(this.Ranges)-1][1] 182 } 183 return 0 184 } 185 186 // Reset 重置范围信息 187 func (this *PartialRanges) Reset() { 188 this.Ranges = [][2]int64{} 189 } 190 191 // IsCompleted 是否已下载完整 192 func (this *PartialRanges) IsCompleted() bool { 193 return len(this.Ranges) == 1 && this.Ranges[0][0] == 0 && this.Ranges[0][1] == this.BodySize-1 194 } 195 196 func (this *PartialRanges) merge(index int) { 197 // forward 198 var lastIndex = index 199 for i := index; i >= 1; i-- { 200 var curr = this.Ranges[i] 201 var prev = this.Ranges[i-1] 202 var w1 = this.w(curr) 203 var w2 = this.w(prev) 204 if w1+w2 >= this.max(curr[1], prev[1])-this.min(curr[0], prev[0])-1 { 205 prev = [2]int64{this.min(curr[0], prev[0]), this.max(curr[1], prev[1])} 206 this.Ranges[i-1] = prev 207 this.Ranges = append(this.Ranges[:i], this.Ranges[i+1:]...) 208 lastIndex = i - 1 209 } else { 210 break 211 } 212 } 213 214 // backward 215 index = lastIndex 216 for index < len(this.Ranges)-1 { 217 var curr = this.Ranges[index] 218 var next = this.Ranges[index+1] 219 var w1 = this.w(curr) 220 var w2 = this.w(next) 221 if w1+w2 >= this.max(curr[1], next[1])-this.min(curr[0], next[0])-1 { 222 curr = [2]int64{this.min(curr[0], next[0]), this.max(curr[1], next[1])} 223 this.Ranges = append(this.Ranges[:index], this.Ranges[index+1:]...) 224 this.Ranges[index] = curr 225 } else { 226 break 227 } 228 } 229 } 230 231 func (this *PartialRanges) w(r [2]int64) int64 { 232 return r[1] - r[0] 233 } 234 235 func (this *PartialRanges) min(n1 int64, n2 int64) int64 { 236 if n1 <= n2 { 237 return n1 238 } 239 return n2 240 } 241 242 func (this *PartialRanges) max(n1 int64, n2 int64) int64 { 243 if n1 >= n2 { 244 return n1 245 } 246 return n2 247 } 248 249 func (this *PartialRanges) formatInt64(i int64) string { 250 return strconv.FormatInt(i, 10) 251 }