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