github.com/Microsoft/azure-vhd-utils@v0.0.0-20230613175315-7c30a3748a1b/vhdcore/common/indexRange.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "sort" 6 ) 7 8 // IndexRange represents sequence of integral numbers in a specified range, where range starts 9 // at Start and ends at End, inclusive 10 // 11 type IndexRange struct { 12 Start int64 13 End int64 14 } 15 16 // NewIndexRange creates a new range with start as value of the first integer in the sequence 17 // and end as value of last integer in the sequence. 18 // 19 func NewIndexRange(start, end int64) *IndexRange { 20 return &IndexRange{Start: start, End: end} 21 } 22 23 // NewIndexRangeFromLength creates a new range starting from start and ends at start + length - 1. 24 // 25 func NewIndexRangeFromLength(start, length int64) *IndexRange { 26 return NewIndexRange(start, start+length-1) 27 } 28 29 // TotalRangeLength returns the total length of a given slice of ranges. 30 // 31 func TotalRangeLength(ranges []*IndexRange) int64 { 32 var length = int64(0) 33 for _, r := range ranges { 34 length += r.Length() 35 } 36 return length 37 } 38 39 // SubtractRanges produces a set of ranges, each subset of ranges in this set is produced by 40 // subtracting subtrahends from each range in minuends. 41 // 42 func SubtractRanges(minuends, subtrahends []*IndexRange) []*IndexRange { 43 var result = make([]*IndexRange, 0) 44 for _, minuend := range minuends { 45 result = minuend.SubtractRanges(subtrahends, false, result) 46 } 47 48 return sortAndDedup(result) 49 } 50 51 // ChunkRangesBySize produces a set of ranges by partitioning the ranges in the given ranges by 52 // the given partition-size. 53 // Each each range in the given ranges X will be partitioned by the given partition-size to produce 54 // a range set A. If the last range in A is not of partition-size and if it is adjacent to the 55 // next range in the X then we calculate the bytes required to reach partition-size and 56 // 1. if next range has more bytes than required, then we borrow the required bytes from next 57 // range and advances the next range start 58 // 2. if next range has less or equal to the required bytes, then we borrow available and skip 59 // next range 60 // 61 func ChunkRangesBySize(ranges []*IndexRange, chunkSizeInBytes int64) []*IndexRange { 62 var chunks = make([]*IndexRange, 0) 63 length := len(ranges) 64 var remaining *IndexRange 65 66 for i, current := range ranges { 67 if remaining != nil { 68 if remaining.Adjacent(current) { 69 requiredBytes := chunkSizeInBytes - remaining.Length() 70 availableBytes := current.Length() 71 if requiredBytes < availableBytes { 72 remaining.End += requiredBytes 73 current.Start += requiredBytes 74 chunks = append(chunks, remaining) 75 remaining = nil 76 } else { 77 remaining.End += availableBytes 78 current = nil 79 } 80 } else { 81 chunks = append(chunks, remaining) 82 remaining = nil 83 } 84 } 85 86 if current != nil { 87 chunksSet := current.PartitionBy(chunkSizeInBytes) 88 89 lastChunkIndex := len(chunksSet) - 1 90 lastChunk := chunksSet[lastChunkIndex] 91 if (lastChunk.Length() != chunkSizeInBytes) && (i+1 < length) && lastChunk.Adjacent(ranges[i+1]) { 92 remaining = lastChunk 93 chunks = append(chunks, chunksSet[:lastChunkIndex]...) 94 } else { 95 chunks = append(chunks, chunksSet...) 96 } 97 } 98 } 99 100 if remaining != nil { 101 chunks = append(chunks, remaining) 102 } 103 104 return chunks 105 } 106 107 // Length returns number of sequential integers in the range. 108 // 109 func (ir *IndexRange) Length() int64 { 110 return ir.End - ir.Start + 1 111 } 112 113 // Equals returns true if this and given range represents the same sequence, two sequences 114 // are same if both have the same start and end. 115 // 116 func (ir *IndexRange) Equals(other *IndexRange) bool { 117 return other != nil && ir.Start == other.Start && ir.End == other.End 118 } 119 120 // CompareTo indicates whether the this range precedes, follows, or occurs in the same 121 // position in the sort order as the other 122 // A return value 123 // Less than zero: This range precedes the other in the sort order, range A precedes 124 // range B if A start before B or both has the same start and A ends 125 // before B. 126 // Zero: This range occurs in the same position as other in sort order, two 127 // ranges are in the same sort position if both has the same start 128 // and end 129 // Greater than zero: This range follows the other in the sort order, a range A follows 130 // range B, if A start after B or both has the same start and A ends 131 // after B 132 // 133 func (ir *IndexRange) CompareTo(other *IndexRange) int64 { 134 r := ir.Start - other.Start 135 if r != 0 { 136 return r 137 } 138 139 return ir.End - other.End 140 } 141 142 // Intersects checks this and other range intersects, two ranges A and B intersects if either 143 // of them starts or ends within the range of other, inclusive. 144 // 145 func (ir *IndexRange) Intersects(other *IndexRange) bool { 146 start := ir.Start 147 if start < other.Start { 148 start = other.Start 149 } 150 151 end := ir.End 152 if end > other.End { 153 end = other.End 154 } 155 156 return start <= end 157 } 158 159 // Intersection computes the range representing the intersection of two ranges, a return 160 // value nil indicates the ranges does not intersects. 161 // 162 func (ir *IndexRange) Intersection(other *IndexRange) *IndexRange { 163 start := ir.Start 164 if start < other.Start { 165 start = other.Start 166 } 167 168 end := ir.End 169 if end > other.End { 170 end = other.End 171 } 172 173 if start > end { 174 return nil 175 } 176 177 return NewIndexRange(start, end) 178 } 179 180 // Includes checks this range includes the other range, a range A includes range B if B starts 181 // and ends within A, inclusive. In other words a range A includes range B if their intersection 182 // produces B 183 // 184 func (ir *IndexRange) Includes(other *IndexRange) bool { 185 if other.Start < ir.Start { 186 return false 187 } 188 189 if other.End > ir.End { 190 return false 191 } 192 193 return true 194 } 195 196 // Gap compute the range representing the gap between this and the other range, a return value 197 // nil indicates there is no gap because either the ranges intersects or they are adjacent. 198 // 199 func (ir *IndexRange) Gap(other *IndexRange) *IndexRange { 200 if ir.Intersects(other) { 201 return nil 202 } 203 204 r := ir.CompareTo(other) 205 if r < 0 { 206 g := NewIndexRange(ir.End+1, other.Start-1) 207 if g.Length() <= 0 { 208 return nil 209 } 210 return g 211 } 212 213 g := NewIndexRange(other.End+1, ir.Start-1) 214 if g.Length() <= 0 { 215 return nil 216 } 217 return g 218 } 219 220 // Adjacent checks this range starts immediately starts after the other range or vice-versa, 221 // a return value nil indicates the ranges intersects or there is a gap between the ranges. 222 // 223 func (ir *IndexRange) Adjacent(other *IndexRange) bool { 224 return !ir.Intersects(other) && ir.Gap(other) == nil 225 } 226 227 // Subtract subtracts other range from this range and appends the ranges representing the 228 // differences to result slice. 229 // 230 // Given two ranges A and B, A - B produces 231 // 1. No result 232 // a. If they are equal or 233 // b. B includes A i.e 'A n B' = A 234 // OR 235 // 2. A, if they don't intersects 236 // OR 237 // 3. [(A n B).End + 1, A.End], if A and 'A n B' has same start 238 // OR 239 // 4. [A.Start, (A n B).Start - 1], if A and 'A n B' has same end 240 // OR 241 // 5. { [A.Start, (A n B).Start - 1], [(A n B).End + 1, A.End] }, otherwise 242 // 243 func (ir *IndexRange) Subtract(other *IndexRange, result []*IndexRange) []*IndexRange { 244 if ir.Equals(other) { 245 return result 246 } 247 248 if !ir.Intersects(other) { 249 result = append(result, NewIndexRange(ir.Start, ir.End)) 250 return result 251 } 252 253 in := ir.Intersection(other) 254 if ir.Equals(in) { 255 return result 256 } 257 258 if in.Start == ir.Start { 259 result = append(result, NewIndexRange(in.End+1, ir.End)) 260 return result 261 } 262 263 if in.End == ir.End { 264 result = append(result, NewIndexRange(ir.Start, in.Start-1)) 265 return result 266 } 267 268 result = append(result, NewIndexRange(ir.Start, in.Start-1)) 269 result = append(result, NewIndexRange(in.End+1, ir.End)) 270 return result 271 } 272 273 // SubtractRanges subtracts a set of ranges from this range and appends the ranges representing 274 // the differences to result slice. The result slice will be sorted and de-duped if sortandDedup 275 // is true. 276 // 277 func (ir *IndexRange) SubtractRanges(ranges []*IndexRange, sortandDedup bool, result []*IndexRange) []*IndexRange { 278 intersectAny := false 279 for _, o := range ranges { 280 if ir.Intersects(o) { 281 result = ir.Subtract(o, result) 282 intersectAny = true 283 } 284 } 285 286 if !intersectAny { 287 result = append(result, NewIndexRange(ir.Start, ir.End)) 288 } 289 290 if !sortandDedup { 291 return result 292 } 293 294 return sortAndDedup(result) 295 } 296 297 // Merge produces a range by merging this and other range if they are adjacent. Trying to merge 298 // non-adjacent ranges are panic. 299 // 300 func (ir *IndexRange) Merge(other *IndexRange) *IndexRange { 301 if !ir.Adjacent(other) { 302 // TODO: error 303 } 304 305 if ir.CompareTo(other) < 0 { 306 return NewIndexRange(ir.Start, other.End) 307 } 308 309 return NewIndexRange(other.Start, ir.End) 310 } 311 312 // PartitionBy produces a slice of adjacent ranges of same size, first range in the slice starts 313 // where this range starts and last range ends where this range ends. The length of last range will 314 // be less than size if length of this range is not multiple of size. 315 // 316 func (ir *IndexRange) PartitionBy(size int64) []*IndexRange { 317 length := ir.Length() 318 if length <= size { 319 return []*IndexRange{NewIndexRange(ir.Start, ir.End)} 320 } 321 322 blocks := length / size 323 r := make([]*IndexRange, blocks+1) 324 for i := int64(0); i < blocks; i++ { 325 r[i] = NewIndexRangeFromLength(ir.Start+i*size, size) 326 } 327 328 reminder := length % size 329 if reminder != 0 { 330 r[blocks] = NewIndexRangeFromLength(ir.Start+blocks*size, reminder) 331 return r 332 } 333 334 return r[:blocks] 335 } 336 337 // String returns the string representation of this range, this satisfies stringer interface. 338 // 339 func (ir *IndexRange) String() string { 340 return fmt.Sprintf("{%d, %d}", ir.Start, ir.End) 341 } 342 343 // sortAndDedup sorts the given range slice in place, remove the duplicates from the sorted slice 344 // and returns the updated slice. 345 // 346 func sortAndDedup(indexRanges []*IndexRange) []*IndexRange { 347 if len(indexRanges) == 0 { 348 return indexRanges 349 } 350 sort.Sort(indexRangeSorter(indexRanges)) 351 i := 0 352 for j := 1; j < len(indexRanges); j++ { 353 if !indexRanges[i].Equals(indexRanges[j]) { 354 i++ 355 indexRanges[i] = indexRanges[j] 356 } 357 } 358 return indexRanges[:i+1] 359 } 360 361 // indexRangeSorter is a type that satisfies sort.Interface interface for supporting sorting of 362 // a IndexRange collection. 363 // 364 type indexRangeSorter []*IndexRange 365 366 // Len is the number of elements in the range collection. 367 // 368 func (s indexRangeSorter) Len() int { 369 return len(s) 370 } 371 372 // Less reports whether range at i-th position precedes the range at j-th position in sort order. 373 // range A precedes range B if A start before B or both has the same start and A ends before B. 374 // 375 func (s indexRangeSorter) Less(i, j int) bool { 376 return s[i].CompareTo(s[j]) < 0 377 } 378 379 // Swap swaps the elements with indexes i and j. 380 // 381 func (s indexRangeSorter) Swap(i, j int) { 382 s[i], s[j] = s[j], s[i] 383 }