| // 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) |
| } |