| // Copyright 2022 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| 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) |
| } |