// Copyright 2020 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cov import ( "fmt" "sort" ) // Location describes a single line-column position in a source file. type Location struct { Line, Column int } func (l Location) String() string { return fmt.Sprintf("%v:%v", l.Line, l.Column) } // Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. func (l Location) Compare(o Location) int { switch { case l.Line < o.Line: return -1 case l.Line > o.Line: return 1 case l.Column < o.Column: return -1 case l.Column > o.Column: return 1 } return 0 } // Before returns true if l comes before o. func (l Location) Before(o Location) bool { return l.Compare(o) == -1 } // After returns true if l comes after o. func (l Location) After(o Location) bool { return l.Compare(o) == 1 } // Span describes a start and end interval in a source file. type Span struct { Start, End Location } func (s Span) String() string { return fmt.Sprintf("%v-%v", s.Start, s.End) } // Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. func (s Span) Compare(o Span) int { switch { case s.Start.Before(o.Start): return -1 case o.Start.Before(s.Start): return 1 case s.End.Before(o.End): return -1 case o.End.Before(s.End): return 1 } return 0 } // Before returns true if span s comes before o. func (s Span) Before(o Span) bool { return s.Compare(o) == -1 } // Inside returns true if span s fits entirely inside o. func (s Span) Inside(o Span) bool { return s.Start.Compare(o.Start) >= 0 && s.End.Compare(o.End) <= 0 } // SpanList is a sorted list of spans. Use SpanList.Add() to insert new spans. type SpanList []Span // Add adds the Span to the SpanList, merging and expanding overlapping spans. func (l *SpanList) Add(s Span) { // [===] // [0] [1] | idxStart: 2 | idxEnd: 2 // [0] [1] | idxStart: 0 | idxEnd: 0 // [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 | idxEnd: 2 // [0] [1] [2] [3] [4] | idxStart: 2 | idxEnd: 2 idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) >= 0 }) if idxStart < len(*l) && s.Inside((*l)[idxStart]) { return // No change. } idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) > 0 }) if idxStart < idxEnd { if first := (*l)[idxStart]; first.Start.Before(s.Start) { s.Start = first.Start } if last := (*l)[idxEnd-1]; last.End.After(s.End) { s.End = last.End } } merged := append(SpanList{}, (*l)[:idxStart]...) merged = append(merged, s) merged = append(merged, (*l)[idxEnd:]...) *l = merged } // Remove cuts out the Span from the SpanList, removing and trimming overlapping // spans. func (l *SpanList) Remove(s Span) { if s.Start == s.End { return // zero length == no split. } // [===] // [0] [1] | idxStart: 2 | idxEnd: 2 // [0] [1] | idxStart: 0 | idxEnd: 0 // [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 | idxEnd: 2 // [0] [1] [2] [3] [4] | idxStart: 2 | idxEnd: 2 idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) > 0 }) idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) >= 0 }) merged := append(SpanList{}, (*l)[:idxStart]...) if idxStart < idxEnd { first, last := (*l)[idxStart], (*l)[idxEnd-1] if first.Start.Compare(s.Start) < 0 { merged = append(merged, Span{first.Start, s.Start}) } if last.End.Compare(s.End) > 0 { merged = append(merged, Span{s.End, last.End}) } } merged = append(merged, (*l)[idxEnd:]...) *l = merged } // Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0. func (l SpanList) Compare(o SpanList) int { switch { case len(l) < len(o): return -1 case len(l) > len(o): return 1 } for i, a := range l { switch a.Compare(o[i]) { case -1: return -1 case 1: return 1 } } return 0 } // NumLines returns the total number of lines covered by all spans in the list. func (l SpanList) NumLines() int { seen := map[int]struct{}{} for _, span := range l { for s := span.Start.Line; s <= span.End.Line; s++ { if _, ok := seen[s]; !ok { seen[s] = struct{}{} } } } return len(seen) }