github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/bitmask/bitmask.go (about)

     1  /*
     2  Copyright 2022 The Katalyst 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 bitmask
    18  
    19  import (
    20  	"fmt"
    21  	"math/bits"
    22  	"strconv"
    23  )
    24  
    25  // BitMask interface allows hint providers to create BitMasks for TopologyHints
    26  type BitMask interface {
    27  	Add(bits ...int) error
    28  	Remove(bits ...int) error
    29  	And(masks ...BitMask)
    30  	Or(masks ...BitMask)
    31  	Clear()
    32  	Fill()
    33  	IsEqual(mask BitMask) bool
    34  	IsEmpty() bool
    35  	IsSet(bit int) bool
    36  	AnySet(bits []int) bool
    37  	IsNarrowerThan(mask BitMask) bool
    38  	String() string
    39  	Count() int
    40  	GetBits() []int
    41  }
    42  
    43  type bitMask uint64
    44  
    45  // NewEmptyBitMask creates a new, empty BitMask
    46  func NewEmptyBitMask() BitMask {
    47  	s := bitMask(0)
    48  	return &s
    49  }
    50  
    51  // NewBitMask creates a new BitMask
    52  func NewBitMask(bits ...int) (BitMask, error) {
    53  	s := bitMask(0)
    54  	err := (&s).Add(bits...)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return &s, nil
    59  }
    60  
    61  // Add adds the bits with topology affinity to the BitMask
    62  func (s *bitMask) Add(bits ...int) error {
    63  	mask := *s
    64  	for _, i := range bits {
    65  		if i < 0 || i >= 64 {
    66  			return fmt.Errorf("bit number must be in range 0-63")
    67  		}
    68  		mask |= 1 << uint64(i)
    69  	}
    70  	*s = mask
    71  	return nil
    72  }
    73  
    74  // Remove removes specified bits from BitMask
    75  func (s *bitMask) Remove(bits ...int) error {
    76  	mask := *s
    77  	for _, i := range bits {
    78  		if i < 0 || i >= 64 {
    79  			return fmt.Errorf("bit number must be in range 0-63")
    80  		}
    81  		mask &^= 1 << uint64(i)
    82  	}
    83  	*s = mask
    84  	return nil
    85  }
    86  
    87  // And performs and operation on all bits in masks
    88  func (s *bitMask) And(masks ...BitMask) {
    89  	for _, m := range masks {
    90  		*s &= *m.(*bitMask)
    91  	}
    92  }
    93  
    94  // Or performs or operation on all bits in masks
    95  func (s *bitMask) Or(masks ...BitMask) {
    96  	for _, m := range masks {
    97  		*s |= *m.(*bitMask)
    98  	}
    99  }
   100  
   101  // Clear resets all bits in mask to zero
   102  func (s *bitMask) Clear() {
   103  	*s = 0
   104  }
   105  
   106  // Fill sets all bits in mask to one
   107  func (s *bitMask) Fill() {
   108  	*s = bitMask(^uint64(0))
   109  }
   110  
   111  // IsEmpty checks mask to see if all bits are zero
   112  func (s *bitMask) IsEmpty() bool {
   113  	return *s == 0
   114  }
   115  
   116  // IsSet checks bit in mask to see if bit is set to one
   117  func (s *bitMask) IsSet(bit int) bool {
   118  	if bit < 0 || bit >= 64 {
   119  		return false
   120  	}
   121  	return (*s & (1 << uint64(bit))) > 0
   122  }
   123  
   124  // AnySet checks bit in mask to see if any provided bit is set to one
   125  func (s *bitMask) AnySet(bits []int) bool {
   126  	for _, b := range bits {
   127  		if s.IsSet(b) {
   128  			return true
   129  		}
   130  	}
   131  	return false
   132  }
   133  
   134  // IsEqual checks if masks are equal
   135  func (s *bitMask) IsEqual(mask BitMask) bool {
   136  	return *s == *mask.(*bitMask)
   137  }
   138  
   139  // IsNarrowerThan checks if one mask is narrower than another.
   140  //
   141  // A mask is said to be "narrower" than another if it has lets bits set. If the
   142  // same number of bits are set in both masks, then the mask with more
   143  // lower-numbered bits set wins out.
   144  func (s *bitMask) IsNarrowerThan(mask BitMask) bool {
   145  	if s.Count() == mask.Count() {
   146  		if *s < *mask.(*bitMask) {
   147  			return true
   148  		}
   149  	}
   150  	return s.Count() < mask.Count()
   151  }
   152  
   153  // String converts mask to string
   154  func (s *bitMask) String() string {
   155  	grouping := 2
   156  	for shift := 64 - grouping; shift > 0; shift -= grouping {
   157  		if *s > (1 << uint(shift)) {
   158  			return fmt.Sprintf("%0"+strconv.Itoa(shift+grouping)+"b", *s)
   159  		}
   160  	}
   161  	return fmt.Sprintf("%0"+strconv.Itoa(grouping)+"b", *s)
   162  }
   163  
   164  // Count counts number of bits in mask set to one
   165  func (s *bitMask) Count() int {
   166  	return bits.OnesCount64(uint64(*s))
   167  }
   168  
   169  // Getbits returns each bit number with bits set to one
   170  func (s *bitMask) GetBits() []int {
   171  	var bits []int
   172  	for i := uint64(0); i < 64; i++ {
   173  		if (*s & (1 << i)) > 0 {
   174  			bits = append(bits, int(i))
   175  		}
   176  	}
   177  	return bits
   178  }
   179  
   180  // And is a package level implementation of 'and' between first and masks
   181  func And(first BitMask, masks ...BitMask) BitMask {
   182  	s := *first.(*bitMask)
   183  	s.And(masks...)
   184  	return &s
   185  }
   186  
   187  // Or is a package level implementation of 'or' between first and masks
   188  func Or(first BitMask, masks ...BitMask) BitMask {
   189  	s := *first.(*bitMask)
   190  	s.Or(masks...)
   191  	return &s
   192  }
   193  
   194  // IterateBitMasks iterates all possible masks from a list of bits,
   195  // issuing a callback on each mask.
   196  func IterateBitMasks(bits []int, callback func(BitMask)) {
   197  	var iterate func(bits, accum []int, size int)
   198  	iterate = func(bits, accum []int, size int) {
   199  		if len(accum) == size {
   200  			mask, _ := NewBitMask(accum...)
   201  			callback(mask)
   202  			return
   203  		}
   204  		for i := range bits {
   205  			iterate(bits[i+1:], append(accum, bits[i]), size)
   206  		}
   207  	}
   208  
   209  	for i := 1; i <= len(bits); i++ {
   210  		iterate(bits, []int{}, i)
   211  	}
   212  }