Skip to content

Commit a7e2bb2

Browse files
committed
Fix some bit-vector bugs that weren't covered by my
random test generator and flesh out the API. Swift SVN r24304
1 parent 3530e54 commit a7e2bb2

File tree

3 files changed

+175
-32
lines changed

3 files changed

+175
-32
lines changed

Diff for: include/swift/Basic/ClusteredBitVector.h

+104-30
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,27 @@ class ClusteredBitVector {
115115
/// Return true if this vector is not using out-of-line storage and
116116
/// does not have any bits set. This is a special-case representation
117117
/// where the capacity can be smaller than the length.
118+
///
119+
/// This is a necessary condition for hasSufficientChunkStorage(),
120+
/// and it's quicker to test, so a lot of routines in this class
121+
/// that need to work on chunk data in the general case test this
122+
/// first.
118123
bool isInlineAndAllClear() const {
119124
assert(!hasOutOfLineData() || Data != 0);
120125
return Data == 0;
121126
}
122127

128+
/// Return true if this vector is not in the special case where the
129+
/// capacity is smaller than the length. If this is true, then
130+
/// it's safe to call routines like getChunks().
131+
bool hasSufficientChunkStorage() const {
132+
return !(isInlineAndAllClear() && LengthInBits > ChunkSizeInBits);
133+
}
134+
123135
/// Return the number of chunks required in order to store the full
124136
/// length (not capacity) of this bit vector. This may be greater
125-
/// than the capacity in exactly one case, (2a).
137+
/// than the capacity in exactly one case, (2a), i.e.
138+
/// !hasSufficientChunkStorage().
126139
size_t getLengthInChunks() const {
127140
return getNumChunksForBits(LengthInBits);
128141
}
@@ -147,7 +160,7 @@ class ClusteredBitVector {
147160
/// that it's using out-of-line storage.
148161
size_t getOutOfLineCapacityInBits() const {
149162
assert(hasOutOfLineData());
150-
return (size_t) getOutOfLineData()[-1];
163+
return (size_t) getOutOfLineChunksPtr()[-1];
151164
}
152165

153166
/// Return the current capacity of this bit vector, in chunks, given
@@ -157,36 +170,38 @@ class ClusteredBitVector {
157170
}
158171

159172
/// Return a pointer to the data storage of this bit vector.
160-
ChunkType *getData() {
161-
return (hasOutOfLineData() ? getOutOfLineData() : &Data);
173+
ChunkType *getChunksPtr() {
174+
assert(hasSufficientChunkStorage());
175+
return (hasOutOfLineData() ? getOutOfLineChunksPtr() : &Data);
162176
}
163-
const ChunkType *getData() const {
164-
return (hasOutOfLineData() ? getOutOfLineData() : &Data);
177+
const ChunkType *getChunksPtr() const {
178+
assert(hasSufficientChunkStorage());
179+
return (hasOutOfLineData() ? getOutOfLineChunksPtr() : &Data);
165180
}
166181

167182
MutableArrayRef<ChunkType> getChunks() {
168-
assert(!isInlineAndAllClear());
169-
return { getData(), getLengthInChunks() };
183+
assert(hasSufficientChunkStorage());
184+
return { getChunksPtr(), getLengthInChunks() };
170185
}
171186
ArrayRef<ChunkType> getChunks() const {
172-
assert(!isInlineAndAllClear());
173-
return { getData(), getLengthInChunks() };
187+
assert(hasSufficientChunkStorage());
188+
return { getChunksPtr(), getLengthInChunks() };
174189
}
175190

176191
MutableArrayRef<ChunkType> getOutOfLineChunks() {
177-
return { getOutOfLineData(), getLengthInChunks() };
192+
return { getOutOfLineChunksPtr(), getLengthInChunks() };
178193
}
179194
ArrayRef<ChunkType> getOutOfLineChunks() const {
180-
return { getOutOfLineData(), getLengthInChunks() };
195+
return { getOutOfLineChunksPtr(), getLengthInChunks() };
181196
}
182197

183198
/// Return a pointer to the data storage of this bit vector, given
184199
/// that it's using out-of-line storage.
185-
ChunkType *getOutOfLineData() {
200+
ChunkType *getOutOfLineChunksPtr() {
186201
assert(hasOutOfLineData());
187202
return reinterpret_cast<ChunkType*>(Data);
188203
}
189-
const ChunkType *getOutOfLineData() const {
204+
const ChunkType *getOutOfLineChunksPtr() const {
190205
assert(hasOutOfLineData());
191206
return reinterpret_cast<const ChunkType*>(Data);
192207
}
@@ -232,10 +247,10 @@ class ClusteredBitVector {
232247
if (otherLengthInChunks <= getOutOfLineCapacityInChunks()) {
233248
LengthInBits = other.LengthInBits;
234249
if (other.isInlineAndAllClear()) {
235-
memset(getOutOfLineData(), 0,
250+
memset(getOutOfLineChunksPtr(), 0,
236251
otherLengthInChunks * sizeof(ChunkType));
237252
} else {
238-
memcpy(getOutOfLineData(), other.getData(),
253+
memcpy(getOutOfLineChunksPtr(), other.getChunksPtr(),
239254
otherLengthInChunks * sizeof(ChunkType));
240255
}
241256
return *this;
@@ -288,10 +303,6 @@ class ClusteredBitVector {
288303
/// Reserve space for an extra N bits. This may unnecessarily force
289304
/// the vector to use an out-of-line representation.
290305
void reserveExtra(size_t numBits) {
291-
// Other parts of the implementation rely on this method putting
292-
// the vector in a state where getData() can be safely assigned
293-
// into.
294-
295306
auto requiredBits = LengthInBits + numBits;
296307
if (requiredBits > getCapacityInBits()) {
297308
auto requiredChunks = getNumChunksForBits(requiredBits);
@@ -307,6 +318,7 @@ class ClusteredBitVector {
307318

308319
reallocate(chunkCount);
309320
}
321+
// Postcondition: hasSufficientChunkStorage().
310322
}
311323

312324
/// Reserve space for a total of N bits. This may unnecessarily
@@ -315,6 +327,7 @@ class ClusteredBitVector {
315327
if (requiredSize > getCapacityInBits()) {
316328
reallocate(getNumChunksForBits(requiredSize));
317329
}
330+
// Postcondition: hasSufficientChunkStorage().
318331
}
319332

320333
/// Append the bits from the given vector to this one.
@@ -347,7 +360,7 @@ class ClusteredBitVector {
347360
if (other.isInlineAndAllClear()) {
348361
appendConstantBitsReserved(other.size(), 0);
349362
} else {
350-
appendReserved(other.size(), other.getData());
363+
appendReserved(other.size(), other.getChunksPtr());
351364
}
352365
}
353366

@@ -382,26 +395,39 @@ class ClusteredBitVector {
382395
appendConstantBitsReserved(numBits, 0);
383396
}
384397

398+
/// Extend the vector out to the given length with clear bits.
399+
void extendWithClearBits(size_t newSize) {
400+
assert(newSize >= size());
401+
appendClearBits(newSize - size());
402+
}
403+
404+
385405
/// Append a number of set bits to this vector.
386406
void appendSetBits(size_t numBits) {
387407
if (numBits == 0) return;
388408
reserveExtra(numBits);
389409
appendConstantBitsReserved(numBits, 1);
390410
}
391411

412+
/// Extend the vector out to the given length with set bits.
413+
void extendWithSetBits(size_t newSize) {
414+
assert(newSize >= size());
415+
appendSetBits(newSize - size());
416+
}
417+
392418
/// Test whether a particular bit is set.
393419
bool operator[](size_t i) const {
394420
assert(i < size());
395421
if (isInlineAndAllClear()) return false;
396-
return getData()[i / ChunkSizeInBits]
422+
return getChunks()[i / ChunkSizeInBits]
397423
& (ChunkType(1) << (i % ChunkSizeInBits));
398424
}
399425

400-
/// Intersect two vectors of the same size.
426+
/// Intersect a bit-vector of the same size into this vector.
401427
ClusteredBitVector &operator&=(const ClusteredBitVector &other) {
402428
assert(size() == other.size());
403429

404-
// If this vector is currently all-clear, this is a no-op.
430+
// If this vector is all-clear, this is a no-op.
405431
if (isInlineAndAllClear())
406432
return *this;
407433

@@ -414,27 +440,72 @@ class ClusteredBitVector {
414440

415441
// Otherwise, &= the chunks pairwise.
416442
auto chunks = getChunks();
417-
auto oi = other.getData();
443+
auto oi = other.getChunksPtr();
418444
for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i, ++oi) {
419445
*i &= *oi;
420446
}
421447
return *this;
422448
}
423449

450+
/// Union a bit-vector of the same size into this vector.
451+
ClusteredBitVector &operator|=(const ClusteredBitVector &other) {
452+
assert(size() == other.size());
453+
454+
// If the other vector is all-clear, this is a no-op.
455+
if (other.isInlineAndAllClear())
456+
return *this;
457+
458+
// If this vector is all-clear, we just copy the other.
459+
if (isInlineAndAllClear()) {
460+
return (*this = other);
461+
}
462+
463+
// Otherwise, |= the chunks pairwise.
464+
auto chunks = getChunks();
465+
auto oi = other.getChunksPtr();
466+
for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i, ++oi) {
467+
*i |= *oi;
468+
}
469+
return *this;
470+
}
471+
424472
/// Set bit i.
425473
void setBit(size_t i) {
426474
assert(i < size());
427475
if (isInlineAndAllClear()) {
428476
reserve(LengthInBits);
429477
}
430-
getData()[i / ChunkSizeInBits] |= (ChunkType(1) << (i % ChunkSizeInBits));
478+
getChunks()[i / ChunkSizeInBits] |= (ChunkType(1) << (i % ChunkSizeInBits));
431479
}
432480

433481
/// Clear bit i.
434482
void clearBit(size_t i) {
435483
assert(i < size());
436484
if (isInlineAndAllClear()) return;
437-
getData()[i / ChunkSizeInBits] |= (ChunkType(1) << (i % ChunkSizeInBits));
485+
getChunksPtr()[i / ChunkSizeInBits] &= ~(ChunkType(1) << (i % ChunkSizeInBits));
486+
}
487+
488+
/// Toggle bit i.
489+
void flipBit(size_t i) {
490+
assert(i < size());
491+
if (isInlineAndAllClear()) {
492+
reserve(LengthInBits);
493+
}
494+
getChunksPtr()[i / ChunkSizeInBits] ^= (ChunkType(1) << (i % ChunkSizeInBits));
495+
}
496+
497+
/// Toggle all the bits in this vector.
498+
void flipAll() {
499+
if (empty()) return;
500+
if (isInlineAndAllClear()) {
501+
reserve(LengthInBits);
502+
}
503+
for (auto &chunk : getChunks()) {
504+
chunk = ~chunk;
505+
}
506+
if (auto tailBits = size() % ChunkSizeInBits) {
507+
getChunks().back() &= ((ChunkType(1) << tailBits) - 1);
508+
}
438509
}
439510

440511
/// Set the length of this vector to zero, but do not release any capacity.
@@ -482,7 +553,7 @@ class ClusteredBitVector {
482553
CurChunkIndex = 0;
483554
NumChunks = 0;
484555
} else {
485-
Chunks = vector.getData();
556+
Chunks = vector.getChunksPtr();
486557
CurChunk = Chunks[0];
487558
CurChunkIndex = 0;
488559
NumChunks = vector.getLengthInChunks();
@@ -534,6 +605,9 @@ class ClusteredBitVector {
534605
/// the least significant bits of the number.
535606
llvm::APInt asAPInt() const;
536607

608+
/// Construct a bit-vector from an APInt.
609+
static ClusteredBitVector fromAPInt(const llvm::APInt &value);
610+
537611
/// Pretty-print the vector.
538612
void print(llvm::raw_ostream &out) const;
539613
void dump() const;
@@ -545,7 +619,7 @@ class ClusteredBitVector {
545619
void makeIndependentCopy() {
546620
assert(hasOutOfLineData());
547621
auto lengthToCopy = getLengthInChunks();
548-
allocateAndCopyFrom(getOutOfLineData(), lengthToCopy, lengthToCopy);
622+
allocateAndCopyFrom(getOutOfLineChunksPtr(), lengthToCopy, lengthToCopy);
549623
}
550624

551625
/// Reallocate this vector, copying the current data into the new space.
@@ -578,7 +652,7 @@ class ClusteredBitVector {
578652
/// Destroy the out of line data currently stored in this object.
579653
void destroy() {
580654
assert(hasOutOfLineData());
581-
delete[] (getOutOfLineData() - 1);
655+
delete[] (getOutOfLineChunksPtr() - 1);
582656
}
583657

584658
/// Append a certain number of constant bits to this vector, given

Diff for: lib/Basic/ClusteredBitVector.cpp

+15-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121

2222
using namespace swift;
2323

24+
ClusteredBitVector ClusteredBitVector::fromAPInt(const llvm::APInt &bits) {
25+
// This is not a very efficient algorithm.
26+
ClusteredBitVector result;
27+
for (unsigned i = 0, e = bits.getBitWidth(); i != e; ++i) {
28+
if (bits[i]) {
29+
result.appendSetBits(1);
30+
} else {
31+
result.appendClearBits(1);
32+
}
33+
}
34+
return result;
35+
}
36+
2437
llvm::APInt ClusteredBitVector::asAPInt() const {
2538
if (isInlineAndAllClear()) {
2639
return llvm::APInt(size(), 0);
@@ -36,7 +49,7 @@ void ClusteredBitVector::reallocate(size_t newCapacityInChunks) {
3649
// will still apply, and we just need to copy the old data into
3750
// the new allocation.
3851
if (hasOutOfLineData()) {
39-
auto oldData = getOutOfLineData();
52+
auto oldData = getOutOfLineChunksPtr();
4053
allocateAndCopyFrom(oldData, newCapacityInChunks, getLengthInChunks());
4154
delete[] (oldData - 1);
4255
return;
@@ -80,7 +93,7 @@ void ClusteredBitVector::appendReserved(size_t numBits,
8093
// Check whether the current end of the vector is a clean multiple
8194
// of the chunk size.
8295
auto offset = LengthInBits % ChunkSizeInBits;
83-
ChunkType *nextChunk = &getData()[LengthInBits / ChunkSizeInBits];
96+
ChunkType *nextChunk = &getChunksPtr()[LengthInBits / ChunkSizeInBits];
8497

8598
// Now we can go ahead and add in the right number of extra bits.
8699
LengthInBits += numBits;

Diff for: unittests/Basic/ClusteredBitVectorTest.cpp

+56
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,59 @@ TEST(ClusteredBitVector, Enumeration) {
152152
EXPECT_EQ(Opt(), enumerator.findNext());
153153
EXPECT_EQ(Opt(), enumerator.findNext());
154154
}
155+
156+
TEST(ClusteredBitVector, SetClearBit) {
157+
ClusteredBitVector vec;
158+
vec.appendClearBits(64);
159+
vec.setBit(3);
160+
vec.setBit(7);
161+
vec.setBit(7);
162+
vec.setBit(63);
163+
EXPECT_EQ(3u, vec.count());
164+
EXPECT_EQ(true, vec[3]);
165+
EXPECT_EQ(true, vec[7]);
166+
EXPECT_EQ(true, vec[63]);
167+
EXPECT_EQ(false, vec[0]);
168+
EXPECT_EQ(false, vec[14]);
169+
EXPECT_EQ(false, vec[62]);
170+
171+
vec.clearBit(63);
172+
EXPECT_EQ(2u, vec.count());
173+
EXPECT_EQ(false, vec[63]);
174+
}
175+
176+
TEST(ClusteredBitVector, FlipAllSmall) {
177+
ClusteredBitVector vec;
178+
vec.appendClearBits(48);
179+
EXPECT_EQ(false, vec[12]);
180+
EXPECT_EQ(0u, vec.count());
181+
vec.flipAll();
182+
EXPECT_EQ(true, vec[12]);
183+
EXPECT_EQ(48u, vec.count());
184+
vec.clearBit(7);
185+
EXPECT_EQ(true, vec[12]);
186+
EXPECT_EQ(false, vec[7]);
187+
EXPECT_EQ(47u, vec.count());
188+
vec.flipAll();
189+
EXPECT_EQ(false, vec[12]);
190+
EXPECT_EQ(true, vec[7]);
191+
EXPECT_EQ(1u, vec.count());
192+
}
193+
194+
TEST(ClusteredBitVector, FlipAllBig) {
195+
ClusteredBitVector vec;
196+
vec.appendClearBits(163);
197+
EXPECT_EQ(false, vec[12]);
198+
EXPECT_EQ(0u, vec.count());
199+
vec.flipAll();
200+
EXPECT_EQ(true, vec[12]);
201+
EXPECT_EQ(163u, vec.count());
202+
vec.clearBit(7);
203+
EXPECT_EQ(true, vec[12]);
204+
EXPECT_EQ(false, vec[7]);
205+
EXPECT_EQ(162u, vec.count());
206+
vec.flipAll();
207+
EXPECT_EQ(false, vec[12]);
208+
EXPECT_EQ(true, vec[7]);
209+
EXPECT_EQ(1u, vec.count());
210+
}

0 commit comments

Comments
 (0)