vitess.io/vitess@v0.16.2/go/vt/vtgate/semantics/bitset/bitset.go (about)

     1  /*
     2  Copyright 2022 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bitset
    18  
    19  import (
    20  	"math/bits"
    21  	"unsafe"
    22  )
    23  
    24  // A Bitset is an immutable collection of bits. You can perform logical operations
    25  // on it, but all mutable operations return a new Bitset.
    26  // It is safe to compare directly using the comparison operator and to use as a map key.
    27  type Bitset string
    28  
    29  const bitsetWidth = 8
    30  
    31  func bitsetWordSize(max int) int {
    32  	return max/bitsetWidth + 1
    33  }
    34  
    35  // toBiset converts a slice of bytes into a Bitset without allocating memory.
    36  // Bitset is actually a type alias for `string`, which is the only native type in Go that is dynamic _and_
    37  // immutable, so it can be used as a key in maps or compared directly.
    38  func toBitset(words []byte) Bitset {
    39  	if len(words) == 0 {
    40  		return ""
    41  	}
    42  	if words[len(words)-1] == 0 {
    43  		panic("toBitset: did not truncate")
    44  	}
    45  	// to convert a byte slice into a bitset without cloning the slice, we use the same trick as
    46  	// the Go standard library's `strings.Builder`. A slice header is [data, len, cap] while a
    47  	// string header is [data, len], hence the first two words of a slice header can be reinterpreted
    48  	// as a string header simply by casting into it.
    49  	// This assumes that the `words` slice will never be written to after returning from this function.
    50  	return *(*Bitset)(unsafe.Pointer(&words))
    51  }
    52  
    53  func minlen(a, b Bitset) int {
    54  	if len(a) < len(b) {
    55  		return len(a)
    56  	}
    57  	return len(b)
    58  }
    59  
    60  // Overlaps returns whether this Bitset and the input have any bits in common
    61  func (bs Bitset) Overlaps(b2 Bitset) bool {
    62  	min := minlen(bs, b2)
    63  	for i := 0; i < min; i++ {
    64  		if bs[i]&b2[i] != 0 {
    65  			return true
    66  		}
    67  	}
    68  	return false
    69  }
    70  
    71  // Or returns the logical OR of the two Bitsets as a new Bitset
    72  func (bs Bitset) Or(b2 Bitset) Bitset {
    73  	if len(bs) == 0 {
    74  		return b2
    75  	}
    76  	if len(b2) == 0 {
    77  		return bs
    78  	}
    79  
    80  	small, large := bs, b2
    81  	if len(small) > len(large) {
    82  		small, large = large, small
    83  	}
    84  
    85  	merged := make([]byte, len(large))
    86  	m := 0
    87  
    88  	for m < len(small) {
    89  		merged[m] = small[m] | large[m]
    90  		m++
    91  	}
    92  	for m < len(large) {
    93  		merged[m] = large[m]
    94  		m++
    95  	}
    96  	return toBitset(merged)
    97  }
    98  
    99  // AndNot returns the logical AND NOT of the two Bitsets as a new Bitset
   100  func (bs Bitset) AndNot(b2 Bitset) Bitset {
   101  	if len(b2) == 0 {
   102  		return bs
   103  	}
   104  
   105  	merged := make([]byte, len(bs))
   106  	m := 0
   107  
   108  	for m = 0; m < len(bs); m++ {
   109  		if m < len(b2) {
   110  			merged[m] = bs[m] & ^b2[m]
   111  		} else {
   112  			merged[m] = bs[m]
   113  		}
   114  	}
   115  	for ; m > 0; m-- {
   116  		if merged[m-1] != 0 {
   117  			break
   118  		}
   119  	}
   120  	return toBitset(merged[:m])
   121  }
   122  
   123  // And returns the logical AND of the two bitsets as a new Bitset
   124  func (bs Bitset) And(b2 Bitset) Bitset {
   125  	if len(bs) == 0 || len(b2) == 0 {
   126  		return ""
   127  	}
   128  
   129  	merged := make([]byte, minlen(bs, b2))
   130  	m := 0
   131  
   132  	for m = 0; m < len(merged); m++ {
   133  		merged[m] = bs[m] & b2[m]
   134  	}
   135  	for ; m > 0; m-- {
   136  		if merged[m-1] != 0 {
   137  			break
   138  		}
   139  	}
   140  	return toBitset(merged[:m])
   141  }
   142  
   143  // Set returns a copy of this Bitset where the bit at `offset` is set
   144  func (bs Bitset) Set(offset int) Bitset {
   145  	alloc := len(bs)
   146  	if max := bitsetWordSize(offset); max > alloc {
   147  		alloc = max
   148  	}
   149  
   150  	words := make([]byte, alloc)
   151  	copy(words, bs)
   152  	words[offset/bitsetWidth] |= 1 << (offset % bitsetWidth)
   153  	return toBitset(words)
   154  }
   155  
   156  // SingleBit returns the position of the single bit that is set in this Bitset
   157  // If the Bitset is empty, or contains more than one set bit, it returns -1
   158  func (bs Bitset) SingleBit() int {
   159  	offset := -1
   160  	for i := 0; i < len(bs); i++ {
   161  		t := bs[i]
   162  		if t == 0 {
   163  			continue
   164  		}
   165  		if offset >= 0 || bits.OnesCount8(t) != 1 {
   166  			return -1
   167  		}
   168  		offset = i*bitsetWidth + bits.TrailingZeros8(t)
   169  	}
   170  	return offset
   171  }
   172  
   173  // IsContainedBy returns whether this Bitset is contained by the given Bitset
   174  func (bs Bitset) IsContainedBy(b2 Bitset) bool {
   175  	if len(bs) > len(b2) {
   176  		return false
   177  	}
   178  	for i := 0; i < len(bs); i++ {
   179  		left := bs[i]
   180  		rigt := b2[i]
   181  		if left&rigt != left {
   182  			return false
   183  		}
   184  	}
   185  	return true
   186  }
   187  
   188  // Popcount returns the number of bits that are set in this Bitset
   189  func (bs Bitset) Popcount() (count int) {
   190  	for i := 0; i < len(bs); i++ {
   191  		count += bits.OnesCount8(bs[i])
   192  	}
   193  	return
   194  }
   195  
   196  // ForEach calls the given callback with the position of each bit set in this Bitset
   197  func (bs Bitset) ForEach(yield func(int)) {
   198  	// From Lemire, "Iterating over set bits quickly"
   199  	// https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
   200  	for i := 0; i < len(bs); i++ {
   201  		bitset := bs[i]
   202  		for bitset != 0 {
   203  			t := bitset & -bitset
   204  			r := bits.TrailingZeros8(bitset)
   205  			yield(i*bitsetWidth + r)
   206  			bitset ^= t
   207  		}
   208  	}
   209  }
   210  
   211  // Build creates a new immutable Bitset where all the given bits are set
   212  func Build(bits ...int) Bitset {
   213  	if len(bits) == 0 {
   214  		return ""
   215  	}
   216  
   217  	max := bits[0]
   218  	for _, b := range bits[1:] {
   219  		if b > max {
   220  			max = b
   221  		}
   222  	}
   223  
   224  	words := make([]byte, bitsetWordSize(max))
   225  	for _, b := range bits {
   226  		words[b/bitsetWidth] |= 1 << (b % bitsetWidth)
   227  	}
   228  	return toBitset(words)
   229  }
   230  
   231  const singleton = "\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x10\x00\x00\x00\x20\x00\x00\x00\x40\x00\x00\x00\x80"
   232  
   233  // Single returns a new Bitset where only the given bit is set.
   234  // If the given bit is less than 32, Single does not allocate to create a new Bitset.
   235  func Single(bit int) Bitset {
   236  	switch {
   237  	case bit < 8:
   238  		bit = (bit + 1) << 2
   239  		return Bitset(singleton[bit-1 : bit])
   240  	case bit < 16:
   241  		bit = (bit + 1 - 8) << 2
   242  		return Bitset(singleton[bit-2 : bit])
   243  	case bit < 24:
   244  		bit = (bit + 1 - 16) << 2
   245  		return Bitset(singleton[bit-3 : bit])
   246  	case bit < 32:
   247  		bit = (bit + 1 - 24) << 2
   248  		return Bitset(singleton[bit-4 : bit])
   249  	default:
   250  		words := make([]byte, bitsetWordSize(bit))
   251  		words[bit/bitsetWidth] |= 1 << (bit % bitsetWidth)
   252  		return toBitset(words)
   253  	}
   254  }