bmv2
Designing your own switch target with bmv2
dynamic_bitset.h
1 /* Copyright 2021 VMware, 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 
16 /*
17  * Antonin Bas
18  *
19  */
20 
21 #ifndef BM_BM_SIM_DYNAMIC_BITSET_H_
22 #define BM_BM_SIM_DYNAMIC_BITSET_H_
23 
24 #include <bitset>
25 #include <cassert>
26 #include <limits>
27 #include <vector>
28 
29 namespace bm {
30 
31 template <typename T>
32 inline int find_lowest_bit(T v);
33 
34 template<>
35 inline int find_lowest_bit<uint32_t>(uint32_t v) {
36  static const int kMultiplyDeBruijnBitPosition[32] = {
37  0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
38  31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
39  };
40  return kMultiplyDeBruijnBitPosition[((v & -v) * 0x077CB531U) >> 27];
41 }
42 
43 template<>
44 inline int find_lowest_bit<uint64_t>(uint64_t v) {
45  auto sw = static_cast<uint32_t>(v & 0xFFFFFFFF);
46  return (sw == 0) ?
47  32 + find_lowest_bit<uint32_t>(static_cast<uint32_t>(v >> 32)) :
48  find_lowest_bit<uint32_t>(sw);
49 }
50 
51 class DynamicBitset {
52  public:
53  using Block = unsigned long; // NOLINT(runtime/int)
54  using Backend = std::vector<Block>;
55  using size_type = Backend::size_type;
56 
57  static constexpr size_type npos = static_cast<size_type>(-1);
58 
59  size_type size() const {
60  return num_bits;
61  }
62 
63  size_type count() const {
64  return num_bits_set;
65  }
66 
67  bool test(size_type idx) const {
68  assert(idx < num_bits);
69  auto word_idx = static_cast<size_type>(idx / block_width);
70  auto bit_idx = static_cast<int>(idx % block_width);
71  return words[word_idx] & static_cast<Block>(1) << bit_idx;
72  }
73 
74  bool set(size_type idx) {
75  assert(idx < num_bits);
76  auto word_idx = static_cast<size_type>(idx / block_width);
77  auto bit_idx = static_cast<int>(idx % block_width);
78  auto mask = static_cast<Block>(1) << bit_idx;
79  auto bit = words[word_idx] & mask;
80  words[word_idx] |= mask;
81  num_bits_set += (bit >> bit_idx) ^ 1;
82  return (bit == 0);
83  }
84 
85  bool reset(size_type idx) {
86  assert(idx < num_bits);
87  auto word_idx = static_cast<size_type>(idx / block_width);
88  auto bit_idx = static_cast<int>(idx % block_width);
89  auto mask = static_cast<Block>(1) << bit_idx;
90  auto bit = words[word_idx] & mask;
91  words[word_idx] &= ~mask;
92  num_bits_set -= (bit >> bit_idx);
93  return (bit != 0);
94  }
95 
96  void push_back(bool v) {
97  auto bit_idx = static_cast<int>(num_bits % block_width);
98  if (bit_idx == 0) words.push_back(0);
99  num_bits++;
100  if (v) set(num_bits - 1);
101  }
102 
103  void clear() {
104  words.clear();
105  num_bits = 0;
106  num_bits_set = 0;
107  }
108 
109  size_type find_first() const {
110  return find_from_block(0);
111  }
112 
113  size_type find_next(size_type idx) const {
114  idx++;
115  if (idx >= num_bits) return npos;
116  auto word_idx = static_cast<size_type>(idx / block_width);
117  auto bit_idx = static_cast<int>(idx % block_width);
118  auto sw = words[word_idx] & (ones << bit_idx);
119  if (sw == 0) return find_from_block(word_idx + 1);
120  return word_idx * block_width + find_lowest_bit(sw);
121  }
122 
123  size_type find_unset_first() const {
124  return find_unset_from_block(0);
125  }
126 
127  size_type find_unset_next(size_type idx) const {
128  idx++;
129  if (idx >= num_bits) return npos;
130  auto word_idx = static_cast<size_type>(idx / block_width);
131  auto bit_idx = static_cast<int>(idx % block_width);
132  auto sw = words[word_idx] | ~(ones << bit_idx);
133  if (sw == ones) return find_unset_from_block(word_idx + 1);
134  return word_idx * block_width + find_lowest_bit(~sw);
135  }
136 
137  bool operator==(const DynamicBitset &other) const {
138  return (num_bits == other.num_bits) && (words == other.words);
139  }
140 
141  bool operator!=(const DynamicBitset &other) const {
142  return !(*this == other);
143  }
144 
145  void resize(size_type new_num_bits) {
146  num_bits = new_num_bits;
147  words.resize((num_bits + block_width - 1) / block_width);
148  }
149 
150  private:
151  size_type find_from_block(size_type word_idx) const {
152  for (size_type i = word_idx; i < words.size(); i++) {
153  if (words[i] == 0) continue;
154  return i * block_width + find_lowest_bit(words[i]);
155  }
156  return npos;
157  }
158 
159  size_type find_unset_from_block(size_type word_idx) const {
160  for (size_type i = word_idx; i < words.size(); i++) {
161  if (words[i] == ones) continue;
162  return i * block_width + find_lowest_bit(~words[i]);
163  }
164  return npos;
165  }
166 
167  static constexpr size_type block_width = std::numeric_limits<Block>::digits;
168  static constexpr Block ones = ~static_cast<Block>(0);
169  Backend words;
170  size_type num_bits{0};
171  size_type num_bits_set{0};
172 };
173 
174 } // namespace bm
175 
176 #endif // BM_BM_SIM_DYNAMIC_BITSET_H_