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