Skip to content

Commit 97a0ecc

Browse files
committed
Bitmap fixes and documentation
1 parent 2bf9686 commit 97a0ecc

File tree

2 files changed

+71
-67
lines changed

2 files changed

+71
-67
lines changed

include/scl/bitmap.h

Lines changed: 60 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <bitset>
2222
#include <cstddef>
23+
#include <iostream>
2324
#include <ostream>
2425
#include <stdexcept>
2526
#include <vector>
@@ -29,14 +30,45 @@
2930
namespace scl {
3031

3132
/**
32-
* @brief A simple bitmap.
33+
* @brief A bitmap.
3334
*
34-
* The Bitmap class holds bits. It serves some of the same functionality as
35-
* <code>std::vector<bool></code>. The implementation of Bitmap stores bits
36-
* packed in objects of type Bitmap::BlockType, current <code>unsigned
37-
* char</code>. As a consequence, Bitmap always stores a multiple of
38-
* <code>sizeof(Bitmap::BlockType) * 8</code> bits. Any unset bits are
39-
* guaranteed to be 0.
35+
* Bitmap serves a similar purpose as <code>std::vector<bool></code>, but
36+
* behaves a bit more like a "fixed-length vector of bits". The main
37+
* differentiator is that Bitmap implements various element-wise operations.
38+
*
39+
* A Bitmap is always a multiple of Bitmap::BlockType. One consequence of this
40+
* is that its "true size" must be tracked externally.
41+
*
42+
* @code
43+
* Bitmap bm(10); // this creates a Bitmap with space for 10 bits, with
44+
* // all 10 bits initialized to be 0.
45+
*
46+
* assert(bm.size() == 10);
47+
* assert(bm.numberOfBlocks() == ...); // a multiple of Bitmap::BITS_PER_BLOCK
48+
*
49+
* assert(bm.count() == 0)
50+
* bm.set(0, true); // set the first bit to be 1
51+
* assert(bm.count() == 1);
52+
* assert(bm.at(0));
53+
* assert(!bm.at(1));
54+
*
55+
* std::cout << bm;
56+
* // 0000000000000001
57+
*
58+
* Bitmap bm1(10);
59+
* Bitmap bm2(10);
60+
*
61+
* bm1.set(0, 1);
62+
* bm1.set(1, 1);
63+
* bm2.set(1, 1);
64+
* bm2.set(2, 1);
65+
*
66+
* assert((bm1 & bm2).count() == 1);
67+
* assert((bm1 ^ bm2).count() == 2);
68+
* assert((bm1 | bm2).count() == 3);
69+
*
70+
* assert((~bm1).count() == 8);
71+
* @endcode
4072
*/
4173
class Bitmap {
4274
public:
@@ -55,24 +87,22 @@ class Bitmap {
5587

5688
public:
5789
/**
58-
* @brief Create a Bitmap from an <code>std::vector<bool></code>.
59-
* @param bool_vec the <code>std::vector<bool></code>.
60-
* @return a Bitmap.
90+
* @brief Create a Bitmap from an STL vector of booleans.
6191
*/
6292
static Bitmap fromStdVecBool(const std::vector<bool>& bool_vec) {
6393
Bitmap bm(bool_vec.size());
6494
for (std::size_t i = 0; i < bool_vec.size(); ++i) {
6595
bm.set(i, bool_vec[i]);
6696
}
6797
return bm;
68-
} // LCOV_EXCL_LINE
98+
}
6999

70100
/**
71101
* @brief Construct a Bitmap with some initial size.
72-
* @param initial_size the initial size.
73102
*/
74103
Bitmap(std::size_t initial_size)
75-
: m_bits(ContainerType(bytesRequired(initial_size), 0)) {}
104+
: m_bits(ContainerType(bytesRequired(initial_size), 0)),
105+
m_true_size(initial_size) {}
76106

77107
/**
78108
* @brief Construct an empty Bitmap.
@@ -81,8 +111,6 @@ class Bitmap {
81111

82112
/**
83113
* @brief Check the bit at some position.
84-
* @param index the bit position.
85-
* @return true if the bit at position \p index is set and 0 otherwise.
86114
*/
87115
bool at(std::size_t index) const {
88116
const std::size_t block = index / BITS_PER_BLOCK;
@@ -92,8 +120,6 @@ class Bitmap {
92120

93121
/**
94122
* @brief Set the bit at some position.
95-
* @param index the position of the bit to set.
96-
* @param b the value to set.
97123
*/
98124
void set(std::size_t index, bool b) {
99125
const std::size_t block = index / BITS_PER_BLOCK;
@@ -103,7 +129,6 @@ class Bitmap {
103129

104130
/**
105131
* @brief Count the number of bits set in this Bitmap.
106-
* @return the population count of this Bitmap.
107132
*/
108133
std::size_t count() const {
109134
// https://stackoverflow.com/a/698108
@@ -118,36 +143,31 @@ class Bitmap {
118143

119144
/**
120145
* @brief Get the number of blocks this Bitmap uses.
121-
* @return the number of BlockType elements used by this Bitmap.
122146
*/
123147
std::size_t numberOfBlocks() const {
124148
return m_bits.size();
125149
}
126150

151+
std::size_t size() const {
152+
return m_true_size;
153+
}
154+
127155
/**
128156
* @brief Check if two bitmaps contain the same content.
129-
* @param bm0 the first Bitmap.
130-
* @param bm1 the second Bitmap.
131-
* @return true if \p bm0 and \p bm1 are equal, false otherwise.
132157
*/
133158
friend bool operator==(const Bitmap& bm0, const Bitmap& bm1) {
134159
return bm0.m_bits == bm1.m_bits;
135160
}
136161

137162
/**
138163
* @brief Check if two bitmaps are different.
139-
* @param bm0 the first Bitmap.
140-
* @param bm1 the second Bitmap.
141-
* @return false if \p bm0 and \p bm1 are equal, true otherwise.
142164
*/
143165
friend bool operator!=(const Bitmap& bm0, const Bitmap& bm1) {
144166
return !(bm0 == bm1);
145167
}
146168

147169
/**
148170
* @brief Write this bitmap to a stream.
149-
* @param os the stream.
150-
* @param m the bitmap.
151171
*/
152172
friend std::ostream& operator<<(std::ostream& os, const Bitmap& m) {
153173
for (const BlockType& block : m.m_bits) {
@@ -158,13 +178,10 @@ class Bitmap {
158178

159179
/**
160180
* @brief Compute the XOR of two bitmaps.
161-
* @param bm0 the first bitmap.
162-
* @param bm1 the other bitmap.
163181
*/
164182
friend Bitmap operator^(const Bitmap& bm0, const Bitmap& bm1) {
165183
validateSizes(bm0, bm1);
166-
Bitmap bm;
167-
bm.m_bits.resize(bm0.numberOfBlocks());
184+
Bitmap bm(bm0.m_true_size);
168185
for (std::size_t i = 0; i < bm.m_bits.size(); i++) {
169186
bm.m_bits[i] = bm0.m_bits[i] ^ bm1.m_bits[i];
170187
}
@@ -173,13 +190,10 @@ class Bitmap {
173190

174191
/**
175192
* @brief Compute the AND of two bitmaps.
176-
* @param bm0 the first bitmap.
177-
* @param bm1 the other bitmap.
178193
*/
179194
friend Bitmap operator&(const Bitmap& bm0, const Bitmap& bm1) {
180195
validateSizes(bm0, bm1);
181-
Bitmap bm;
182-
bm.m_bits.resize(bm0.numberOfBlocks());
196+
Bitmap bm(bm0.m_true_size);
183197
for (std::size_t i = 0; i < bm.m_bits.size(); i++) {
184198
bm.m_bits[i] = bm0.m_bits[i] & bm1.m_bits[i];
185199
}
@@ -188,13 +202,10 @@ class Bitmap {
188202

189203
/**
190204
* @brief Compute the OR of two bitmaps.
191-
* @param bm0 the first bitmap.
192-
* @param bm1 the other bitmap.
193205
*/
194206
friend Bitmap operator|(const Bitmap& bm0, const Bitmap& bm1) {
195207
validateSizes(bm0, bm1);
196-
Bitmap bm;
197-
bm.m_bits.resize(bm0.numberOfBlocks());
208+
Bitmap bm(bm0.m_true_size);
198209
for (std::size_t i = 0; i < bm.m_bits.size(); i++) {
199210
bm.m_bits[i] = bm0.m_bits[i] | bm1.m_bits[i];
200211
}
@@ -203,63 +214,47 @@ class Bitmap {
203214

204215
/**
205216
* @brief Compute the negation of a bitmap.
206-
* @param bm0 the bitmap.
207217
*/
208218
friend Bitmap operator~(const Bitmap& bm0) {
209-
Bitmap bm;
210-
bm.m_bits.resize(bm0.numberOfBlocks());
211-
for (std::size_t i = 0; i < bm.m_bits.size(); i++) {
219+
Bitmap bm(bm0.m_true_size);
220+
std::size_t i = 0;
221+
for (; i < bm.m_bits.size() - 1; i++) {
212222
bm.m_bits[i] = ~bm0.m_bits[i];
213223
}
224+
// for the last block we only negate some of the bits, potentially.
225+
const auto mask = (1 << (bm.m_true_size % BITS_PER_BLOCK)) - 1;
226+
bm.m_bits[i] = ~bm0.m_bits[i] & mask;
214227
return bm;
215228
}
216229

217230
private:
231+
friend Serializer<Bitmap>;
232+
218233
ContainerType m_bits;
234+
std::size_t m_true_size;
219235

220236
static constexpr std::size_t bytesRequired(std::size_t bits) {
221237
return bits == 0 ? 1 : (bits - 1) / (BITS_PER_BLOCK) + 1;
222238
}
223239

224240
static void validateSizes(const Bitmap& bm0, const Bitmap& bm1) {
225-
if (bm0.numberOfBlocks() != bm1.numberOfBlocks()) {
241+
if (bm0.size() != bm1.size()) {
226242
throw std::logic_error("bitmaps are different sizes");
227243
}
228244
}
229-
230-
friend Serializer<Bitmap>;
231245
};
232246

233247
/**
234248
* @brief Serializer for util::Bitmap types.
235249
*/
236250
template <>
237251
struct Serializer<Bitmap> {
238-
/**
239-
* @brief Get serialized size of a util::Bitmap.
240-
* @param bm the util::Bitmap.
241-
* @return the size in bytes of the \p bm.
242-
*/
243252
static std::size_t sizeOf(const Bitmap& bm) {
244253
return Serializer<Bitmap::ContainerType>::sizeOf(bm.m_bits);
245254
}
246-
247-
/**
248-
* @brief Write a util::Bitmap to a buffer.
249-
* @param bm the util::Bitmap.
250-
* @param buf the buffer.
251-
* @return the number of bytes written.
252-
*/
253255
static std::size_t write(const Bitmap& bm, unsigned char* buf) {
254256
return Serializer<Bitmap::ContainerType>::write(bm.m_bits, buf);
255257
}
256-
257-
/**
258-
* @brief Read a util::Bitmap from a buffer.
259-
* @param bm the util::Bitmap that will store the result.
260-
* @param buf the buffer to read the util::Bitmap from.
261-
* @return the number of bytes read from \p buf.
262-
*/
263258
static std::size_t read(Bitmap& bm, const unsigned char* buf) {
264259
return Serializer<Bitmap::ContainerType>::read(bm.m_bits, buf);
265260
}

test/scl/test_bitmap.cc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ TEST_CASE("Bitmap construct", "[util]") {
2727
Bitmap bm(10);
2828
REQUIRE(bm.numberOfBlocks() == 2);
2929
REQUIRE(bm.count() == 0);
30+
REQUIRE(bm.size() == 10);
3031

3132
Bitmap bm0;
3233
REQUIRE(bm0.numberOfBlocks() == 1);
3334
REQUIRE(bm0.count() == 0);
35+
REQUIRE(bm0.size() == 0);
3436
}
3537

3638
TEST_CASE("Bitmap get/set", "[util]") {
@@ -70,6 +72,7 @@ TEST_CASE("Bitmap XOR", "[util]") {
7072
REQUIRE(bm.at(0) == false);
7173
REQUIRE(bm.at(4) == true);
7274
REQUIRE(bm.at(5) == false);
75+
REQUIRE(bm.size() == bm0.size());
7376
}
7477

7578
TEST_CASE("Bitmap AND", "[util]") {
@@ -89,6 +92,7 @@ TEST_CASE("Bitmap AND", "[util]") {
8992
REQUIRE(bm.at(0) == true);
9093
REQUIRE(bm.at(4) == false);
9194
REQUIRE(bm.at(5) == false);
95+
REQUIRE(bm.size() == bm0.size());
9296
}
9397

9498
TEST_CASE("Bitmap OR", "[util]") {
@@ -108,19 +112,24 @@ TEST_CASE("Bitmap OR", "[util]") {
108112
REQUIRE(bm.at(0) == true);
109113
REQUIRE(bm.at(4) == true);
110114
REQUIRE(bm.at(5) == false);
115+
REQUIRE(bm.size() == bm0.size());
111116
}
112117

113118
TEST_CASE("Bitmap NEG", "[util]") {
114119
Bitmap bm0(10);
115120

116121
bm0.set(0, true);
117-
bm0.set(4, true);
122+
bm0.set(7, true);
123+
124+
REQUIRE(bm0.count() == 2);
118125

119126
auto bm = ~bm0;
120127

121128
REQUIRE(bm.at(0) == false);
122-
REQUIRE(bm.at(4) == false);
129+
REQUIRE(bm.at(7) == false);
123130
REQUIRE(bm.at(5) == true);
131+
REQUIRE(bm.count() == 8);
132+
REQUIRE(bm.size() == bm0.size());
124133
}
125134

126135
TEST_CASE("Bitmap equal", "[util]") {

0 commit comments

Comments
 (0)