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  }