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  }