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  }