/Users/deen/code/yugabyte-db/src/yb/util/bitmap-test.cc
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include <bitset> |
34 | | #include <random> |
35 | | #include <vector> |
36 | | |
37 | | #include <gtest/gtest.h> |
38 | | |
39 | | #include "yb/gutil/strings/join.h" |
40 | | |
41 | | #include "yb/util/bitmap.h" |
42 | | #include "yb/util/random_util.h" |
43 | | #include "yb/util/result.h" |
44 | | #include "yb/util/slice.h" |
45 | | #include "yb/util/test_macros.h" |
46 | | |
47 | | namespace yb { |
48 | | |
49 | | static int ReadBackBitmap(uint8_t *bm, size_t bits, |
50 | 2 | std::vector<size_t> *result) { |
51 | 2 | int iters = 0; |
52 | 2 | for (TrueBitIterator iter(bm, bits); |
53 | 9 | !iter.done(); |
54 | 7 | ++iter) { |
55 | 7 | size_t val = *iter; |
56 | 7 | result->push_back(val); |
57 | | |
58 | 7 | iters++; |
59 | 7 | } |
60 | 2 | return iters; |
61 | 2 | } |
62 | | |
63 | 1 | TEST(TestBitMap, TestIteration) { |
64 | 1 | uint8_t bm[8]; |
65 | 1 | memset(bm, 0, sizeof(bm)); |
66 | 1 | BitmapSet(bm, 0); |
67 | 1 | BitmapSet(bm, 8); |
68 | 1 | BitmapSet(bm, 31); |
69 | 1 | BitmapSet(bm, 32); |
70 | 1 | BitmapSet(bm, 33); |
71 | 1 | BitmapSet(bm, 63); |
72 | | |
73 | 1 | EXPECT_EQ(" 0: 10000000 10000000 00000000 00000001 11000000 00000000 00000000 00000001 \n", |
74 | 1 | BitmapToString(bm, sizeof(bm) * 8)); |
75 | | |
76 | 1 | std::vector<size_t> read_back; |
77 | | |
78 | 1 | int iters = ReadBackBitmap(bm, sizeof(bm)*8, &read_back); |
79 | 1 | ASSERT_EQ(6, iters); |
80 | 1 | ASSERT_EQ("0,8,31,32,33,63", JoinElements(read_back, ",")); |
81 | 1 | } |
82 | | |
83 | | |
84 | 1 | TEST(TestBitMap, TestIteration2) { |
85 | 1 | uint8_t bm[1]; |
86 | 1 | memset(bm, 0, sizeof(bm)); |
87 | 1 | BitmapSet(bm, 1); |
88 | | |
89 | 1 | std::vector<size_t> read_back; |
90 | | |
91 | 1 | int iters = ReadBackBitmap(bm, 3, &read_back); |
92 | 1 | ASSERT_EQ(1, iters); |
93 | 1 | ASSERT_EQ("1", JoinElements(read_back, ",")); |
94 | 1 | } |
95 | | |
96 | 1 | TEST(TestBitmap, TestSetAndTestBits) { |
97 | 1 | uint8_t bm[1]; |
98 | 1 | memset(bm, 0, sizeof(bm)); |
99 | | |
100 | 1 | size_t num_bits = sizeof(bm) * 8; |
101 | 9 | for (size_t i = 0; i < num_bits; i++) { |
102 | 8 | ASSERT_FALSE(BitmapTest(bm, i)); |
103 | | |
104 | 8 | BitmapSet(bm, i); |
105 | 8 | ASSERT_TRUE(BitmapTest(bm, i)); |
106 | | |
107 | 8 | BitmapClear(bm, i); |
108 | 8 | ASSERT_FALSE(BitmapTest(bm, i)); |
109 | | |
110 | 8 | BitmapChange(bm, i, true); |
111 | 8 | ASSERT_TRUE(BitmapTest(bm, i)); |
112 | | |
113 | 8 | BitmapChange(bm, i, false); |
114 | 8 | ASSERT_FALSE(BitmapTest(bm, i)); |
115 | 8 | } |
116 | | |
117 | | // Set the other bit: 01010101 |
118 | 9 | for (size_t i = 0; i < num_bits; ++i) { |
119 | 8 | ASSERT_FALSE(BitmapTest(bm, i)); |
120 | 8 | if (i & 1) BitmapSet(bm, i); |
121 | 8 | } |
122 | | |
123 | | // Check and Clear the other bit: 0000000 |
124 | 9 | for (size_t i = 0; i < num_bits; ++i) { |
125 | 8 | ASSERT_EQ(!!(i & 1), BitmapTest(bm, i)); |
126 | 8 | if (i & 1) BitmapClear(bm, i); |
127 | 8 | } |
128 | | |
129 | | // Check if bits are zero and change the other to one |
130 | 9 | for (size_t i = 0; i < num_bits; ++i) { |
131 | 8 | ASSERT_FALSE(BitmapTest(bm, i)); |
132 | 8 | BitmapChange(bm, i, i & 1); |
133 | 8 | } |
134 | | |
135 | | // Check the bits change them again |
136 | 9 | for (size_t i = 0; i < num_bits; ++i) { |
137 | 8 | ASSERT_EQ(!!(i & 1), BitmapTest(bm, i)); |
138 | 8 | BitmapChange(bm, i, !(i & 1)); |
139 | 8 | } |
140 | | |
141 | | // Check the last setup |
142 | 9 | for (size_t i = 0; i < num_bits; ++i) { |
143 | 8 | ASSERT_EQ(!(i & 1), BitmapTest(bm, i)); |
144 | 8 | } |
145 | 1 | } |
146 | | |
147 | 1 | TEST(TestBitMap, TestBulkSetAndTestBits) { |
148 | 1 | uint8_t bm[16]; |
149 | 1 | size_t total_size = sizeof(bm) * 8; |
150 | | |
151 | | // Test Bulk change bits and test bits |
152 | 5 | for (int i = 0; i < 4; ++i) { |
153 | 4 | bool value = i & 1; |
154 | 4 | size_t num_bits = total_size; |
155 | 516 | while (num_bits > 0) { |
156 | 33.5k | for (size_t offset = 0; offset < num_bits; ++offset) { |
157 | 33.0k | BitmapChangeBits(bm, 0, total_size, !value); |
158 | 33.0k | BitmapChangeBits(bm, offset, num_bits - offset, value); |
159 | | |
160 | 33.0k | ASSERT_EQ(value, BitMapIsAllSet(bm, offset, num_bits)); |
161 | 33.0k | ASSERT_EQ(!value, BitmapIsAllZero(bm, offset, num_bits)); |
162 | | |
163 | 33.0k | if (offset > 1) { |
164 | 32.0k | ASSERT_EQ(value, BitmapIsAllZero(bm, 0, offset - 1)); |
165 | 32.0k | ASSERT_EQ(!value, BitMapIsAllSet(bm, 0, offset - 1)); |
166 | 32.0k | } |
167 | | |
168 | 33.0k | if ((offset + num_bits) < total_size) { |
169 | 16.3k | ASSERT_EQ(value, BitmapIsAllZero(bm, num_bits, total_size)); |
170 | 16.3k | ASSERT_EQ(!value, BitMapIsAllSet(bm, num_bits, total_size)); |
171 | 16.3k | } |
172 | 33.0k | } |
173 | 512 | num_bits--; |
174 | 512 | } |
175 | 4 | } |
176 | 1 | } |
177 | | |
178 | 1 | TEST(TestBitMap, TestFindBit) { |
179 | 1 | uint8_t bm[16]; |
180 | | |
181 | 1 | size_t num_bits = sizeof(bm) * 8; |
182 | 1 | BitmapChangeBits(bm, 0, num_bits, false); |
183 | 129 | while (num_bits > 0) { |
184 | 8.38k | for (size_t offset = 0; offset < num_bits; ++offset) { |
185 | 8.25k | size_t idx; |
186 | 8.25k | ASSERT_FALSE(BitmapFindFirstSet(bm, offset, num_bits, &idx)); |
187 | 8.25k | ASSERT_TRUE(BitmapFindFirstZero(bm, offset, num_bits, &idx)); |
188 | 8.25k | ASSERT_EQ(idx, offset); |
189 | 8.25k | } |
190 | 128 | num_bits--; |
191 | 128 | } |
192 | | |
193 | 1 | num_bits = sizeof(bm) * 8; |
194 | 129 | for (size_t i = 0; i < num_bits; ++i) { |
195 | 128 | BitmapChange(bm, i, i & 3); |
196 | 128 | } |
197 | | |
198 | 129 | while (num_bits--) { |
199 | 8.25k | for (size_t offset = 0; offset < num_bits; ++offset) { |
200 | 8.12k | size_t idx; |
201 | | |
202 | | // Find a set bit |
203 | 8.12k | bool res = BitmapFindFirstSet(bm, offset, num_bits, &idx); |
204 | 8.12k | size_t expected_set_idx = (offset + !(offset & 3)); |
205 | 8.12k | bool expect_set_found = (expected_set_idx < num_bits); |
206 | 8.12k | ASSERT_EQ(expect_set_found, res); |
207 | 8.12k | if (expect_set_found) { |
208 | 8.09k | ASSERT_EQ(expected_set_idx, idx); |
209 | 8.09k | } |
210 | | |
211 | | // Find a zero bit |
212 | 8.12k | res = BitmapFindFirstZero(bm, offset, num_bits, &idx); |
213 | 6.04k | size_t expected_zero_idx = offset + ((offset & 3) ? (4 - (offset & 3)) : 0); |
214 | 8.12k | bool expect_zero_found = (expected_zero_idx < num_bits); |
215 | 8.12k | ASSERT_EQ(expect_zero_found, res); |
216 | 8.12k | if (expect_zero_found) { |
217 | 7.93k | ASSERT_EQ(expected_zero_idx, idx); |
218 | 7.93k | } |
219 | 8.12k | } |
220 | 128 | } |
221 | 1 | } |
222 | | |
223 | 1 | TEST(TestBitMap, TestBitmapIteration) { |
224 | 1 | uint8_t bm[8]; |
225 | 1 | memset(bm, 0, sizeof(bm)); |
226 | 1 | BitmapSet(bm, 0); |
227 | 1 | BitmapSet(bm, 8); |
228 | 1 | BitmapSet(bm, 31); |
229 | 1 | BitmapSet(bm, 32); |
230 | 1 | BitmapSet(bm, 33); |
231 | 1 | BitmapSet(bm, 63); |
232 | | |
233 | 1 | BitmapIterator biter(bm, sizeof(bm) * 8); |
234 | | |
235 | 1 | size_t i = 0; |
236 | 1 | size_t size; |
237 | 1 | bool value = false; |
238 | 1 | bool expected_value = true; |
239 | 1 | size_t expected_sizes[] = {1, 7, 1, 22, 3, 29, 1, 0}; |
240 | 8 | while ((size = biter.Next(&value)) > 0) { |
241 | 7 | ASSERT_LT(i, 8); |
242 | 7 | ASSERT_EQ(expected_value, value); |
243 | 7 | ASSERT_EQ(expected_sizes[i], size); |
244 | 7 | expected_value = !expected_value; |
245 | 7 | i++; |
246 | 7 | } |
247 | 1 | ASSERT_EQ(expected_sizes[i], size); |
248 | 1 | } |
249 | | |
250 | 1 | TEST(TestBitMap, OneWayBitmapSimple) { |
251 | 1 | OneWayBitmap bitmap; |
252 | 1 | ASSERT_FALSE(bitmap.Test(0)); |
253 | | |
254 | 1 | bitmap.Set(0); |
255 | 1 | ASSERT_TRUE(bitmap.Test(0)); |
256 | 1 | ASSERT_FALSE(bitmap.Test(1)); |
257 | 1 | ASSERT_EQ(bitmap.CountSet(), 1); |
258 | 1 | ASSERT_EQ(bitmap.EncodeToHexString(), "020001"); |
259 | 1 | ASSERT_EQ(bitmap.ToString(), "[0]"); |
260 | | |
261 | 1 | bitmap.Set(2); |
262 | 1 | ASSERT_TRUE(bitmap.Test(0)); |
263 | 1 | ASSERT_TRUE(bitmap.Test(2)); |
264 | 1 | ASSERT_FALSE(bitmap.Test(1)); |
265 | 1 | ASSERT_EQ(bitmap.CountSet(), 2); |
266 | 1 | ASSERT_EQ(bitmap.EncodeToHexString(), "020005"); |
267 | 1 | ASSERT_EQ(bitmap.ToString(), "[0, 2]"); |
268 | 1 | } |
269 | | |
270 | | template<size_t N> |
271 | 6.92k | void CheckSame(const OneWayBitmap& bitmap, const std::bitset<N>& bitset) { |
272 | 6.92k | EXPECT_EQ(bitmap.CountSet(), bitset.count()); |
273 | 7.09M | for (size_t bit = 0; bit != N; ++bit) { |
274 | 14.1M | EXPECT_EQ(bitmap.Test(bit), bitset.test(bit)) << "Bit: " << bit; |
275 | 7.08M | } |
276 | 449k | for (size_t bit = N; bit != N + sizeof(size_t) * 8; ++bit) { |
277 | 443k | EXPECT_FALSE(bitmap.Test(N)); |
278 | 443k | } |
279 | 6.92k | } |
280 | | |
281 | | template<size_t N> |
282 | 3.46k | void CheckBitmap(const OneWayBitmap& bitmap, const std::bitset<N>& bitset) { |
283 | 3.46k | ASSERT_NO_FATALS(CheckSame(bitmap, bitset)); |
284 | 3.46k | boost::container::small_vector<uint8_t, 8> buffer; |
285 | 3.46k | bitmap.EncodeTo(&buffer); |
286 | 3.46k | Slice slice(buffer.data(), buffer.size()); |
287 | | |
288 | 3.46k | auto decoded_bitmap = ASSERT_RESULT_FAST(OneWayBitmap::Decode(&slice)); |
289 | 3.46k | ASSERT_TRUE(slice.empty()); |
290 | 3.46k | ASSERT_NO_FATALS(CheckSame(decoded_bitmap, bitset)); |
291 | | |
292 | 3.46k | slice = Slice(buffer.data(), buffer.size()); |
293 | 3.46k | ASSERT_OK_FAST(OneWayBitmap::Skip(&slice)); |
294 | 3.46k | ASSERT_TRUE(slice.empty()); |
295 | 3.46k | } |
296 | | |
297 | | template<size_t N> |
298 | 2 | void CheckBitSequence(const std::vector<size_t>& bits_to_set) { |
299 | 2 | std::bitset<N> bitset; |
300 | 2 | OneWayBitmap bitmap; |
301 | | |
302 | 2 | ASSERT_NO_FATALS(CheckBitmap(bitmap, bitset)); |
303 | | |
304 | 3.45k | for (auto i : bits_to_set) { |
305 | 3.45k | bitmap.Set(i); |
306 | 3.45k | bitset.set(i, true); |
307 | | |
308 | 3.45k | ASSERT_NO_FATALS(CheckBitmap(bitmap, bitset)); |
309 | 3.45k | } |
310 | 2 | } |
311 | | |
312 | | // Test OneWayBitmap when bits are set randomly. |
313 | 1 | TEST(TestBitMap, OneWayBitmapRandom) { |
314 | 1 | std::mt19937_64 rng(123456); |
315 | | |
316 | 1 | constexpr size_t kTotalBits = 1024; |
317 | 1 | std::vector<size_t> bits_to_set; |
318 | 1 | bits_to_set.reserve(kTotalBits * 2); |
319 | 1.02k | for (size_t i = 0; i != kTotalBits; ++i) { |
320 | 1.02k | bits_to_set.push_back(i); |
321 | 1.02k | if (RandomUniformBool(&rng)) { |
322 | 483 | bits_to_set.push_back(i); |
323 | 483 | } |
324 | 1.02k | } |
325 | | |
326 | 1 | std::shuffle(bits_to_set.begin(), bits_to_set.end(), rng); |
327 | | |
328 | 1 | ASSERT_NO_FATALS(CheckBitSequence<kTotalBits>(bits_to_set)); |
329 | 1 | } |
330 | | |
331 | | // Test OneWayBitmap when bits are set in nearly increasing order. |
332 | 1 | TEST(TestBitMap, OneWayBitmapMetaIncreasing) { |
333 | 1 | std::mt19937_64 rng(123456); |
334 | | |
335 | 1 | constexpr size_t kTotalBits = 1024; |
336 | 1 | std::vector<size_t> bits_to_set; |
337 | 1 | bits_to_set.reserve(kTotalBits * 2); |
338 | 1.02k | for (size_t i = 0; i != kTotalBits; ++i) { |
339 | 1.02k | size_t extra_index = i + RandomUniformInt(10, 20, &rng) - 10; |
340 | 1.02k | if (extra_index > i && extra_index < kTotalBits) { |
341 | 928 | bits_to_set.push_back(extra_index); |
342 | 928 | } |
343 | 1.02k | bits_to_set.push_back(i); |
344 | 1.02k | } |
345 | | |
346 | 1 | ASSERT_NO_FATALS(CheckBitSequence<kTotalBits>(bits_to_set)); |
347 | 1 | } |
348 | | |
349 | | } // namespace yb |