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  }