1// Copyright 2020 The SwiftShader Authors. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cov 16 17import ( 18 "fmt" 19 "sort" 20) 21 22// Location describes a single line-column position in a source file. 23type Location struct { 24 Line, Column int 25} 26 27func (l Location) String() string { 28 return fmt.Sprintf("%v:%v", l.Line, l.Column) 29} 30 31// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. 32func (l Location) Compare(o Location) int { 33 switch { 34 case l.Line < o.Line: 35 return -1 36 case l.Line > o.Line: 37 return 1 38 case l.Column < o.Column: 39 return -1 40 case l.Column > o.Column: 41 return 1 42 } 43 return 0 44} 45 46// Before returns true if l comes before o. 47func (l Location) Before(o Location) bool { return l.Compare(o) == -1 } 48 49// After returns true if l comes after o. 50func (l Location) After(o Location) bool { return l.Compare(o) == 1 } 51 52// Span describes a start and end interval in a source file. 53type Span struct { 54 Start, End Location 55} 56 57func (s Span) String() string { 58 return fmt.Sprintf("%v-%v", s.Start, s.End) 59} 60 61// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. 62func (s Span) Compare(o Span) int { 63 switch { 64 case s.Start.Before(o.Start): 65 return -1 66 case o.Start.Before(s.Start): 67 return 1 68 case s.End.Before(o.End): 69 return -1 70 case o.End.Before(s.End): 71 return 1 72 } 73 return 0 74} 75 76// Before returns true if span s comes before o. 77func (s Span) Before(o Span) bool { return s.Compare(o) == -1 } 78 79// Inside returns true if span s fits entirely inside o. 80func (s Span) Inside(o Span) bool { return s.Start.Compare(o.Start) >= 0 && s.End.Compare(o.End) <= 0 } 81 82// SpanList is a sorted list of spans. Use SpanList.Add() to insert new spans. 83type SpanList []Span 84 85// Add adds the Span to the SpanList, merging and expanding overlapping spans. 86func (l *SpanList) Add(s Span) { 87 // [===] 88 // [0] [1] | idxStart: 2 | idxEnd: 2 89 // [0] [1] | idxStart: 0 | idxEnd: 0 90 // [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 | idxEnd: 2 91 // [0] [1] [2] [3] [4] | idxStart: 2 | idxEnd: 2 92 idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) >= 0 }) 93 94 if idxStart < len(*l) && s.Inside((*l)[idxStart]) { 95 return // No change. 96 } 97 98 idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) > 0 }) 99 100 if idxStart < idxEnd { 101 if first := (*l)[idxStart]; first.Start.Before(s.Start) { 102 s.Start = first.Start 103 } 104 if last := (*l)[idxEnd-1]; last.End.After(s.End) { 105 s.End = last.End 106 } 107 } 108 109 merged := append(SpanList{}, (*l)[:idxStart]...) 110 merged = append(merged, s) 111 merged = append(merged, (*l)[idxEnd:]...) 112 *l = merged 113} 114 115// Remove cuts out the Span from the SpanList, removing and trimming overlapping 116// spans. 117func (l *SpanList) Remove(s Span) { 118 if s.Start == s.End { 119 return // zero length == no split. 120 } 121 122 // [===] 123 // [0] [1] | idxStart: 2 | idxEnd: 2 124 // [0] [1] | idxStart: 0 | idxEnd: 0 125 // [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 | idxEnd: 2 126 // [0] [1] [2] [3] [4] | idxStart: 2 | idxEnd: 2 127 idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) > 0 }) 128 idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) >= 0 }) 129 130 merged := append(SpanList{}, (*l)[:idxStart]...) 131 132 if idxStart < idxEnd { 133 first, last := (*l)[idxStart], (*l)[idxEnd-1] 134 if first.Start.Compare(s.Start) < 0 { 135 merged = append(merged, Span{first.Start, s.Start}) 136 } 137 if last.End.Compare(s.End) > 0 { 138 merged = append(merged, Span{s.End, last.End}) 139 } 140 } 141 142 merged = append(merged, (*l)[idxEnd:]...) 143 *l = merged 144} 145 146// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. 147func (l SpanList) Compare(o SpanList) int { 148 switch { 149 case len(l) < len(o): 150 return -1 151 case len(l) > len(o): 152 return 1 153 } 154 for i, a := range l { 155 switch a.Compare(o[i]) { 156 case -1: 157 return -1 158 case 1: 159 return 1 160 } 161 } 162 return 0 163} 164 165// NumLines returns the total number of lines covered by all spans in the list. 166func (l SpanList) NumLines() int { 167 seen := map[int]struct{}{} 168 for _, span := range l { 169 for s := span.Start.Line; s <= span.End.Line; s++ { 170 if _, ok := seen[s]; !ok { 171 seen[s] = struct{}{} 172 } 173 } 174 } 175 return len(seen) 176} 177