github.com/linuxboot/fiano@v1.2.0/pkg/bytes/range.go (about)

     1  // Copyright 2019 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package bytes
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  	"strings"
    11  )
    12  
    13  // Range defines is a generic bytes range headers.
    14  type Range struct {
    15  	Offset uint64
    16  	Length uint64
    17  }
    18  
    19  func (r Range) String() string {
    20  	return fmt.Sprintf(`{"Offset":"0x%x", "Length":"0x%x"}`, r.Offset, r.Length)
    21  }
    22  
    23  // Intersect returns True if ranges "r" and "cmp" has at least
    24  // one byte with the same offset.
    25  func (r Range) Intersect(cmp Range) bool {
    26  	if r.Length == 0 || cmp.Length == 0 {
    27  		return false
    28  	}
    29  
    30  	startIdx0 := r.Offset
    31  	startIdx1 := cmp.Offset
    32  	endIdx0 := startIdx0 + r.Length
    33  	endIdx1 := startIdx1 + cmp.Length
    34  
    35  	if endIdx0 <= startIdx1 {
    36  		return false
    37  	}
    38  	if startIdx0 >= endIdx1 {
    39  		return false
    40  	}
    41  
    42  	return true
    43  }
    44  
    45  // End returns the first offset after the range.
    46  func (r Range) End() uint64 {
    47  	return r.Offset + r.Length
    48  }
    49  
    50  // Exclude returns the parts of the range, which excludes the selected ones.
    51  func (r Range) Exclude(_toExcludes ...Range) Ranges {
    52  	toExcludes := Ranges(_toExcludes)
    53  	toExcludes.SortAndMerge()
    54  
    55  	var result Ranges
    56  	curStart := r.Offset
    57  	curEnd := r.End()
    58  	for _, toExclude := range toExcludes {
    59  		excStart := toExclude.Offset
    60  		excEnd := toExclude.End()
    61  		if excEnd <= curStart {
    62  			continue
    63  		}
    64  		if excStart >= curEnd {
    65  			continue
    66  		}
    67  
    68  		if curStart < excStart {
    69  			result = append(result, Range{
    70  				Offset: curStart,
    71  				Length: excStart - curStart,
    72  			})
    73  		}
    74  
    75  		curStart = excEnd
    76  		if curStart >= curEnd {
    77  			return result
    78  		}
    79  	}
    80  
    81  	result = append(result, Range{
    82  		Offset: curStart,
    83  		Length: curEnd - curStart,
    84  	})
    85  	return result
    86  }
    87  
    88  // Ranges is a helper to manipulate multiple `Range`-s at once
    89  type Ranges []Range
    90  
    91  func (s Ranges) String() string {
    92  	r := make([]string, 0, len(s))
    93  	for _, oneRange := range s {
    94  		r = append(r, oneRange.String())
    95  	}
    96  	return `[` + strings.Join(r, `, `) + `]`
    97  }
    98  
    99  // Sort sorts the slice by field Offset
   100  func (s Ranges) Sort() {
   101  	sort.Slice(s, func(i, j int) bool {
   102  		return s[i].Offset < s[j].Offset
   103  	})
   104  }
   105  
   106  func maxUint64(a, b uint64) uint64 {
   107  	if a >= b {
   108  		return a
   109  	}
   110  	return b
   111  }
   112  
   113  // MergeRanges just merges ranges which has distance less or equal to
   114  // mergeDistance.
   115  //
   116  // Warning: should be called only on sorted ranges!
   117  func MergeRanges(in Ranges, mergeDistance uint64) Ranges {
   118  	if len(in) < 2 {
   119  		return in
   120  	}
   121  
   122  	var result Ranges
   123  	entry := in[0]
   124  	for _, nextEntry := range in[1:] {
   125  		// merge "nextEntry" to "entry" if the distance is lower or equal to
   126  		// mergeDistance.
   127  
   128  		if entry.Offset+entry.Length+mergeDistance >= nextEntry.Offset {
   129  			newRangeEnd := maxUint64(nextEntry.Offset+nextEntry.Length, entry.Offset+entry.Length)
   130  			entry.Length = newRangeEnd - entry.Offset
   131  			continue
   132  		}
   133  
   134  		result = append(result, entry)
   135  		entry = nextEntry
   136  	}
   137  	result = append(result, entry)
   138  
   139  	return result
   140  }
   141  
   142  // SortAndMerge sorts the slice (by field Offset) and the merges ranges
   143  // which could be merged.
   144  func (s *Ranges) SortAndMerge() {
   145  	// See also TestDiffEntriesSortAndMerge
   146  
   147  	if len(*s) < 2 {
   148  		return
   149  	}
   150  	s.Sort()
   151  
   152  	*s = MergeRanges(*s, 0)
   153  }
   154  
   155  // Compile returns the bytes from `b` which are referenced by `Range`-s `s`.
   156  func (s Ranges) Compile(b []byte) []byte {
   157  	var result []byte
   158  	for _, r := range s {
   159  		result = append(result, b[r.Offset:r.Offset+r.Length]...)
   160  	}
   161  	return result
   162  }
   163  
   164  // IsIn returns if the index is covered by this ranges
   165  func (s Ranges) IsIn(index uint64) bool {
   166  	for _, r := range s {
   167  		startIdx := r.Offset
   168  		endIdx := r.Offset + r.Length
   169  		// `startIdx` is inclusive, while `endIdx` is exclusive.
   170  		// The same as usual slice indices works:
   171  		//
   172  		//     slice[startIdx:endIdx]
   173  
   174  		if startIdx <= index && index < endIdx {
   175  			return true
   176  		}
   177  	}
   178  	return false
   179  }