github.com/dolthub/go-mysql-server@v0.18.0/sql/fast_int_set.go (about) 1 // Copyright 2022, DoltHub Inc. 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 // 15 // This file incorporates work from the cockroachdb/cockroach Github 16 // repository. 17 // 18 // The incorporated file contents come from cockroachdb/cockroach 19 // cockroach/pkg/util/fast_int_set.go @ commit 20 // c097a16427f65e9070991f062716d222ea5903fe. 21 // 22 // The Change Date on the BSL for this revision is 2022-10-01. 23 // 24 // Portions of this file are covered by the following copyright and permission 25 // notice: 26 // 27 // Copyright 2017 The Cockroach Authors. 28 // 29 // Use of this software is governed by the Business Source License 30 // included in the file licenses/BSL.txt. 31 // 32 // As of the Change Date specified in that file, in accordance with 33 // the Business Source License, use of this software will be governed 34 // by the Apache License, Version 2.0, included in the file 35 // licenses/APL.txt. 36 37 package sql 38 39 import ( 40 "bytes" 41 "fmt" 42 "math/bits" 43 44 "golang.org/x/tools/container/intsets" 45 ) 46 47 // FastIntSet keeps track of a set of integers. It does not perform any 48 // allocations when the values are small. It is not thread-safe. 49 type FastIntSet struct { 50 // We use a uint64 as long as all elements are between 0 and 63. If we add an 51 // element outside of this range, we switch to Sparse. We don't just use the 52 // latter directly because it's larger and can't be passed around by value. 53 small uint64 54 large *intsets.Sparse 55 } 56 57 // NewFastIntSet returns a set initialized with the given values. 58 func NewFastIntSet(vals ...int) FastIntSet { 59 var res FastIntSet 60 for _, v := range vals { 61 res.Add(v) 62 } 63 return res 64 } 65 66 // We store bits for values smaller than this cutoff. 67 // Note: this can be set to a smaller value, e.g. for testing. 68 const smallCutoff = 64 69 70 func (s *FastIntSet) toLarge() *intsets.Sparse { 71 if s.large != nil { 72 return s.large 73 } 74 large := new(intsets.Sparse) 75 for i, ok := s.Next(0); ok; i, ok = s.Next(i + 1) { 76 large.Insert(i) 77 } 78 return large 79 } 80 81 // Returns the bit encoded set from 0 to 63, and a flag that indicates whether 82 // there are elements outside this range. 83 func (s *FastIntSet) largeToSmall() (small uint64, otherValues bool) { 84 if s.large == nil { 85 panic("set not large") 86 } 87 for x := s.large.LowerBound(0); x < smallCutoff; x = s.large.LowerBound(x + 1) { 88 small |= (1 << uint64(x)) 89 } 90 return small, s.large.Min() < 0 || s.large.Max() >= smallCutoff 91 } 92 93 // Add adds a value to the set. No-op if the value is already in the set. 94 func (s *FastIntSet) Add(i int) { 95 if i >= 0 && i < smallCutoff && s.large == nil { 96 // Fast path. 97 s.small |= (1 << uint64(i)) 98 return 99 } 100 if s.large == nil { 101 s.large = s.toLarge() 102 s.small = 0 103 } 104 s.large.Insert(i) 105 } 106 107 // AddRange adds values 'from' up to 'to' (inclusively) to the set. 108 // E.g. AddRange(1,5) adds the values 1, 2, 3, 4, 5 to the set. 109 // 'to' must be >= 'from'. 110 // AddRange is always more efficient than individual Adds. 111 func (s *FastIntSet) AddRange(from, to int) { 112 if to < from { 113 panic("invalid range when adding range to FastIntSet") 114 } 115 116 if from >= 0 && to < smallCutoff && s.large == nil { 117 nValues := to - from + 1 118 // Fast path. 119 s.small |= (1<<uint64(nValues) - 1) << uint64(from) 120 return 121 } 122 123 if s.large == nil { 124 s.large = s.toLarge() 125 s.small = 0 126 } 127 for i := from; i <= to; i++ { 128 s.large.Insert(i) 129 } 130 } 131 132 // Remove removes a value from the set. No-op if the value is not in the set. 133 func (s *FastIntSet) Remove(i int) { 134 if s.large == nil { 135 if i >= 0 && i < smallCutoff { 136 s.small &= ^(1 << uint64(i)) 137 } 138 } else { 139 s.large.Remove(i) 140 } 141 } 142 143 // Contains returns true if the set contains the value. 144 func (s FastIntSet) Contains(i int) bool { 145 if s.large != nil { 146 return s.large.Has(i) 147 } 148 return i >= 0 && i < smallCutoff && (s.small&(1<<uint64(i))) != 0 149 } 150 151 // Empty returns true if the set is empty. 152 func (s FastIntSet) Empty() bool { 153 return s.small == 0 && (s.large == nil || s.large.IsEmpty()) 154 } 155 156 // Len returns the number of the elements in the set. 157 func (s FastIntSet) Len() int { 158 if s.large == nil { 159 return bits.OnesCount64(s.small) 160 } 161 return s.large.Len() 162 } 163 164 // Next returns the first value in the set which is >= startVal. If there is no 165 // value, the second return value is false. 166 func (s FastIntSet) Next(startVal int) (int, bool) { 167 if s.large != nil { 168 res := s.large.LowerBound(startVal) 169 return res, res != intsets.MaxInt 170 } 171 if startVal < smallCutoff { 172 if startVal < 0 { 173 startVal = 0 174 } 175 176 if ntz := bits.TrailingZeros64(s.small >> uint64(startVal)); ntz < 64 { 177 return startVal + ntz, true 178 } 179 } 180 return intsets.MaxInt, false 181 } 182 183 // ForEach calls a function for each value in the set (in increasing order). 184 func (s FastIntSet) ForEach(f func(i int)) { 185 if s.large != nil { 186 for x := s.large.Min(); x != intsets.MaxInt; x = s.large.LowerBound(x + 1) { 187 f(x) 188 } 189 return 190 } 191 for v := s.small; v != 0; { 192 i := bits.TrailingZeros64(v) 193 f(i) 194 v &^= 1 << uint(i) 195 } 196 } 197 198 // Ordered returns a slice with all the integers in the set, in increasing order. 199 func (s FastIntSet) Ordered() []int { 200 if s.Empty() { 201 return nil 202 } 203 if s.large != nil { 204 return s.large.AppendTo([]int(nil)) 205 } 206 result := make([]int, 0, s.Len()) 207 s.ForEach(func(i int) { 208 result = append(result, i) 209 }) 210 return result 211 } 212 213 // Copy returns a copy of s which can be modified independently. 214 func (s FastIntSet) Copy() FastIntSet { 215 var c FastIntSet 216 if s.large != nil { 217 c.large = new(intsets.Sparse) 218 c.large.Copy(s.large) 219 } else { 220 c.small = s.small 221 } 222 return c 223 } 224 225 // CopyFrom sets the receiver to a copy of c, which can then be modified 226 // independently. 227 func (s *FastIntSet) CopyFrom(c FastIntSet) { 228 if c.large != nil { 229 if s.large == nil { 230 s.large = new(intsets.Sparse) 231 } 232 s.large.Copy(s.large) 233 } else { 234 s.small = c.small 235 if s.large != nil { 236 s.large.Clear() 237 } 238 } 239 } 240 241 // UnionWith adds all the elements from rhs to this set. 242 func (s *FastIntSet) UnionWith(rhs FastIntSet) { 243 if s.large == nil && rhs.large == nil { 244 // Fast path. 245 s.small |= rhs.small 246 return 247 } 248 249 if s.large == nil { 250 s.large = s.toLarge() 251 s.small = 0 252 } 253 s.large.UnionWith(rhs.toLarge()) 254 } 255 256 // Union returns the union of s and rhs as a new set. 257 func (s FastIntSet) Union(rhs FastIntSet) FastIntSet { 258 r := s.Copy() 259 r.UnionWith(rhs) 260 return r 261 } 262 263 // IntersectionWith removes any elements not in rhs from this set. 264 func (s *FastIntSet) IntersectionWith(rhs FastIntSet) { 265 if s.large == nil { 266 // Fast path. 267 other := rhs.small 268 if rhs.large != nil { 269 // If the other set is large, we can ignore any values outside of the 270 // small range. 271 other, _ = rhs.largeToSmall() 272 } 273 s.small &= other 274 return 275 } 276 277 s.large.IntersectionWith(rhs.toLarge()) 278 } 279 280 // Intersection returns the intersection of s and rhs as a new set. 281 func (s FastIntSet) Intersection(rhs FastIntSet) FastIntSet { 282 r := s.Copy() 283 r.IntersectionWith(rhs) 284 return r 285 } 286 287 // Intersects returns true if s has any elements in common with rhs. 288 func (s FastIntSet) Intersects(rhs FastIntSet) bool { 289 if s.large == nil { 290 // Fast path 291 other := rhs.small 292 if rhs.large != nil { 293 // If the other set is large, we can ignore any values outside of the 294 // small range. 295 other, _ = rhs.largeToSmall() 296 } 297 return (s.small & other) != 0 298 } 299 return s.large.Intersects(rhs.toLarge()) 300 } 301 302 // DifferenceWith removes any elements in rhs from this set. 303 func (s *FastIntSet) DifferenceWith(rhs FastIntSet) { 304 if s.large == nil { 305 // Fast path 306 other := rhs.small 307 if rhs.large != nil { 308 // If the other set is large, we can ignore any values outside of the 309 // small range. 310 other, _ = rhs.largeToSmall() 311 } 312 s.small &^= other 313 return 314 } 315 s.large.DifferenceWith(rhs.toLarge()) 316 } 317 318 // Difference returns the elements of s that are not in rhs as a new set. 319 func (s FastIntSet) Difference(rhs FastIntSet) FastIntSet { 320 r := s.Copy() 321 r.DifferenceWith(rhs) 322 return r 323 } 324 325 // Equals returns true if the two sets are identical. 326 func (s FastIntSet) Equals(rhs FastIntSet) bool { 327 if s.large == nil && rhs.large == nil { 328 return s.small == rhs.small 329 } 330 if s.large != nil && rhs.large != nil { 331 return s.large.Equals(rhs.large) 332 } 333 // One set is "large" and one is "small". They might still be equal (the large 334 // set could have had a large element added and then removed). 335 var extraVals bool 336 s1 := s.small 337 s2 := rhs.small 338 if s.large != nil { 339 s1, extraVals = s.largeToSmall() 340 } else { 341 s2, extraVals = rhs.largeToSmall() 342 } 343 return !extraVals && s1 == s2 344 } 345 346 // SubsetOf returns true if rhs contains all the elements in s. 347 func (s FastIntSet) SubsetOf(rhs FastIntSet) bool { 348 if s.large == nil && rhs.large == nil { 349 return (s.small & rhs.small) == s.small 350 } 351 if s.large != nil && rhs.large != nil { 352 return s.large.SubsetOf(rhs.large) 353 } 354 // One set is "large" and one is "small". 355 s1 := s.small 356 s2 := rhs.small 357 if s.large != nil { 358 var extraVals bool 359 s1, extraVals = s.largeToSmall() 360 if extraVals { 361 // s has elements that rhs (which is small) can't have. 362 return false 363 } 364 } else { 365 // We don't care if rhs has extra values. 366 s2, _ = rhs.largeToSmall() 367 } 368 return (s1 & s2) == s1 369 } 370 371 // Shift generates a new set which contains elements i+delta for elements i in 372 // the original set. 373 func (s *FastIntSet) Shift(delta int) FastIntSet { 374 if s.large == nil { 375 // Fast paths. 376 if delta > 0 { 377 if bits.LeadingZeros64(s.small)-(64-smallCutoff) >= delta { 378 return FastIntSet{small: s.small << uint32(delta)} 379 } 380 } else { 381 if bits.TrailingZeros64(s.small) >= -delta { 382 return FastIntSet{small: s.small >> uint32(-delta)} 383 } 384 } 385 } 386 // Do the slow thing. 387 var result FastIntSet 388 s.ForEach(func(i int) { 389 result.Add(i + delta) 390 }) 391 return result 392 } 393 394 // String returns a list representation of elements. Sequential runs of positive 395 // numbers are shown as ranges. For example, for the set {0, 1, 2, 5, 6, 10}, 396 // the output is "(0-2,5,6,10)". 397 func (s FastIntSet) String() string { 398 var buf bytes.Buffer 399 buf.WriteByte('(') 400 appendRange := func(start, end int) { 401 if buf.Len() > 1 { 402 buf.WriteByte(',') 403 } 404 if start == end { 405 fmt.Fprintf(&buf, "%d", start) 406 } else if start+1 == end { 407 fmt.Fprintf(&buf, "%d,%d", start, end) 408 } else { 409 fmt.Fprintf(&buf, "%d-%d", start, end) 410 } 411 } 412 rangeStart, rangeEnd := -1, -1 413 s.ForEach(func(i int) { 414 if i < 0 { 415 appendRange(i, i) 416 return 417 } 418 if rangeStart != -1 && rangeEnd == i-1 { 419 rangeEnd = i 420 } else { 421 if rangeStart != -1 { 422 appendRange(rangeStart, rangeEnd) 423 } 424 rangeStart, rangeEnd = i, i 425 } 426 }) 427 if rangeStart != -1 { 428 appendRange(rangeStart, rangeEnd) 429 } 430 buf.WriteByte(')') 431 return buf.String() 432 }