github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/constraint/spans.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package constraint 12 13 import ( 14 "sort" 15 "strings" 16 17 "github.com/cockroachdb/errors" 18 ) 19 20 // Spans is a collection of spans. There are no general requirements on the 21 // contents of the spans in the structure; the caller has to make sure they make 22 // sense in the respective context. 23 type Spans struct { 24 // firstSpan holds the first span and otherSpans hold any spans beyond the 25 // first. These are separated in order to optimize for the common case of a 26 // single-span constraint. 27 firstSpan Span 28 otherSpans []Span 29 numSpans int32 30 immutable bool 31 } 32 33 // Alloc allocates enough space to support the given amount of spans without 34 // reallocation. Does nothing if the structure already contains spans. 35 func (s *Spans) Alloc(capacity int) { 36 // We don't preallocate if the capacity is only 2: pre-allocating the slice to 37 // size 1 is no better than allocating it on the first Append, but it's worse 38 // if we end up not needing it. 39 if capacity > 2 && s.numSpans == 0 { 40 s.otherSpans = make([]Span, 0, capacity-1) 41 } 42 } 43 44 // InitSingleSpan initializes the structure with a single span. 45 func (s *Spans) InitSingleSpan(sp *Span) { 46 s.firstSpan = *sp 47 s.otherSpans = nil 48 s.numSpans = 1 49 s.immutable = false 50 } 51 52 // Count returns the number of spans. 53 func (s *Spans) Count() int { 54 return int(s.numSpans) 55 } 56 57 // Get returns the nth span. 58 func (s *Spans) Get(nth int) *Span { 59 if nth == 0 && s.numSpans > 0 { 60 return &s.firstSpan 61 } 62 return &s.otherSpans[nth-1] 63 } 64 65 // Append adds another span (at the end). 66 func (s *Spans) Append(sp *Span) { 67 if s.immutable { 68 panic(errors.AssertionFailedf("mutation disallowed")) 69 } 70 if s.numSpans == 0 { 71 s.firstSpan = *sp 72 } else { 73 s.otherSpans = append(s.otherSpans, *sp) 74 } 75 s.numSpans++ 76 } 77 78 // Truncate removes all but the first newLength spans. 79 func (s *Spans) Truncate(newLength int) { 80 if s.immutable { 81 panic(errors.AssertionFailedf("mutation disallowed")) 82 } 83 if int32(newLength) > s.numSpans { 84 panic(errors.AssertionFailedf("can't truncate to longer length")) 85 } 86 if newLength == 0 { 87 s.otherSpans = s.otherSpans[:0] 88 } else { 89 s.otherSpans = s.otherSpans[:newLength-1] 90 } 91 s.numSpans = int32(newLength) 92 } 93 94 func (s Spans) String() string { 95 var b strings.Builder 96 for i := 0; i < s.Count(); i++ { 97 if i > 0 { 98 b.WriteRune(' ') 99 } 100 b.WriteString(s.Get(i).String()) 101 } 102 return b.String() 103 } 104 105 // makeImmutable causes panics in any future calls to methods that mutate either 106 // the Spans structure or any Span returned by Get. 107 func (s *Spans) makeImmutable() { 108 s.immutable = true 109 } 110 111 // sortedAndMerged returns true if the collection of spans is strictly 112 // ordered and no spans overlap. 113 func (s *Spans) sortedAndMerged(keyCtx *KeyContext) bool { 114 for i := 1; i < s.Count(); i++ { 115 if !s.Get(i).StartsStrictlyAfter(keyCtx, s.Get(i-1)) { 116 return false 117 } 118 } 119 return true 120 } 121 122 // SortAndMerge sorts the spans and merges any overlapping spans. 123 func (s *Spans) SortAndMerge(keyCtx *KeyContext) { 124 if s.sortedAndMerged(keyCtx) { 125 return 126 } 127 sort.Sort(&spanSorter{keyCtx: *keyCtx, spans: s}) 128 129 // Merge overlapping spans. We maintain the last span and extend it with 130 // whatever spans it overlaps with. 131 n := 0 132 currentSpan := *s.Get(0) 133 for i := 1; i < s.Count(); i++ { 134 sp := s.Get(i) 135 if sp.StartsStrictlyAfter(keyCtx, ¤tSpan) { 136 // No overlap. "Output" the current span. 137 *s.Get(n) = currentSpan 138 n++ 139 currentSpan = *sp 140 } else { 141 // There is overlap; extend the current span to the right if necessary. 142 if currentSpan.CompareEnds(keyCtx, sp) < 0 { 143 currentSpan.end = sp.end 144 currentSpan.endBoundary = sp.endBoundary 145 } 146 } 147 } 148 *s.Get(n) = currentSpan 149 s.Truncate(n + 1) 150 } 151 152 type spanSorter struct { 153 keyCtx KeyContext 154 spans *Spans 155 } 156 157 var _ sort.Interface = &spanSorter{} 158 159 // Len is part of sort.Interface. 160 func (ss *spanSorter) Len() int { 161 return ss.spans.Count() 162 } 163 164 // Less is part of sort.Interface. 165 func (ss *spanSorter) Less(i, j int) bool { 166 return ss.spans.Get(i).Compare(&ss.keyCtx, ss.spans.Get(j)) < 0 167 } 168 169 // Swap is part of sort.Interface. 170 func (ss *spanSorter) Swap(i, j int) { 171 si := ss.spans.Get(i) 172 sj := ss.spans.Get(j) 173 *si, *sj = *sj, *si 174 }