package cache import ( "github.com/samber/lo" "gitlink.org.cn/cloudream/common/utils/lo2" "gitlink.org.cn/cloudream/common/utils/math2" "gitlink.org.cn/cloudream/common/utils/sort2" ) // 查找第一个包含指定位置的段索引。如果所有段都不包含指定位置,那么有以下三种情况: // // 1. pos小于第一个段的位置,返回-1 // // 2. pos大于等于最后一个段的结束位置,返回BuzyCount() - 1 // // 3. pos在段之间的空洞内,那么会返回小于pos的最后一个段 // // 注:2、3情况返回的结果是相同的 func FirstContainsIndex(arr []*Range, pos int64) int { low, high := 0, len(arr)-1 for low <= high { mid := (low + high) / 2 if arr[mid].GetPosition() > pos { high = mid - 1 } else { low = mid + 1 } } return low - 1 } func FirstContains(arr []*Range, pos int64) (*Range, bool) { index := FirstContainsIndex(arr, pos) var ret *Range if index < 0 { return nil, false } ret = arr[index] if ret.End() <= pos { return nil, false } return ret, true } func AddRange(arr []*Range, rng *Range) []*Range { if len(arr) == 0 { return []*Range{rng} } idx := FirstContainsIndex(arr, rng.GetPosition()) arr = lo2.Insert(arr, idx+1, rng) lowerIdx := math2.Max(0, idx) lowerRng := arr[lowerIdx] for i := lowerIdx + 1; i < len(arr); i++ { nextRng := arr[i] if nextRng.GetPosition() > lowerRng.End() { break } curRngEnd := lowerRng.End() nextRngEnd := nextRng.End() lowerRng.SetPosition(math2.Min(lowerRng.GetPosition(), nextRng.GetPosition())) lowerRng.SetLength(math2.Max(curRngEnd, nextRngEnd) - lowerRng.GetPosition()) nextRng.SetLength(0) } return lo.Reject(arr, func(i *Range, idx int) bool { return i.GetLength() == 0 }) } func MergeRanges(arr []*Range, rngs []*Range) []*Range { type Point struct { pos int64 isStart bool } points := make([]Point, 0, (len(arr)+len(rngs))*2) for _, rng := range arr { points = append(points, Point{rng.GetPosition(), true}) points = append(points, Point{rng.GetPosition() + rng.GetLength(), false}) } for _, rng := range rngs { points = append(points, Point{rng.GetPosition(), true}) points = append(points, Point{rng.GetPosition() + rng.GetLength(), false}) } points = sort2.Sort(points, func(a, b Point) int { if a.pos == b.pos { // 位置相同,则endpoint在后 return sort2.CmpBool(b.isStart, a.isStart) } return math2.Sign(a.pos - b.pos) }) var merged []*Range depth := 0 for _, p := range points { if p.isStart { depth++ if depth == 1 { merged = append(merged, &Range{p.pos, 0}) } } else { depth-- if depth == 0 { lastIdx := len(merged) - 1 merged[lastIdx].Length = p.pos - merged[lastIdx].Position } } } return merged } func TruncateRange(arr []*Range, length int64) []*Range { if len(arr) == 0 { return arr } idx := FirstContainsIndex(arr, length) if idx < 0 { return arr[:0] } rng := arr[idx] if rng.End() > length { rng.Length = length - rng.Position } if rng.Length <= 0 { return arr[:idx] } return arr[:idx+1] } // 修剪指定的范围,使其仅包含在arr的空洞中。 // 如果范围会被切分成多个,那么就只会返回第一个切分段。 func DifferentRange(rng *Range, arr []*Range) { rngStart := rng.Position rngEnd := rng.End() idx := FirstContainsIndex(arr, rng.Position) if idx >= 0 { leftRng := arr[idx] rngStart = math2.Max(rng.Position, leftRng.End()) } if idx+1 < len(arr) { rightRng := arr[idx+1] rngEnd = math2.Min(rngEnd, rightRng.Position) } rng.Position = rngStart rng.Length = rngEnd - rngStart }