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