1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "util/yet_another_bit_vector.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "util/osp_logging.h"
11
12 namespace openscreen {
13
14 namespace {
15
16 // Returns a bitmask where all the bits whose positions are in the range
17 // [begin,begin+count) are set, and all other bits are cleared.
MakeBitmask(int begin,int count)18 constexpr uint64_t MakeBitmask(int begin, int count) {
19 // Form a contiguous sequence of bits by subtracting one from the appropriate
20 // power of 2. Set all the bits if count >= 64.
21 const uint64_t bits_in_wrong_position =
22 (count >= std::numeric_limits<uint64_t>::digits)
23 ? std::numeric_limits<uint64_t>::max()
24 : ((uint64_t{1} << count) - 1);
25
26 // Now shift the contiguous sequence of bits into the correct position.
27 return bits_in_wrong_position << begin;
28 }
29
30 } // namespace
31
YetAnotherBitVector()32 YetAnotherBitVector::YetAnotherBitVector() : size_(0), bits_{.as_integer = 0} {}
33
YetAnotherBitVector(int size,YetAnotherBitVector::Fill fill)34 YetAnotherBitVector::YetAnotherBitVector(int size,
35 YetAnotherBitVector::Fill fill) {
36 InitializeForNewSize(size, fill);
37 }
38
~YetAnotherBitVector()39 YetAnotherBitVector::~YetAnotherBitVector() {
40 if (using_array_storage()) {
41 delete[] bits_.as_array;
42 }
43 }
44
YetAnotherBitVector(YetAnotherBitVector && other)45 YetAnotherBitVector::YetAnotherBitVector(YetAnotherBitVector&& other)
46 : size_(other.size_), bits_(other.bits_) {
47 other.size_ = 0;
48 other.bits_.as_integer = 0;
49 }
50
operator =(YetAnotherBitVector && other)51 YetAnotherBitVector& YetAnotherBitVector::operator=(
52 YetAnotherBitVector&& other) {
53 if (this == &other) {
54 return *this;
55 }
56 if (using_array_storage()) {
57 delete[] bits_.as_array;
58 }
59 size_ = other.size_;
60 bits_ = other.bits_;
61 other.size_ = 0;
62 other.bits_.as_integer = 0;
63 return *this;
64 }
65
IsSet(int pos) const66 bool YetAnotherBitVector::IsSet(int pos) const {
67 OSP_DCHECK_LT(pos, size_);
68 const uint64_t* const elem = Select(&pos);
69 return (*elem & (uint64_t{1} << pos)) != 0;
70 }
71
Set(int pos)72 void YetAnotherBitVector::Set(int pos) {
73 OSP_DCHECK_LT(pos, size_);
74 uint64_t* const elem = const_cast<uint64_t*>(Select(&pos));
75 *elem |= (uint64_t{1} << pos);
76 }
77
Clear(int pos)78 void YetAnotherBitVector::Clear(int pos) {
79 OSP_DCHECK_LT(pos, size_);
80 uint64_t* const elem = const_cast<uint64_t*>(Select(&pos));
81 *elem &= ~(uint64_t{1} << pos);
82 }
83
Resize(int new_size,YetAnotherBitVector::Fill fill)84 void YetAnotherBitVector::Resize(int new_size, YetAnotherBitVector::Fill fill) {
85 if (using_array_storage()) {
86 delete[] bits_.as_array;
87 }
88 InitializeForNewSize(new_size, fill);
89 }
90
SetAll()91 void YetAnotherBitVector::SetAll() {
92 // Implementation note: Set all bits except those in the last integer that are
93 // outside the defined range. This allows the other operations to become
94 // simpler and more efficient because they can assume the bits moving into the
95 // valid range are not set.
96
97 if (using_array_storage()) {
98 const int last_index = array_size() - 1;
99 uint64_t* const last = &bits_.as_array[last_index];
100 std::fill(&bits_.as_array[0], last, kAllBitsSet);
101 *last = MakeBitmask(0, size_ - (last_index * kBitsPerInteger));
102 } else {
103 bits_.as_integer = MakeBitmask(0, size_);
104 }
105 }
106
ClearAll()107 void YetAnotherBitVector::ClearAll() {
108 if (using_array_storage()) {
109 std::fill(&bits_.as_array[0], &bits_.as_array[array_size()], kNoBitsSet);
110 } else {
111 bits_.as_integer = kNoBitsSet;
112 }
113 }
114
ShiftRight(int steps)115 void YetAnotherBitVector::ShiftRight(int steps) {
116 // Negative |steps| should probably mean "shift left," but this is not
117 // implemented.
118 OSP_DCHECK_GE(steps, 0);
119 OSP_DCHECK_LE(steps, size_);
120
121 if (using_array_storage()) {
122 // If |steps| is greater than one integer's worth of bits, first shift the
123 // array elements right. This is effectively shifting all the bits right by
124 // some multiple of 64.
125 const int num_integers = array_size();
126 if (steps >= kBitsPerInteger) {
127 const int integer_steps = steps / kBitsPerInteger;
128 for (int i = integer_steps; i < num_integers; ++i) {
129 bits_.as_array[i - integer_steps] = bits_.as_array[i];
130 }
131 std::fill(&bits_.as_array[num_integers - integer_steps],
132 &bits_.as_array[num_integers], kNoBitsSet);
133 steps %= kBitsPerInteger;
134 }
135
136 // With |steps| now less than 64, shift the bits right within each array
137 // element. Start from the back of the array, working towards the front, and
138 // propagating any bits that are moving across array elements.
139 uint64_t incoming_carry_bits = 0;
140 const uint64_t outgoing_mask = MakeBitmask(0, steps);
141 for (int i = num_integers; i-- > 0;) {
142 const uint64_t outgoing_carry_bits = bits_.as_array[i] & outgoing_mask;
143 bits_.as_array[i] >>= steps;
144 bits_.as_array[i] |= (incoming_carry_bits << (kBitsPerInteger - steps));
145 incoming_carry_bits = outgoing_carry_bits;
146 }
147 } else {
148 if (steps < kBitsPerInteger) {
149 bits_.as_integer >>= steps;
150 } else {
151 bits_.as_integer = 0;
152 }
153 }
154 }
155
FindFirstSet() const156 int YetAnotherBitVector::FindFirstSet() const {
157 // Almost all processors provide a single instruction to "count trailing
158 // zeros" in an integer, which is great because this is the same as the
159 // 0-based index of the first set bit. So, have the compiler use that
160 // whenever it's available. However, note that the intrinsic (and the CPU
161 // instruction used) provides undefined results when operating on zero; and
162 // so that special case is checked and handled.
163 #if defined(__clang__) || defined(__GNUC__)
164 #define CountTrailingZeros(bits) __builtin_ctzll(bits)
165 #else
166 const auto CountTrailingZeros = [](uint64_t bits) -> int {
167 // Based on one of the public domain "Bit Twiddling Hacks" heuristics:
168 // https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel
169 // clang-format off
170 bits &= ~bits + 1;
171 int count = kBitsPerInteger;
172 if (bits) --count;
173 if (bits & UINT64_C(0x00000000ffffffff)) count -= 32;
174 if (bits & UINT64_C(0x0000ffff0000ffff)) count -= 16;
175 if (bits & UINT64_C(0x00ff00ff00ff00ff)) count -= 8;
176 if (bits & UINT64_C(0x0f0f0f0f0f0f0f0f)) count -= 4;
177 if (bits & UINT64_C(0x3333333333333333)) count -= 2;
178 if (bits & UINT64_C(0x5555555555555555)) count -= 1;
179 return count;
180 // clang-format on
181 };
182 #endif
183
184 if (using_array_storage()) {
185 for (int i = 0, end = array_size(); i < end; ++i) {
186 if (bits_.as_array[i] != 0) {
187 return (i * kBitsPerInteger) + CountTrailingZeros(bits_.as_array[i]);
188 }
189 }
190 return size_; // All bits are not set.
191 }
192 return (bits_.as_integer != 0) ? CountTrailingZeros(bits_.as_integer) : size_;
193 }
194
CountBitsSet(int begin,int end) const195 int YetAnotherBitVector::CountBitsSet(int begin, int end) const {
196 OSP_DCHECK_LE(0, begin);
197 OSP_DCHECK_LE(begin, end);
198 OSP_DCHECK_LE(end, size_);
199
200 // Almost all processors provide a single instruction to "count the number of
201 // bits set" in an integer. So, have the compiler use that whenever it's
202 // available.
203 #if defined(__clang__) || defined(__GNUC__)
204 #define PopCount(bits) __builtin_popcountll(bits)
205 #else
206 const auto PopCount = [](uint64_t bits) -> int {
207 // Based on one of the public domain "Bit Twiddling Hacks" heuristics:
208 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
209 constexpr int kBitsPerByte = 8;
210 bits = bits - ((bits >> 1) & (kAllBitsSet / 3));
211 bits = (bits & (kAllBitsSet / 15 * 3)) +
212 ((bits >> 2) & (kAllBitsSet / 15 * 3));
213 bits = (bits + (bits >> 4)) & (kAllBitsSet / 255 * 15);
214 const uint64_t count =
215 (bits * (kAllBitsSet / 255)) >> ((sizeof(uint64_t) - 1) * kBitsPerByte);
216 return static_cast<int>(count);
217 };
218 #endif
219
220 int count;
221 if (using_array_storage()) {
222 const int first = begin / kBitsPerInteger;
223 const int last = (end - 1) / kBitsPerInteger;
224 if (first == last) {
225 count = PopCount(bits_.as_array[first] &
226 MakeBitmask(begin % kBitsPerInteger, end - begin));
227 } else if (first < last) {
228 // Count a subset of the bits in the first and last integers (according to
229 // |begin| and |end|), and all of the bits in the integers in-between.
230 const uint64_t* p = &bits_.as_array[first];
231 count = PopCount((*p) &
232 MakeBitmask(begin % kBitsPerInteger, kBitsPerInteger));
233 for (++p; p != &bits_.as_array[last]; ++p) {
234 count += PopCount(*p);
235 }
236 count += PopCount((*p) & MakeBitmask(0, end - (last * kBitsPerInteger)));
237 } else {
238 count = 0;
239 }
240 } else {
241 count = PopCount(bits_.as_integer & MakeBitmask(begin, end - begin));
242 }
243 return count;
244 }
245
InitializeForNewSize(int new_size,Fill fill)246 void YetAnotherBitVector::InitializeForNewSize(int new_size, Fill fill) {
247 OSP_DCHECK_GE(new_size, 0);
248 size_ = new_size;
249 if (using_array_storage()) {
250 bits_.as_array = new uint64_t[array_size()];
251 }
252 if (fill == SET) {
253 SetAll();
254 } else {
255 ClearAll();
256 }
257 }
258
Select(int * pos) const259 const uint64_t* YetAnotherBitVector::Select(int* pos) const {
260 if (using_array_storage()) {
261 const int index = *pos / kBitsPerInteger;
262 *pos %= kBitsPerInteger;
263 return &bits_.as_array[index];
264 }
265 return &bits_.as_integer;
266 }
267
268 // NOTE: These declarations can be removed when C++17 compliance is mandatory
269 // for all embedders, as static constexpr members can be declared inline.
270 constexpr int YetAnotherBitVector::kBitsPerInteger;
271 constexpr uint64_t YetAnotherBitVector::kAllBitsSet;
272 constexpr uint64_t YetAnotherBitVector::kNoBitsSet;
273
274 } // namespace openscreen
275