bitmask.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package bitmask
  14. import (
  15. "fmt"
  16. "math/bits"
  17. "strconv"
  18. )
  19. // BitMask interface allows hint providers to create BitMasks for TopologyHints
  20. type BitMask interface {
  21. Add(bits ...int) error
  22. Remove(bits ...int) error
  23. And(masks ...BitMask)
  24. Or(masks ...BitMask)
  25. Clear()
  26. Fill()
  27. IsEqual(mask BitMask) bool
  28. IsEmpty() bool
  29. IsSet(bit int) bool
  30. IsNarrowerThan(mask BitMask) bool
  31. String() string
  32. Count() int
  33. GetBits() []int
  34. }
  35. type bitMask uint64
  36. // NewEmptyBitMask creates a new, empty BitMask
  37. func NewEmptyBitMask() BitMask {
  38. s := bitMask(0)
  39. return &s
  40. }
  41. // NewBitMask creates a new BitMask
  42. func NewBitMask(bits ...int) (BitMask, error) {
  43. s := bitMask(0)
  44. err := (&s).Add(bits...)
  45. if err != nil {
  46. return nil, err
  47. }
  48. return &s, nil
  49. }
  50. // Add adds the bits with topology affinity to the BitMask
  51. func (s *bitMask) Add(bits ...int) error {
  52. mask := *s
  53. for _, i := range bits {
  54. if i < 0 || i >= 64 {
  55. return fmt.Errorf("bit number must be in range 0-63")
  56. }
  57. mask |= 1 << uint64(i)
  58. }
  59. *s = mask
  60. return nil
  61. }
  62. // Remove removes specified bits from BitMask
  63. func (s *bitMask) Remove(bits ...int) error {
  64. mask := *s
  65. for _, i := range bits {
  66. if i < 0 || i >= 64 {
  67. return fmt.Errorf("bit number must be in range 0-63")
  68. }
  69. mask &^= 1 << uint64(i)
  70. }
  71. *s = mask
  72. return nil
  73. }
  74. // And performs and operation on all bits in masks
  75. func (s *bitMask) And(masks ...BitMask) {
  76. for _, m := range masks {
  77. *s &= *m.(*bitMask)
  78. }
  79. }
  80. // Or performs or operation on all bits in masks
  81. func (s *bitMask) Or(masks ...BitMask) {
  82. for _, m := range masks {
  83. *s |= *m.(*bitMask)
  84. }
  85. }
  86. // Clear resets all bits in mask to zero
  87. func (s *bitMask) Clear() {
  88. *s = 0
  89. }
  90. // Fill sets all bits in mask to one
  91. func (s *bitMask) Fill() {
  92. *s = bitMask(^uint64(0))
  93. }
  94. // IsEmpty checks mask to see if all bits are zero
  95. func (s *bitMask) IsEmpty() bool {
  96. return *s == 0
  97. }
  98. // IsSet checks bit in mask to see if bit is set to one
  99. func (s *bitMask) IsSet(bit int) bool {
  100. if bit < 0 || bit >= 64 {
  101. return false
  102. }
  103. return (*s & (1 << uint64(bit))) > 0
  104. }
  105. // IsEqual checks if masks are equal
  106. func (s *bitMask) IsEqual(mask BitMask) bool {
  107. return *s == *mask.(*bitMask)
  108. }
  109. // IsNarrowerThan checks if one mask is narrower than another.
  110. //
  111. // A mask is said to be "narrower" than another if it has lets bits set. If the
  112. // same number of bits are set in both masks, then the mask with more
  113. // lower-numbered bits set wins out.
  114. func (s *bitMask) IsNarrowerThan(mask BitMask) bool {
  115. if s.Count() == mask.Count() {
  116. if *s < *mask.(*bitMask) {
  117. return true
  118. }
  119. }
  120. return s.Count() < mask.Count()
  121. }
  122. // String converts mask to string
  123. func (s *bitMask) String() string {
  124. grouping := 2
  125. for shift := 64 - grouping; shift > 0; shift -= grouping {
  126. if *s > (1 << uint(shift)) {
  127. return fmt.Sprintf("%0"+strconv.Itoa(shift+grouping)+"b", *s)
  128. }
  129. }
  130. return fmt.Sprintf("%0"+strconv.Itoa(grouping)+"b", *s)
  131. }
  132. // Count counts number of bits in mask set to one
  133. func (s *bitMask) Count() int {
  134. return bits.OnesCount64(uint64(*s))
  135. }
  136. // Getbits returns each bit number with bits set to one
  137. func (s *bitMask) GetBits() []int {
  138. var bits []int
  139. for i := uint64(0); i < 64; i++ {
  140. if (*s & (1 << i)) > 0 {
  141. bits = append(bits, int(i))
  142. }
  143. }
  144. return bits
  145. }
  146. // And is a package level implementation of 'and' between first and masks
  147. func And(first BitMask, masks ...BitMask) BitMask {
  148. s := *first.(*bitMask)
  149. s.And(masks...)
  150. return &s
  151. }
  152. // Or is a package level implementation of 'or' between first and masks
  153. func Or(first BitMask, masks ...BitMask) BitMask {
  154. s := *first.(*bitMask)
  155. s.Or(masks...)
  156. return &s
  157. }
  158. // IterateBitMasks iterates all possible masks from a list of bits,
  159. // issuing a callback on each mask.
  160. func IterateBitMasks(bits []int, callback func(BitMask)) {
  161. var iterate func(bits, accum []int, size int)
  162. iterate = func(bits, accum []int, size int) {
  163. if len(accum) == size {
  164. mask, _ := NewBitMask(accum...)
  165. callback(mask)
  166. return
  167. }
  168. for i := range bits {
  169. iterate(bits[i+1:], append(accum, bits[i]), size)
  170. }
  171. }
  172. for i := 1; i <= len(bits); i++ {
  173. iterate(bits, []int{}, i)
  174. }
  175. }