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