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 }