xref: /aosp_15_r20/external/swiftshader/tests/regres/cov/span.go (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cov
16
17import (
18	"fmt"
19	"sort"
20)
21
22// Location describes a single line-column position in a source file.
23type Location struct {
24	Line, Column int
25}
26
27func (l Location) String() string {
28	return fmt.Sprintf("%v:%v", l.Line, l.Column)
29}
30
31// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0.
32func (l Location) Compare(o Location) int {
33	switch {
34	case l.Line < o.Line:
35		return -1
36	case l.Line > o.Line:
37		return 1
38	case l.Column < o.Column:
39		return -1
40	case l.Column > o.Column:
41		return 1
42	}
43	return 0
44}
45
46// Before returns true if l comes before o.
47func (l Location) Before(o Location) bool { return l.Compare(o) == -1 }
48
49// After returns true if l comes after o.
50func (l Location) After(o Location) bool { return l.Compare(o) == 1 }
51
52// Span describes a start and end interval in a source file.
53type Span struct {
54	Start, End Location
55}
56
57func (s Span) String() string {
58	return fmt.Sprintf("%v-%v", s.Start, s.End)
59}
60
61// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0.
62func (s Span) Compare(o Span) int {
63	switch {
64	case s.Start.Before(o.Start):
65		return -1
66	case o.Start.Before(s.Start):
67		return 1
68	case s.End.Before(o.End):
69		return -1
70	case o.End.Before(s.End):
71		return 1
72	}
73	return 0
74}
75
76// Before returns true if span s comes before o.
77func (s Span) Before(o Span) bool { return s.Compare(o) == -1 }
78
79// Inside returns true if span s fits entirely inside o.
80func (s Span) Inside(o Span) bool { return s.Start.Compare(o.Start) >= 0 && s.End.Compare(o.End) <= 0 }
81
82// SpanList is a sorted list of spans. Use SpanList.Add() to insert new spans.
83type SpanList []Span
84
85// Add adds the Span to the SpanList, merging and expanding overlapping spans.
86func (l *SpanList) Add(s Span) {
87	//          [===]
88	//  [0] [1]                | idxStart: 2 |  idxEnd: 2
89	//                [0] [1]  | idxStart: 0 |  idxEnd: 0
90	// [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 |  idxEnd: 2
91	// [0]  [1]  [2]  [3]  [4] | idxStart: 2 |  idxEnd: 2
92	idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) >= 0 })
93
94	if idxStart < len(*l) && s.Inside((*l)[idxStart]) {
95		return // No change.
96	}
97
98	idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) > 0 })
99
100	if idxStart < idxEnd {
101		if first := (*l)[idxStart]; first.Start.Before(s.Start) {
102			s.Start = first.Start
103		}
104		if last := (*l)[idxEnd-1]; last.End.After(s.End) {
105			s.End = last.End
106		}
107	}
108
109	merged := append(SpanList{}, (*l)[:idxStart]...)
110	merged = append(merged, s)
111	merged = append(merged, (*l)[idxEnd:]...)
112	*l = merged
113}
114
115// Remove cuts out the Span from the SpanList, removing and trimming overlapping
116// spans.
117func (l *SpanList) Remove(s Span) {
118	if s.Start == s.End {
119		return // zero length == no split.
120	}
121
122	//          [===]
123	//  [0] [1]                | idxStart: 2 |  idxEnd: 2
124	//                [0] [1]  | idxStart: 0 |  idxEnd: 0
125	// [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 |  idxEnd: 2
126	// [0]  [1]  [2]  [3]  [4] | idxStart: 2 |  idxEnd: 2
127	idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) > 0 })
128	idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) >= 0 })
129
130	merged := append(SpanList{}, (*l)[:idxStart]...)
131
132	if idxStart < idxEnd {
133		first, last := (*l)[idxStart], (*l)[idxEnd-1]
134		if first.Start.Compare(s.Start) < 0 {
135			merged = append(merged, Span{first.Start, s.Start})
136		}
137		if last.End.Compare(s.End) > 0 {
138			merged = append(merged, Span{s.End, last.End})
139		}
140	}
141
142	merged = append(merged, (*l)[idxEnd:]...)
143	*l = merged
144}
145
146// Compare returns -1 if l comes before o, 1 if l comes after o, otherwise 0.
147func (l SpanList) Compare(o SpanList) int {
148	switch {
149	case len(l) < len(o):
150		return -1
151	case len(l) > len(o):
152		return 1
153	}
154	for i, a := range l {
155		switch a.Compare(o[i]) {
156		case -1:
157			return -1
158		case 1:
159			return 1
160		}
161	}
162	return 0
163}
164
165// NumLines returns the total number of lines covered by all spans in the list.
166func (l SpanList) NumLines() int {
167	seen := map[int]struct{}{}
168	for _, span := range l {
169		for s := span.Start.Line; s <= span.End.Line; s++ {
170			if _, ok := seen[s]; !ok {
171				seen[s] = struct{}{}
172			}
173		}
174	}
175	return len(seen)
176}
177