• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 27083073843

06 Jun 2026 10:39PM UTC coverage: 89.361% (-0.003%) from 89.364%
27083073843

push

github

randombit
Use a mirror for Intel SDE [ci skip]

Downloads fine with a browser but has recently started failing with wget or a script.
Seems like Intel has started playing some silly game with JavaScript. The license
allows anyone to copy and redistribute SDE so I'm not sure why they are doing this.

110631 of 123803 relevant lines covered (89.36%)

11056986.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.79
/src/tests/test_utils_bitvector.cpp
1
/*
2
 * (C) 2023-2024 Jack Lloyd
3
 * (C) 2023-2024 René Meusel, Rohde & Schwarz Cybersecurity
4
 *
5
 * Botan is released under the Simplified BSD License (see license.txt)
6
 */
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_BITVECTOR)
11
   #include <botan/hex.h>
12
   #include <botan/rng.h>
13
   #include <botan/internal/bitvector.h>
14
   #include <botan/internal/fmt.h>
15
   #include <algorithm>
16
   #include <numeric>
17
   #include <set>
18
#endif
19

20
namespace Botan_Tests {
21

22
namespace {
23

24
#if defined(BOTAN_HAS_BITVECTOR)
25

26
/// Returns a random number in the range [min, max)
27
size_t rand_in_range(Botan::RandomNumberGenerator& rng, size_t min, size_t max) {
165✔
28
   if(min == max) {
165✔
29
      return min;
30
   }
31

32
   const size_t val = Botan::load_le<size_t>(rng.random_array<sizeof(size_t)>());
165✔
33
   return min + (val % (max - min));
165✔
34
}
35

36
/// Returns @p n integers smaller than @p upper_bound in random order
37
std::vector<size_t> rand_indices(Botan::RandomNumberGenerator& rng, size_t n, size_t upper_bound) {
3✔
38
   auto shuffle = [&](std::vector<size_t>& v) {
6✔
39
      // Fisher-Yates shuffle
40
      if(v.size() < 2) {
3✔
41
         return;
42
      }
43
      for(size_t i = 0; i < v.size() - 1; ++i) {
162✔
44
         auto j = rand_in_range(rng, i, v.size());
159✔
45
         std::swap(v[i], v[j]);
159✔
46
      }
47
   };
3✔
48

49
   std::vector<size_t> indices(upper_bound);
3✔
50
   std::iota(indices.begin(), indices.end(), 0);
3✔
51
   shuffle(indices);
3✔
52
   indices.resize(n);
3✔
53
   return indices;
3✔
54
}
×
55

56
/// Create an empty bitvector of random size and chose a random number of points of interests
57
std::pair<Botan::bitvector, std::set<size_t>> rnd_bitvector_with_rnd_pois(Botan::RandomNumberGenerator& rng) {
3✔
58
   const Botan::bitvector bv(rand_in_range(rng, 0, 65));
3✔
59
   const size_t no_poi = rand_in_range(rng, 0, bv.size());
3✔
60
   auto points_of_interest = rand_indices(rng, no_poi, bv.size());
3✔
61

62
   return {bv, {points_of_interest.begin(), points_of_interest.end()}};
6✔
63
}
6✔
64

65
template <size_t mod>
66
auto pattern_generator(size_t offset = 0) {
79✔
67
   return [i = offset]() mutable -> bool {
5,209✔
68
      const bool result = (i % mod) != 0;
5,209✔
69
      ++i;
5,209✔
70
      return result;
71
   };
72
}
73

74
std::vector<Test::Result> test_bitvector_bitwise_accessors(Botan::RandomNumberGenerator& rng) {
1✔
75
   return {
1✔
76
      CHECK("default constructed bitvector",
77
            [](auto& result) {
1✔
78
               const Botan::bitvector bv;
1✔
79
               result.test_is_true("default constructed bitvector is empty", bv.empty());
1✔
80
               result.test_sz_eq("default constructed bitvector has zero size", bv.size(), size_t(0));
1✔
81
            }),
1✔
82

83
      CHECK("preallocated construction of bitvector",
84
            [](auto& result) {
1✔
85
               Botan::bitvector bv(10);
1✔
86
               result.test_is_true("allocated bitvector is not empty", !bv.empty());
1✔
87
               result.test_sz_eq("allocated bitvector has allocated size", bv.size(), size_t(10));
1✔
88
               for(size_t i = 0; i < 10; ++i) {
11✔
89
                  result.test_is_true("bit not set yet", !bv.at(i));
10✔
90
               }
91
            }),
1✔
92

93
      CHECK("setting bits",
94
            [&](auto& result) {
1✔
95
               auto [bv, ones] = rnd_bitvector_with_rnd_pois(rng);
1✔
96

97
               for(const size_t i : ones) {
2✔
98
                  if(rng.next_byte() % 2 == 0) {
1✔
99
                     bv.set(i);
×
100
                  } else {
101
                     bv.at(i) = true;
1✔
102
                  }
103
               }
104
               for(size_t i = 0; i < bv.size(); ++i) {
49✔
105
                  result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i));
144✔
106
               }
107
            }),
1✔
108

109
      CHECK("unsetting bits",
110
            [&](auto& result) {
1✔
111
               auto [bv, zeros] = rnd_bitvector_with_rnd_pois(rng);
1✔
112
               for(auto b : bv) {
62✔
113
                  b.set();
60✔
114
               }
115

116
               for(const size_t i : zeros) {
45✔
117
                  if(rng.next_byte() % 2 == 0) {
44✔
118
                     bv.unset(i);
23✔
119
                  } else {
120
                     bv.at(i) = false;
21✔
121
                  }
122
               }
123
               for(size_t i = 0; i < bv.size(); ++i) {
61✔
124
                  result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == !zeros.contains(i));
180✔
125
               }
126
            }),
1✔
127

128
      CHECK("flipping bits",
129
            [&](auto& result) {
1✔
130
               auto [bv, ones] = rnd_bitvector_with_rnd_pois(rng);
1✔
131

132
               for(size_t i = 0; i < bv.size(); ++i) {
55✔
133
                  if(std::find(ones.begin(), ones.end(), i) == ones.end()) {
108✔
134
                     bv.set(i);
22✔
135
                  }
136
                  bv.flip(i);
54✔
137
               }
138
               for(size_t i = 0; i < bv.size(); ++i) {
55✔
139
                  result.test_is_true(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i));
162✔
140
               }
141
            }),
1✔
142

143
      CHECK("accessors validate offsets",
144
            [](auto& result) {
1✔
145
               Botan::bitvector bv(10);
1✔
146
               result.template test_throws<Botan::Invalid_Argument>(
1✔
147
                  ".at() const out of range", [&] { const_cast<const decltype(bv)&>(bv).at(10); });
2✔
148
               result.template test_throws<Botan::Invalid_Argument>(".at() out of range", [&] { bv.at(10); });
2✔
149
               result.template test_throws<Botan::Invalid_Argument>(".set() out of range", [&] { bv.set(10); });
2✔
150
               result.template test_throws<Botan::Invalid_Argument>(".unset() out of range", [&] { bv.unset(10); });
2✔
151
               result.template test_throws<Botan::Invalid_Argument>(".flip() out of range", [&] { bv.flip(10); });
2✔
152
            }),
1✔
153

154
      CHECK("multiblock handling",
155
            [](auto& result) {
1✔
156
               Botan::bitvector bv(128);
1✔
157
               result.test_sz_eq("has more than 64 bits", bv.size(), 128);
1✔
158
               bv.set(1).set(63).set(64).set(127);
1✔
159
               for(size_t i = 0; i < bv.size(); ++i) {
129✔
160
                  const bool expected = (i == 1 || i == 63 || i == 64 || i == 127);
128✔
161
                  result.test_bool_eq(Botan::fmt("bit {} in expected state", i), bv.at(i), expected);
256✔
162
               }
163
            }),
1✔
164

165
      CHECK("subscript operator",
166
            [](auto& result) {
1✔
167
               Botan::bitvector bv(128);
1✔
168
               bv[0].set();
1✔
169
               bv[1] = true;
1✔
170
               bv[2].flip();
1✔
171
               bv[64] = true;
1✔
172
               bv[80] = true;
1✔
173
               result.test_is_true("bit 0", bv[0]);
1✔
174
               result.test_is_true("bit 1", bv[1]);
1✔
175
               result.test_is_true("bit 2", bv[2]);
1✔
176
               result.test_is_true("bit 3", !bv[3]);
1✔
177
               result.test_is_true("bit 64", bv[64]);
1✔
178
               result.test_is_true("bit 80", bv[80]);
1✔
179
            }),
1✔
180

181
      CHECK("subscript operator does not validate offsets",
182
            [](auto& result) {
1✔
183
               Botan::bitvector bv(10);
1✔
184
               result.template test_throws<Botan::Invalid_Argument>(".at() out of range", [&] { bv.at(10); });
2✔
185
               // Technically the next line is undefined behaviour.
186
               // Though, the current implementation detail won't
187
               // cause issues, which might change!
188
               result.test_no_throw("subscript out of range", [&] { bv[10]; });
1✔
189
            }),
1✔
190

191
      CHECK("bitwise assignment modifiers",
192
            [](auto& result) {
1✔
193
               Botan::bitvector bv(4);
1✔
194

195
               result.require("precondition", !bv[0] && !bv[1]);
1✔
196
               bv[0] &= 1;  // NOLINT(*-use-bool-literals)
1✔
197
               result.test_is_true("bv[0] still 0", !bv[0]);
1✔
198
               bv[0].set();
1✔
199
               bv[0] &= 1;  // NOLINT(*-use-bool-literals)
1✔
200
               result.test_is_true("bv[0] still 1", bv[0]);
1✔
201
               bv[0] &= false;
1✔
202
               result.test_is_true("bv[0] now 0 again", !bv[0]);
1✔
203
               bv[0] &= !bv[1];
1✔
204
               result.test_is_true("bv[0] still 0 once more", !bv[0]);
1✔
205

206
               result.require("precondition 2", !bv[1] && !bv[2]);
1✔
207
               bv[1] |= 1;  // NOLINT(modernize-use-bool-literals)
1✔
208
               result.test_is_true("bv[1] is now 1", bv[1]);
1✔
209
               bv[1] |= 0;  // NOLINT(modernize-use-bool-literals)
1✔
210
               result.test_is_true("bv[1] is still 1", bv[1]);
1✔
211
               bv[1].unset();
1✔
212
               bv[1] |= false;
1✔
213
               result.test_is_true("bv[1] is 0", !bv[1]);
1✔
214
               bv[1] |= !bv[2];
1✔
215
               result.test_is_true("bv[1] is 1 again", bv[1]);
1✔
216

217
               result.require("precondition 3", !bv[2] && !bv[3]);
1✔
218
               bv[2] ^= 0;  // NOLINT(modernize-use-bool-literals)
1✔
219
               result.test_is_true("bv[2] is still 0", !bv[2]);
1✔
220
               bv[2] ^= true;
1✔
221
               result.test_is_true("bv[2] is now 1", bv[2]);
1✔
222
               bv[2] ^= !bv[3];
1✔
223
               result.test_is_true("bv[2] is 0 again", !bv[2]);
1✔
224
            }),
1✔
225
   };
11✔
226
}
1✔
227

228
std::vector<Test::Result> test_bitvector_capacity(Botan::RandomNumberGenerator& /*rng*/) {
1✔
229
   return {
1✔
230
      CHECK("default constructed bitvector",
231
            [](auto& result) {
1✔
232
               const Botan::bitvector bv;
1✔
233
               result.test_is_true("empty", bv.empty());
1✔
234
               result.test_sz_eq("no size", bv.size(), size_t(0));
1✔
235
               result.test_sz_eq("no capacity", bv.capacity(), size_t(0));
1✔
236
            }),
1✔
237

238
      CHECK("allocated bitvector has capacity",
239
            [](auto& result) {
1✔
240
               const Botan::bitvector bv(1);
1✔
241
               result.test_is_true("empty", !bv.empty());
1✔
242
               result.test_sz_eq("small size", bv.size(), size_t(1));
1✔
243
               result.test_sz_gte("a little capacity", bv.capacity(), size_t(8));
1✔
244
            }),
1✔
245

246
      CHECK("reserved bitvector has capacity",
247
            [](auto& result) {
1✔
248
               Botan::bitvector bv;
1✔
249
               result.test_sz_eq("no size", bv.size(), size_t(0));
1✔
250
               result.test_sz_eq("no capacity", bv.capacity(), size_t(0));
1✔
251

252
               bv.reserve(64);
1✔
253
               result.test_sz_eq("no size", bv.size(), size_t(0));
1✔
254
               result.test_sz_gte("no capacity", bv.capacity(), size_t(64));
1✔
255

256
               bv.reserve(128);
1✔
257
               result.test_sz_eq("no size", bv.size(), size_t(0));
1✔
258
               result.test_sz_gte("no capacity", bv.capacity(), size_t(128));
1✔
259
            }),
1✔
260

261
      CHECK("push_back() extends bitvector",
262
            [](Test::Result& result) {
1✔
263
               Botan::bitvector bv;
1✔
264
               result.test_is_true("empty", bv.empty());
1✔
265
               result.test_sz_eq("no size", bv.size(), size_t(0));
1✔
266

267
               bv.push_back(true);
1✔
268
               bv.push_back(false);
1✔
269
               bv.push_back(true);
1✔
270
               bv.push_back(false);
1✔
271

272
               result.test_is_true("not empty", !bv.empty());
1✔
273
               result.test_sz_eq("some size", bv.size(), size_t(4));
1✔
274
               result.test_sz_gte("capacity is typically bigger than size", bv.capacity(), size_t(8));
1✔
275

276
               result.test_is_true("bit 0", bv.at(0));
1✔
277
               result.test_is_true("bit 1", !bv.at(1));
1✔
278
               result.test_is_true("bit 2", bv.at(2));
1✔
279
               result.test_is_true("bit 3", !bv.at(3));
1✔
280

281
               result.test_throws("bit 4 is not yet allocated", [&] { bv.at(4); });
2✔
282
            }),
1✔
283

284
      CHECK("pop_back() shortens bitvector",
285
            [](Test::Result& result) {
1✔
286
               Botan::bitvector bv;
1✔
287
               bv.push_back(true);
1✔
288
               bv.push_back(false);
1✔
289
               bv.push_back(true);
1✔
290
               bv.push_back(false);
1✔
291
               result.test_is_true("last is false", !bv.back());
1✔
292

293
               bv.pop_back();
1✔
294
               result.test_sz_eq("size() == 3", bv.size(), 3);
1✔
295
               result.test_is_true("last is true", bv.back());
1✔
296

297
               bv.pop_back();
1✔
298
               result.test_sz_eq("size() == 2", bv.size(), 2);
1✔
299
               result.test_is_true("last is false", !bv.back());
1✔
300

301
               bv.pop_back();
1✔
302
               result.test_sz_eq("size() == 1", bv.size(), 1);
1✔
303
               result.test_is_true("last is true", bv.back());
1✔
304
               result.test_is_true("first is true", bv.front());
1✔
305

306
               bv.pop_back();
1✔
307
               result.test_is_true("empty", bv.empty());
1✔
308

309
               result.test_throws("bit 4 is not yet allocated", [&] { bv.at(4); });
2✔
310
            }),
1✔
311

312
      CHECK("resize()",
313
            [](auto& result) {
1✔
314
               Botan::bitvector bv(10);
1✔
315
               bv[0] = true;
1✔
316
               bv[5] = true;
1✔
317
               bv[9] = true;
1✔
318

319
               bv.resize(8);
1✔
320
               result.test_sz_eq("size is reduced", bv.size(), size_t(8));
1✔
321

322
               for(size_t i = 0; i < bv.size(); ++i) {
9✔
323
                  const bool expected = (i == 0 || i == 5);
8✔
324
                  result.test_bool_eq(Botan::fmt("{} is as expected", i), bv[i], expected);
16✔
325
               }
326

327
               bv.resize(0);
1✔
328
               result.test_is_true("resize(0) empties buffer", bv.empty());
1✔
329

330
               bv.resize(8);
1✔
331
               result.test_is_true("0 is false", !bv[0]);
1✔
332
               result.test_is_true("5 is false", !bv[5]);
1✔
333
            }),
1✔
334

335
      CHECK("binary bitwise and comparison operators",
336
            [](Test::Result& result) {
1✔
337
               Botan::bitvector a(8);
1✔
338
               a.set(0).set(3);
1✔
339
               Botan::bitvector b(8);
1✔
340
               b.set(1).set(3);
1✔
341
               Botan::bitvector c(8);
1✔
342
               c.set(0).set(3);  // same is a
1✔
343

344
               result.test_is_true("equal bitvectors compare equal", a == c);
2✔
345
               result.test_is_true("different bitvectors do not compare equal", !(a == b));
2✔
346
               result.test_is_true("different bitvectors compare not equal", a != b);
2✔
347
               result.test_is_true("equal bitvectors do not compare not equal", !(a != c));
2✔
348

349
               auto or_ab = a | b;
1✔
350
               result.test_is_true("OR sets union bit 0", or_ab.at(0).is_set());
1✔
351
               result.test_is_true("OR sets union bit 1", or_ab.at(1).is_set());
1✔
352
               result.test_is_true("OR sets union bit 3", or_ab.at(3).is_set());
1✔
353
               result.test_is_true("OR leaves unset bit 2", !or_ab.at(2).is_set());
1✔
354

355
               auto and_ab = a & b;
1✔
356
               result.test_is_true("AND keeps common bit 3", and_ab.at(3).is_set());
1✔
357
               result.test_is_true("AND clears non-common bit 0", !and_ab.at(0).is_set());
1✔
358
               result.test_is_true("AND clears non-common bit 1", !and_ab.at(1).is_set());
1✔
359

360
               auto xor_ab = a ^ b;
1✔
361
               result.test_is_true("XOR sets differing bit 0", xor_ab.at(0).is_set());
1✔
362
               result.test_is_true("XOR sets differing bit 1", xor_ab.at(1).is_set());
1✔
363
               result.test_is_true("XOR clears common bit 3", !xor_ab.at(3).is_set());
1✔
364
            }),
6✔
365

366
      CHECK("comparison and reduction of empty bitvectors",
367
            [](Test::Result& result) {
1✔
368
               const Botan::bitvector empty1;
1✔
369
               const Botan::bitvector empty2;
1✔
370
               result.test_is_true("empty is empty", empty1.empty());
1✔
371
               result.test_sz_eq("empty has zero size", empty1.size(), size_t(0));
1✔
372

373
               // Full-range reductions on an empty bitvector must not throw or
374
               // terminate (the underlying buffer may be null).
375
               result.test_is_true("empty none()", empty1.none());
1✔
376
               result.test_is_true("empty none_vartime()", empty1.none_vartime());
1✔
377
               result.test_is_true("empty !any()", !empty1.any());
1✔
378
               result.test_is_true("empty !any_vartime()", !empty1.any_vartime());
1✔
379
               result.test_is_true("empty all()", empty1.all());
1✔
380
               result.test_is_true("empty all_vartime()", empty1.all_vartime());
1✔
381
               result.test_sz_eq("empty hamming_weight()", empty1.hamming_weight(), size_t(0));
1✔
382

383
               // Two empty bitvectors compare equal. equals() is constant-time
384
               // and noexcept, so a throw here would call std::terminate().
385
               result.test_is_true("empty equals empty", empty1.equals(empty2));
1✔
386
               result.test_is_true("empty equals_vartime empty", empty1.equals_vartime(empty2));
2✔
387
               result.test_is_true("empty == empty", empty1 == empty2);
2✔
388
               result.test_is_true("empty not != empty", !(empty1 != empty2));
2✔
389

390
               // Mismatched sizes must compare unequal without touching blocks.
391
               const Botan::bitvector nonempty(8);
1✔
392
               result.test_is_true("empty does not equal non-empty", !empty1.equals(nonempty));
1✔
393
               result.test_is_true("empty does not equal_vartime non-empty", !empty1.equals_vartime(nonempty));
2✔
394
               result.test_is_true("empty != non-empty", empty1 != nonempty);
2✔
395

396
               // Same coverage for the secure allocator, whose empty buffer is
397
               // likewise potentially null.
398
               const Botan::secure_bitvector sempty1;
1✔
399
               const Botan::secure_bitvector sempty2;
1✔
400
               result.test_is_true("empty secure none()", sempty1.none());
1✔
401
               result.test_is_true("empty secure equals empty", sempty1.equals(sempty2));
1✔
402
               result.test_is_true("empty secure == empty", sempty1 == sempty2);
2✔
403
            }),
2✔
404
   };
9✔
405
}
1✔
406

407
std::vector<Test::Result> test_bitvector_subvector(Botan::RandomNumberGenerator& /*rng*/) {
1✔
408
   auto make_bitpattern = [&]<typename T>(T& bitvector, size_t pattern_offset = 0) {
21✔
409
      auto next = pattern_generator<3>(pattern_offset);
20✔
410

411
      if constexpr(std::unsigned_integral<T>) {
412
         for(size_t i = 0; i < sizeof(T) * 8; ++i) {
316✔
413
            bitvector |= static_cast<T>(next()) << i;
304✔
414
         }
415
      } else {
416
         for(auto& i : bitvector) {
1,616✔
417
            i = next();
800✔
418
         }
419
      }
420
   };
8✔
421

422
   auto bitpattern_at = [&]<std::unsigned_integral T>(T /* ignored */, size_t pattern_offset) -> T {
13✔
423
      T bitvector = 0;
12✔
424
      make_bitpattern(bitvector, pattern_offset);
24✔
425
      return bitvector;
426
   };
1✔
427

428
   auto check_bitpattern = [&](auto& result, auto& bitvector, size_t offset = 0) {
44✔
429
      using bv_t = std::remove_cvref_t<decltype(bitvector)>;
430
      auto next = pattern_generator<3>(offset);
43✔
431

432
      if constexpr(std::unsigned_integral<bv_t>) {
433
         for(size_t i = 0; i < sizeof(bv_t) * 8; ++i) {
496✔
434
            result.test_bool_eq(Botan::fmt("{} is as expected", i), (bitvector & (bv_t(1) << i)) != 0, next());
960✔
435
         }
436
      } else {
437
         for(size_t i = 0; i < bitvector.size(); ++i) {
2,056✔
438
            result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], next());
4,058✔
439
         }
440
      }
441
   };
43✔
442

443
   auto check_bitpattern_with_zero_region = [&](auto& result, auto& bitvector, std::pair<size_t, size_t> zero_region) {
13✔
444
      auto next = pattern_generator<3>();
12✔
445
      for(size_t i = 0; i < bitvector.size(); ++i) {
1,212✔
446
         const bool i_in_range = (zero_region.first <= i && i < zero_region.second);
1,200✔
447
         const bool expected = next();
1,200✔
448
         result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], !i_in_range && expected);
2,400✔
449
      }
450
   };
12✔
451

452
   return {
1✔
453
      CHECK("range errors are caught",
454
            [&](auto& result) {
1✔
455
               const Botan::bitvector bv(100);
1✔
456
               result.template test_throws<Botan::Invalid_Argument>("out of range", [&] { bv.subvector(0, 101); });
2✔
457
               result.template test_throws<Botan::Invalid_Argument>("out of range", [&] { bv.subvector(90, 11); });
2✔
458
               result.template test_throws<Botan::Invalid_Argument>("out of range", [&] { bv.subvector(100, 1); });
2✔
459
               result.template test_throws<Botan::Invalid_Argument>("out of range", [&] { bv.subvector(101, 0); });
2✔
460
            }),
1✔
461

462
      CHECK("empty copy is allowed",
463
            [&](auto& result) {
1✔
464
               const Botan::bitvector bv1(100);
1✔
465
               auto bv2 = bv1.subvector(0, 0);
1✔
466
               result.test_sz_eq("empty at 0", bv2.size(), size_t(0));
1✔
467
               auto bv3 = bv1.subvector(10, 0);
1✔
468
               result.test_sz_eq("empty at 10", bv3.size(), size_t(0));
1✔
469
               auto bv4 = bv1.subvector(100, 0);
1✔
470
               result.test_sz_eq("empty at 100", bv3.size(), size_t(0));
1✔
471
            }),
2✔
472

473
      CHECK("byte-aligned copy",
474
            [&](auto& result) {
1✔
475
               Botan::bitvector bv1(100);
1✔
476
               make_bitpattern(bv1);
1✔
477

478
               auto bv2 = bv1.subvector(16, 58);
1✔
479
               result.test_sz_eq("size is as requested", bv2.size(), size_t(58));
1✔
480
               check_bitpattern(result, bv2, 16);
1✔
481

482
               auto bv3 = bv1.subvector(32);  // copy until the end
1✔
483
               result.test_sz_eq("size is as expected", bv3.size(), size_t(68));
1✔
484
               check_bitpattern(result, bv3, 32);
1✔
485
            }),
3✔
486

487
      CHECK("byte-aligned 2",
488
            [&](auto& result) {
1✔
489
               Botan::bitvector bv1(100);
1✔
490
               make_bitpattern(bv1);
1✔
491

492
               auto bv2 = bv1.subvector(8, 91);
1✔
493
               result.test_sz_eq("size is as expected", bv2.size(), size_t(91));
1✔
494
               check_bitpattern(result, bv2, 8);
1✔
495

496
               auto bv3 = bv1.subvector(16, 58);
1✔
497
               result.test_sz_eq("size is as requested", bv3.size(), size_t(58));
1✔
498
               check_bitpattern(result, bv3, 16);
1✔
499

500
               auto bv4 = bv1.subvector(24);  // copy until the end
1✔
501
               result.test_sz_eq("size is as expected", bv4.size(), size_t(100 - 24));
1✔
502
               check_bitpattern(result, bv4, 24);
1✔
503

504
               auto bv5 = bv1.subvector(32);  // copy until the end
1✔
505
               result.test_sz_eq("size is as expected", bv5.size(), size_t(100 - 32));
1✔
506
               check_bitpattern(result, bv5, 32);
1✔
507

508
               auto bv6 = bv1.subvector(48, 51);  // copy until the end
1✔
509
               result.test_sz_eq("size is as expected", bv6.size(), size_t(51));
1✔
510
               check_bitpattern(result, bv6, 48);
1✔
511
            }),
6✔
512

513
      CHECK("byte-aligned copy must zero-out unused bits",
514
            [&](auto& result) {
1✔
515
               Botan::bitvector bv1(100);
1✔
516
               make_bitpattern(bv1);
1✔
517

518
               auto bv2 = bv1.subvector(16, 17);
1✔
519
               result.test_sz_eq("size is as requested", bv2.size(), size_t(17));
1✔
520
               check_bitpattern(result, bv2, 16);
1✔
521

522
               bv2.resize(32);
1✔
523
               for(size_t i = 17; i < bv2.size(); ++i) {
16✔
524
                  result.test_is_true("tail is zero", !bv2[i]);
15✔
525
               }
526
            }),
2✔
527

528
      CHECK("unaligned copy",
529
            [&](auto& result) {
1✔
530
               Botan::bitvector bv1(100);
1✔
531
               make_bitpattern(bv1);
1✔
532

533
               auto bv2 = bv1.subvector(19, 69);
1✔
534
               result.test_sz_eq("size is as requested", bv2.size(), size_t(69));
1✔
535
               check_bitpattern(result, bv2, 19);
1✔
536

537
               auto bv3 = bv1.subvector(21);  // copy until the end
1✔
538
               result.test_sz_eq("size is as expected", bv3.size(), size_t(79));
1✔
539
               check_bitpattern(result, bv3, 21);
1✔
540

541
               auto bv4 = bv1.subvector(1, 16);
1✔
542
               result.test_sz_eq("size is as expected", bv4.size(), size_t(16));
1✔
543
               check_bitpattern(result, bv4, 1);
1✔
544

545
               auto bv5 = bv1.subvector(1, 32);
1✔
546
               result.test_sz_eq("size is as expected", bv5.size(), size_t(32));
1✔
547
               check_bitpattern(result, bv5, 1);
1✔
548

549
               auto bv6 = bv5.subvector(1, 12);
1✔
550
               result.test_sz_eq("size is as expected", bv6.size(), size_t(12));
1✔
551
               check_bitpattern(result, bv6, 1 + 1);
1✔
552

553
               auto bv7 = bv1.subvector(17, 67);
1✔
554
               result.test_sz_eq("size is as expected", bv7.size(), size_t(67));
1✔
555
               check_bitpattern(result, bv7, 17);
1✔
556

557
               auto bv8 = bv1.subvector(33);  // copy until the end
1✔
558
               result.test_sz_eq("size is as expected", bv8.size(), size_t(67));
1✔
559
               check_bitpattern(result, bv8, 33);
1✔
560
            }),
8✔
561

562
      CHECK("byte-aligned unsigned integer subvector",
563
            [&](auto& result) {
1✔
564
               Botan::bitvector bv1(100);
1✔
565
               make_bitpattern(bv1);
1✔
566

567
               const auto u8_0 = bv1.subvector<uint8_t>(0);
1✔
568
               const auto u8_32 = bv1.subvector<uint8_t>(32);
1✔
569
               check_bitpattern(result, u8_0, 0);
1✔
570
               check_bitpattern(result, u8_32, 32);
1✔
571

572
               const auto u16_0 = bv1.subvector<uint16_t>(0);
1✔
573
               const auto u16_56 = bv1.subvector<uint16_t>(56);
1✔
574
               check_bitpattern(result, u16_0, 0);
1✔
575
               check_bitpattern(result, u16_56, 56);
1✔
576

577
               const auto u32_0 = bv1.subvector<uint32_t>(0);
1✔
578
               const auto u32_48 = bv1.subvector<uint32_t>(48);
1✔
579
               check_bitpattern(result, u32_0, 0);
1✔
580
               check_bitpattern(result, u32_48, 48);
1✔
581

582
               const auto u64_0 = bv1.subvector<uint64_t>(0);
1✔
583
               const auto u64_32 = bv1.subvector<uint64_t>(32);
1✔
584
               check_bitpattern(result, u64_0, 0);
1✔
585
               check_bitpattern(result, u64_32, 32);
1✔
586

587
               result.test_throws("out of range (uint8_t)", [&] { bv1.subvector<uint8_t>(93); });
2✔
588
               result.test_throws("out of range (uint16_t)", [&] { bv1.subvector<uint16_t>(85); });
2✔
589
               result.test_throws("out of range (uint32_t)", [&] { bv1.subvector<uint32_t>(69); });
2✔
590
               result.test_throws("out of range (uint64_t)", [&] { bv1.subvector<uint64_t>(37); });
2✔
591
            }),
1✔
592

593
      CHECK("unaligned unsigned integer subvector",
594
            [&](Test::Result& result) {
1✔
595
               Botan::bitvector bv1(100);
1✔
596
               make_bitpattern(bv1);
1✔
597

598
               const auto u8_3 = bv1.subvector<uint8_t>(3);
1✔
599
               const auto u8_92 = bv1.subvector<uint8_t>(92);
1✔
600
               check_bitpattern(result, u8_3, 3);
1✔
601
               check_bitpattern(result, u8_92, 92);
1✔
602

603
               const auto u16_7 = bv1.subvector<uint16_t>(7);
1✔
604
               const auto u16_84 = bv1.subvector<uint16_t>(84);
1✔
605
               check_bitpattern(result, u16_7, 7);
1✔
606
               check_bitpattern(result, u16_84, 84);
1✔
607

608
               const auto u32_11 = bv1.subvector<uint32_t>(11);
1✔
609
               const auto u32_68 = bv1.subvector<uint32_t>(68);
1✔
610
               check_bitpattern(result, u32_11, 11);
1✔
611
               check_bitpattern(result, u32_68, 68);
1✔
612

613
               const auto u64_21 = bv1.subvector<uint64_t>(21);
1✔
614
               const auto u64_36 = bv1.subvector<uint64_t>(36);
1✔
615
               check_bitpattern(result, u64_21, 21);
1✔
616
               check_bitpattern(result, u64_36, 36);
1✔
617
            }),
1✔
618

619
      CHECK("byte-aligned unsigned integer subvector replacement",
620
            [&](auto& result) {
1✔
621
               Botan::bitvector bv1(100);
1✔
622
               make_bitpattern(bv1);
1✔
623

624
               bv1.subvector_replace(0, uint8_t(0));
1✔
625
               check_bitpattern_with_zero_region(result, bv1, {0, 8});
1✔
626
               bv1.subvector_replace(0, bitpattern_at(uint8_t(0), 0));
2✔
627
               check_bitpattern(result, bv1);
1✔
628

629
               bv1.subvector_replace(32, uint8_t(0));
1✔
630
               check_bitpattern_with_zero_region(result, bv1, {32, 32 + 8});
1✔
631
               bv1.subvector_replace(32, bitpattern_at(uint8_t(0), 32));
2✔
632
               check_bitpattern(result, bv1);
1✔
633

634
               bv1.subvector_replace(56, uint16_t(0));
1✔
635
               check_bitpattern_with_zero_region(result, bv1, {56, 56 + 16});
1✔
636
               bv1.subvector_replace(56, bitpattern_at(uint16_t(0), 56));
2✔
637
               check_bitpattern(result, bv1);
1✔
638

639
               bv1.subvector_replace(48, uint32_t(0));
1✔
640
               check_bitpattern_with_zero_region(result, bv1, {48, 48 + 32});
1✔
641
               bv1.subvector_replace(48, bitpattern_at(uint32_t(0), 48));
2✔
642
               check_bitpattern(result, bv1);
1✔
643

644
               bv1.subvector_replace(16, uint64_t(0));
1✔
645
               check_bitpattern_with_zero_region(result, bv1, {16, 16 + 64});
1✔
646
               bv1.subvector_replace(16, bitpattern_at(uint64_t(0), 16));
2✔
647
               check_bitpattern(result, bv1);
1✔
648

649
               result.test_throws("out of range (uint8_t)", [&] { bv1.subvector_replace<uint8_t>(93, 42); });
2✔
650
               result.test_throws("out of range (uint16_t)", [&] { bv1.subvector_replace<uint16_t>(85, 42); });
2✔
651
               result.test_throws("out of range (uint32_t)", [&] { bv1.subvector_replace<uint32_t>(69, 42); });
2✔
652
               result.test_throws("out of range (uint64_t)", [&] { bv1.subvector_replace<uint64_t>(37, 42); });
2✔
653
            }),
1✔
654

655
      CHECK("unaligned unsigned integer subvector replacement",
656
            [&](auto& result) {
1✔
657
               Botan::bitvector bv1(100);
1✔
658
               make_bitpattern(bv1);
1✔
659

660
               bv1.subvector_replace(3, uint8_t(0));
1✔
661
               check_bitpattern_with_zero_region(result, bv1, {3, 3 + 8});
1✔
662
               bv1.subvector_replace(3, bitpattern_at(uint8_t(0), 3));
2✔
663
               check_bitpattern(result, bv1);
1✔
664

665
               bv1.subvector_replace(92, uint8_t(0));
1✔
666
               check_bitpattern_with_zero_region(result, bv1, {92, 92 + 8});
1✔
667
               bv1.subvector_replace(92, bitpattern_at(uint8_t(0), 92));
2✔
668
               check_bitpattern(result, bv1);
1✔
669

670
               bv1.subvector_replace(7, uint16_t(0));
1✔
671
               check_bitpattern_with_zero_region(result, bv1, {7, 7 + 16});
1✔
672
               bv1.subvector_replace(7, bitpattern_at(uint16_t(0), 7));
2✔
673
               check_bitpattern(result, bv1);
1✔
674

675
               bv1.subvector_replace(84, uint16_t(0));
1✔
676
               check_bitpattern_with_zero_region(result, bv1, {84, 84 + 16});
1✔
677
               bv1.subvector_replace(84, bitpattern_at(uint16_t(0), 84));
2✔
678
               check_bitpattern(result, bv1);
1✔
679

680
               bv1.subvector_replace(11, uint32_t(0));
1✔
681
               check_bitpattern_with_zero_region(result, bv1, {11, 11 + 32});
1✔
682
               bv1.subvector_replace(11, bitpattern_at(uint32_t(0), 11));
2✔
683
               check_bitpattern(result, bv1);
1✔
684

685
               bv1.subvector_replace(68, uint32_t(0));
1✔
686
               check_bitpattern_with_zero_region(result, bv1, {68, 68 + 32});
1✔
687
               bv1.subvector_replace(68, bitpattern_at(uint32_t(0), 68));
2✔
688
               check_bitpattern(result, bv1);
1✔
689

690
               bv1.subvector_replace(21, uint64_t(0));
1✔
691
               check_bitpattern_with_zero_region(result, bv1, {21, 21 + 64});
1✔
692
               bv1.subvector_replace(21, bitpattern_at(uint64_t(0), 21));
2✔
693
               check_bitpattern(result, bv1);
1✔
694
            }),
1✔
695
   };
11✔
696
}
1✔
697

698
std::vector<Test::Result> test_bitvector_global_modifiers_and_predicates(Botan::RandomNumberGenerator& /*rng*/) {
1✔
699
   auto make_bitpattern = [](auto& bitvector) {
3✔
700
      auto next = pattern_generator<5>();
2✔
701
      for(auto& i : bitvector) {
400✔
702
         i = next();
198✔
703
      }
704
   };
2✔
705

706
   auto check_bitpattern = [](auto& result, auto& bitvector) {
2✔
707
      auto next = pattern_generator<5>();
1✔
708
      for(size_t i = 0; i < bitvector.size(); ++i) {
100✔
709
         result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], next());
198✔
710
      }
711
   };
1✔
712

713
   auto check_flipped_bitpattern = [](auto& result, auto& bitvector) {
2✔
714
      auto next = pattern_generator<5>();
1✔
715
      for(size_t i = 0; i < bitvector.size(); ++i) {
100✔
716
         result.test_bool_eq(Botan::fmt("{} is as expected", i), bitvector[i], !next());
198✔
717
      }
718
   };
1✔
719

720
   return {
1✔
721
      CHECK("one bit",
722
            [](auto& result) {
1✔
723
               Botan::bitvector bv;
1✔
724
               bv.push_back(true);
1✔
725

726
               bv.flip();
1✔
727
               result.test_is_true("bit is flipped", !bv[0]);
1✔
728

729
               // check that unused bits aren't flipped
730
               bv.resize(8);
1✔
731
               for(auto&& b : bv) {
10✔
732
                  result.test_is_true("all bits are false", !b);
8✔
733
               }
734
               bv.resize(1);
1✔
735

736
               bv.flip();
1✔
737
               result.test_is_true("bit is flipped again", bv[0]);
1✔
738
            }),
1✔
739

740
      CHECK("bits in many blocks",
741
            [&](auto& result) {
1✔
742
               Botan::bitvector bv(99);
1✔
743

744
               make_bitpattern(bv);
1✔
745
               bv.flip();
1✔
746
               check_flipped_bitpattern(result, bv);
1✔
747

748
               bv = ~bv;
2✔
749
               check_bitpattern(result, bv);
1✔
750

751
               bv.resize(112);
1✔
752
               for(size_t i = 99; i < bv.size(); ++i) {
14✔
753
                  result.test_is_true("just-allocated bit is not set", !bv[i]);
13✔
754
               }
755
            }),
1✔
756

757
      CHECK("set and unset",
758
            [&](auto& result) {
1✔
759
               Botan::bitvector bv(99);
1✔
760

761
               make_bitpattern(bv);
1✔
762
               bv.set();
1✔
763
               bv.resize(128);
1✔
764
               for(size_t i = 0; i < bv.size(); ++i) {
129✔
765
                  const bool expected = (i < 99);
128✔
766
                  result.test_bool_eq("only set bits are set", bv[i], expected);
128✔
767
               }
768

769
               bv.unset();
1✔
770
               for(auto&& b : bv) {
130✔
771
                  result.test_is_true("bit is not set", !b);
128✔
772
               }
773
            }),
1✔
774

775
      CHECK("any, none and all",
776
            [&](auto& result) {
1✔
777
               Botan::bitvector bv(99);
1✔
778

779
               result.test_is_true("default construction yields all-zero", bv.none_vartime());
1✔
780
               result.test_is_true("default construction yields all-zero 2", !bv.any_vartime());
1✔
781
               result.test_is_true("default construction yields all-zero 3", !bv.all_vartime());
1✔
782
               result.test_is_true("default construction yields all-zero 4", bv.none());
1✔
783
               result.test_is_true("default construction yields all-zero 5", !bv.any());
1✔
784
               result.test_is_true("default construction yields all-zero 6", !bv.all());
1✔
785

786
               bv.set(42);
1✔
787
               result.test_is_true("setting a bit means there's a bit set", !bv.none_vartime());
1✔
788
               result.test_is_true("setting a bit means there's a bit set 2", bv.any_vartime());
1✔
789
               result.test_is_true("setting a bit means there's not all bits set", !bv.all_vartime());
1✔
790
               result.test_is_true("setting a bit means there's a bit set 3", !bv.none());
1✔
791
               result.test_is_true("setting a bit means there's a bit set 4", bv.any());
1✔
792
               result.test_is_true("setting a bit means there's not all bits set 2", !bv.all());
1✔
793

794
               bv.set();
1✔
795
               result.test_is_true("setting all bits means there's a bit set", !bv.none_vartime());
1✔
796
               result.test_is_true("setting all bits means there's a bit set 2", bv.any_vartime());
1✔
797
               result.test_is_true("setting all bits means all bits are set", bv.all_vartime());
1✔
798
               result.test_is_true("setting all bits means there's a bit set 3", !bv.none());
1✔
799
               result.test_is_true("setting all bits means there's a bit set 4", bv.any());
1✔
800
               result.test_is_true("setting all bits means all bits are set 2", bv.all());
1✔
801

802
               bv.unset(97);
1✔
803
               result.test_is_true("a single 0 at the end means that there's a bit set", !bv.none_vartime());
1✔
804
               result.test_is_true("a single 0 at the end means that there are bits set", bv.any_vartime());
1✔
805
               result.test_is_true("a single 0 at the end means that there are not all bits set", !bv.all_vartime());
1✔
806
               result.test_is_true("a single 0 at the end means that there's a bit set 2", !bv.none());
1✔
807
               result.test_is_true("a single 0 at the end means that there are bits set 2", bv.any());
1✔
808
               result.test_is_true("a single 0 at the end means that there are not all bits set 2", !bv.all());
1✔
809

810
               bv.unset();
1✔
811
               result.test_is_true("unsetting all bits means there's no bit set", bv.none_vartime());
1✔
812
               result.test_is_true("unsetting all bits means there's no bit set 2", !bv.any_vartime());
1✔
813
               result.test_is_true("unsetting all bits means there's not all bits set", !bv.all_vartime());
1✔
814
               result.test_is_true("unsetting all bits means there's no bit set 3", bv.none());
1✔
815
               result.test_is_true("unsetting all bits means there's no bit set 4", !bv.any());
1✔
816
               result.test_is_true("unsetting all bits means there's not all bits set 2", !bv.all());
1✔
817
            }),
1✔
818

819
      CHECK("hamming weight oddness",
820
            [](auto& result) {
1✔
821
               const auto even = Botan::hex_decode("FE3410CB0278E4D26602");
1✔
822
               const auto odd = Botan::hex_decode("BB2418C2B4F288921203");
1✔
823

824
               result.test_is_true("odd hamming", Botan::bitvector(odd).has_odd_hamming_weight().as_bool());
2✔
825
               result.test_is_true("even hamming", !Botan::bitvector(even).has_odd_hamming_weight().as_bool());
2✔
826
            }),
2✔
827

828
      CHECK("hamming weight",
829
            [](auto& result) {
1✔
830
               auto naive_count = [](const auto& v) {
5✔
831
                  size_t weight = 0;
5✔
832
                  for(const auto& bit : v) {
440✔
833
                     weight += bit.template as<size_t>();
430✔
834
                  }
835
                  return weight;
5✔
836
               };
837

838
               // the last three bits of this bitvector are set, then there's a gap
839
               auto bv = Botan::bitvector(Botan::hex_decode("FE3410CB0278E4D26602E0"));
2✔
840
               result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(37));
1✔
841
               result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv));
1✔
842

843
               bv.pop_back();
1✔
844
               result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(36));
1✔
845
               result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv));
1✔
846

847
               bv.pop_back();
1✔
848
               result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(35));
1✔
849
               result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv));
1✔
850

851
               bv.pop_back();
1✔
852
               result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(34));
1✔
853
               result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv));
1✔
854

855
               bv.pop_back();
1✔
856
               result.test_sz_eq("hamming weight", bv.hamming_weight(), size_t(34));
1✔
857
               result.test_sz_eq("hamming weight", bv.hamming_weight(), naive_count(bv));
1✔
858
            }),
1✔
859
   };
7✔
860
}
1✔
861

862
std::vector<Test::Result> test_bitvector_binary_operators(Botan::RandomNumberGenerator& /*rng*/) {
1✔
863
   auto check_set = [](auto& result, auto bits, std::vector<size_t> set_bits) {
13✔
864
      for(size_t i = 0; i < bits.size(); ++i) {
252✔
865
         const bool should_be_set = std::find(set_bits.begin(), set_bits.end(), i) != set_bits.end();
240✔
866
         result.test_bool_eq(
240✔
867
            Botan::fmt("{} should {}be set", i, (!should_be_set ? "not " : "")), bits[i], should_be_set);
661✔
868
      }
869
   };
12✔
870

871
   auto is_secure_allocator = []<template <typename> typename AllocatorT>(auto& result,
7✔
872
                                                                          const Botan::bitvector_base<AllocatorT>&) {
873
      result.test_is_true("allocator is Botan::secure_allocator<>",
12✔
874
                          std::same_as<Botan::secure_allocator<uint8_t>, AllocatorT<uint8_t>>);
875
   };
876

877
   auto is_standard_allocator = []<template <typename> typename AllocatorT>(auto& result,
4✔
878
                                                                            const Botan::bitvector_base<AllocatorT>&) {
879
      result.test_is_true("allocator is std::allocator<>", std::same_as<std::allocator<uint8_t>, AllocatorT<uint8_t>>);
6✔
880
   };
881

882
   return {
1✔
883
      CHECK("bitwise_equals",
884
            [&](auto& result) {
1✔
885
               Botan::bitvector lhs(20);
1✔
886
               lhs.set(0).set(4).set(15).set(16).set(19);
1✔
887
               Botan::bitvector rhs(20);
1✔
888
               rhs.set(1).set(4).set(16).set(17).set(18);
1✔
889

890
               result.test_is_false("Not equal bitvectors", lhs.equals_vartime(rhs));
1✔
891
               result.test_is_false("Not equal bitvectors 2", lhs.equals(rhs));
1✔
892

893
               lhs.unset().set(13);
1✔
894
               rhs.unset().set(13);
1✔
895

896
               result.test_is_true("equal bitvectors", lhs.equals_vartime(rhs));
1✔
897
               result.test_is_true("equal bitvectors 2", lhs.equals(rhs));
1✔
898
            }),
2✔
899

900
      CHECK("bitwise OR",
901
            [&](auto& result) {
1✔
902
               Botan::bitvector lhs(20);
1✔
903
               lhs.set(0).set(4).set(15).set(16).set(19);
1✔
904
               Botan::bitvector rhs(20);
1✔
905
               rhs.set(1).set(4).set(16).set(17).set(18);
1✔
906
               Botan::bitvector unary(20);
1✔
907
               unary.set(8);
1✔
908

909
               Botan::bitvector res = lhs | rhs;
1✔
910
               check_set(result, res, {0, 1, 4, 15, 16, 17, 18, 19});
4✔
911

912
               res |= unary;
1✔
913
               check_set(result, res, {0, 1, 4, 8, 15, 16, 17, 18, 19});
3✔
914

915
               is_standard_allocator(result, res);
1✔
916
            }),
4✔
917

918
      CHECK("bitwise AND",
919
            [&](auto& result) {
1✔
920
               Botan::bitvector lhs(20);
1✔
921
               lhs.set(0).set(4).set(15).set(16).set(18);
1✔
922
               Botan::bitvector rhs(20);
1✔
923
               rhs.set(1).set(4).set(16).set(17).set(18);
1✔
924
               Botan::bitvector unary(20);
1✔
925
               unary.set(8).set(16);
1✔
926

927
               Botan::bitvector res = lhs & rhs;
1✔
928
               check_set(result, res, {4, 16, 18});
4✔
929

930
               res &= unary;
1✔
931
               check_set(result, res, {16});
3✔
932

933
               is_standard_allocator(result, res);
1✔
934
            }),
4✔
935

936
      CHECK("bitwise XOR",
937
            [&](auto& result) {
1✔
938
               Botan::bitvector lhs(20);
1✔
939
               lhs.set(0).set(4).set(15).set(16).set(18);
1✔
940
               Botan::bitvector rhs(20);
1✔
941
               rhs.set(1).set(4).set(16).set(17).set(18);
1✔
942
               Botan::bitvector unary(20);
1✔
943
               unary.set(8).set(16);
1✔
944

945
               Botan::bitvector res = lhs ^ rhs;
1✔
946
               check_set(result, res, {0, 1, 15, 17});
4✔
947

948
               res ^= unary;
1✔
949
               check_set(result, res, {0, 1, 8, 15, 16, 17});
3✔
950

951
               is_standard_allocator(result, res);
1✔
952
            }),
4✔
953

954
      CHECK("bitwise operators with heterogeneous allocators",
955
            [&](auto& result) {
1✔
956
               Botan::bitvector lhs(20);
1✔
957
               lhs.set(0).set(4).set(15).set(16).set(18);
1✔
958
               Botan::secure_bitvector rhs(20);
1✔
959
               rhs.set(1).set(4).set(16).set(17).set(18);
1✔
960
               Botan::bitvector unary(20);
1✔
961
               unary.set(8).set(16);
1✔
962

963
               auto res1 = lhs | rhs;
1✔
964
               is_secure_allocator(result, res1);
1✔
965
               check_set(result, res1, {0, 1, 4, 15, 16, 17, 18});
3✔
966

967
               auto res2 = rhs | lhs;
1✔
968
               is_secure_allocator(result, res2);
1✔
969
               check_set(result, res2, {0, 1, 4, 15, 16, 17, 18});
3✔
970

971
               auto res3 = lhs & rhs;
1✔
972
               is_secure_allocator(result, res3);
1✔
973
               check_set(result, res3, {4, 16, 18});
3✔
974

975
               auto res4 = rhs & lhs;
1✔
976
               is_secure_allocator(result, res4);
1✔
977
               check_set(result, res4, {4, 16, 18});
3✔
978

979
               auto res5 = lhs ^ rhs;
1✔
980
               is_secure_allocator(result, res5);
1✔
981
               check_set(result, res5, {0, 1, 15, 17});
3✔
982

983
               auto res6 = rhs ^ lhs;
1✔
984
               is_secure_allocator(result, res6);
1✔
985
               check_set(result, res6, {0, 1, 15, 17});
4✔
986
            }),
9✔
987
   };
6✔
988
}
1✔
989

990
std::vector<Test::Result> test_bitvector_serialization(Botan::RandomNumberGenerator& /*rng*/) {
1✔
991
   constexpr uint8_t outlen = 64;
1✔
992
   const auto bytearray = [] {
1✔
993
      std::array<uint8_t, outlen> out{};
994
      for(uint8_t i = 0; i < outlen; ++i) {
995
         out[i] = i;
996
      }
997
      return out;
998
   }();
999

1000
   auto validate_bytewise = [](auto& result, const auto& bv, std::span<const uint8_t> bytes) {
3✔
1001
      for(size_t i = 0; i < bytes.size(); ++i) {
129✔
1002
         const uint8_t b = (static_cast<uint8_t>(bv[0 + i * 8]) << 0) | (static_cast<uint8_t>(bv[1 + i * 8]) << 1) |
127✔
1003
                           (static_cast<uint8_t>(bv[2 + i * 8]) << 2) | (static_cast<uint8_t>(bv[3 + i * 8]) << 3) |
127✔
1004
                           (static_cast<uint8_t>(bv[4 + i * 8]) << 4) | (static_cast<uint8_t>(bv[5 + i * 8]) << 5) |
127✔
1005
                           (static_cast<uint8_t>(bv[6 + i * 8]) << 6) | (static_cast<uint8_t>(bv[7 + i * 8]) << 7);
127✔
1006

1007
         result.test_sz_eq(
254✔
1008
            Botan::fmt("byte {} is as expected", i), static_cast<size_t>(b), static_cast<size_t>(bytes[i]));
127✔
1009
      }
1010
   };
2✔
1011

1012
   return {
1✔
1013
      CHECK("empty byte-array",
1014
            [](auto& result) {
1✔
1015
               std::vector<uint8_t> bytes;
1✔
1016
               result.require("empty buffer", bytes.empty());
1✔
1017

1018
               const Botan::bitvector bv(bytes);
1✔
1019
               result.test_is_true("empty bit vector", bv.empty());
1✔
1020

1021
               auto rendered = bv.to_bytes();
1✔
1022
               result.test_is_true("empty bit vector renders an empty buffer", rendered.empty());
1✔
1023
            }),
1✔
1024

1025
      CHECK("to_bytes() uses secure_allocator if necessary",
1026
            [](auto& result) {
1✔
1027
               const Botan::bitvector bv;
1✔
1028
               const Botan::secure_bitvector sbv;
1✔
1029

1030
               auto rbv = bv.to_bytes();
1✔
1031
               auto rsbv = sbv.to_bytes();
1✔
1032

1033
               result.test_is_true("ordinary bitvector uses ordinary std::vector",
1✔
1034
                                   std::is_same_v<std::vector<uint8_t>, decltype(rbv)>);
1035
               result.test_is_true("secure bitvector uses secure_vector",
1✔
1036
                                   std::is_same_v<Botan::secure_vector<uint8_t>, decltype(rsbv)>);
1037
            }),
1✔
1038

1039
      CHECK("load all bits from byte-array (aligned data)",
1040
            [&](auto& result) {
1✔
1041
               const Botan::bitvector bv(bytearray);
1✔
1042
               validate_bytewise(result, bv, bytearray);
1✔
1043

1044
               const auto rbv = bv.to_bytes();
1✔
1045
               result.test_is_true("uint8_t rendered correctly", std::ranges::equal(bytearray, rbv));
2✔
1046
            }),
2✔
1047

1048
      CHECK("load all bits from byte-array (unaligned blocks)",
1049
            [&](auto& result) {
1✔
1050
               std::array<uint8_t, 63> unaligned_bytearray{};
1✔
1051
               Botan::copy_mem(unaligned_bytearray, std::span{bytearray}.first<unaligned_bytearray.size()>());
1✔
1052

1053
               const Botan::bitvector bv(unaligned_bytearray);
1✔
1054
               validate_bytewise(result, bv, unaligned_bytearray);
1✔
1055

1056
               const auto rbv = bv.to_bytes();
1✔
1057
               result.test_is_true("uint8_t rendered correctly", std::ranges::equal(unaligned_bytearray, rbv));
1✔
1058
            }),
3✔
1059

1060
      CHECK("load bits from byte-array (unaligned data)",
1061
            [&](auto& result) {
1✔
1062
               constexpr size_t bits_to_load = 31;
1✔
1063
               constexpr size_t bytes_to_load = Botan::ceil_tobytes(bits_to_load);
1✔
1064

1065
               Botan::bitvector bv(bytearray, bits_to_load);
1✔
1066

1067
               for(size_t i = 0; i < bits_to_load; ++i) {
32✔
1068
                  const bool expected = (i == 8) || (i == 17) || (i == 24) || (i == 25);
31✔
1069
                  result.test_bool_eq(Botan::fmt("bit {} is correct", i), bv.at(i), expected);
62✔
1070
               }
1071

1072
               const auto rbv = bv.to_bytes();
1✔
1073
               std::array<uint8_t, bytes_to_load> expected_bytes{};
1✔
1074
               Botan::copy_mem(expected_bytes, std::span{bytearray}.first<bytes_to_load>());
1✔
1075
               expected_bytes.back() &= (uint8_t(1) << (bits_to_load % 8)) - 1;
1✔
1076
               result.test_is_true("uint8_t rendered correctly", std::ranges::equal(expected_bytes, rbv));
1✔
1077
            }),
2✔
1078

1079
      CHECK("to_bytes(std::span) can handle non-zero out-memory",
1080
            [&](auto& result) {
1✔
1081
               constexpr size_t bits_to_load = 33;
1✔
1082
               constexpr size_t bytes_to_load = Botan::ceil_tobytes(bits_to_load);
1✔
1083

1084
               Botan::bitvector bv(bytearray, bits_to_load);
1✔
1085
               bv.set(32);
1✔
1086

1087
               std::array<uint8_t, bytes_to_load> out = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1088
               bv.to_bytes(out);
1✔
1089

1090
               result.test_u8_eq("uint8_t rendered correctly", out[4], 0x01);
1✔
1091
            }),
1✔
1092
   };
7✔
1093
}
1✔
1094

1095
std::vector<Test::Result> test_bitvector_constant_time_operations(Botan::RandomNumberGenerator& /*rng*/) {
1✔
1096
   constexpr Botan::CT::Choice yes = Botan::CT::Choice::yes();
1✔
1097
   constexpr Botan::CT::Choice no = Botan::CT::Choice::no();
1✔
1098

1099
   return {
1✔
1100
      CHECK("conditional XOR, block aligned",
1101
            [&](auto& result) {
1✔
1102
               Botan::bitvector bv1(Botan::hex_decode("BAADF00DCAFEBEEF"));
2✔
1103
               const Botan::secure_bitvector bv2(Botan::hex_decode("CAFEBEEFC001B33F"));
2✔
1104
               const auto initial_bv1 = bv1;
1✔
1105
               const auto xor_result = bv1 ^ bv2;
1✔
1106

1107
               bv1.ct_conditional_xor(no, bv2);
1✔
1108
               result.test_is_true("no change after false condition", bv1 == initial_bv1);
1✔
1109

1110
               bv1.ct_conditional_xor(yes, bv2);
1✔
1111
               result.test_is_true("XORed if condition was true", bv1 == xor_result);
1✔
1112
            }),
4✔
1113

1114
      CHECK("conditional XOR, byte aligned",
1115
            [&](auto& result) {
1✔
1116
               Botan::bitvector bv1(Botan::hex_decode("BAADF00DCAFEBEEF42"));
2✔
1117
               const Botan::secure_bitvector bv2(Botan::hex_decode("CAFEBEEFC001B33F13"));
2✔
1118
               const auto initial_bv1 = bv1;
1✔
1119
               const auto xor_result = bv1 ^ bv2;
1✔
1120

1121
               bv1.ct_conditional_xor(no, bv2);
1✔
1122
               result.test_is_true("no change after false condition", bv1 == initial_bv1);
1✔
1123

1124
               bv1.ct_conditional_xor(yes, bv2);
1✔
1125
               result.test_is_true("XORed if condition was true", bv1 == xor_result);
1✔
1126
            }),
4✔
1127

1128
      CHECK("conditional XOR, no alignment",
1129
            [&](auto& result) {
1✔
1130
               Botan::bitvector bv1(Botan::hex_decode("BAADF00DCAFEBEEF42"));
1✔
1131
               bv1.push_back(true);
1✔
1132
               bv1.push_back(false);
1✔
1133
               Botan::secure_bitvector bv2(Botan::hex_decode("CAFEBEEFC001B33F13"));
1✔
1134
               bv2.push_back(false);
1✔
1135
               bv2.push_back(false);
1✔
1136

1137
               const auto initial_bv1 = bv1;
1✔
1138
               const auto xor_result = bv1 ^ bv2;
1✔
1139

1140
               bv1.ct_conditional_xor(no, bv2);
1✔
1141
               result.test_is_true("no change after false condition", bv1 == initial_bv1);
1✔
1142

1143
               bv1.ct_conditional_xor(yes, bv2);
1✔
1144
               result.test_is_true("XORed if condition was true", bv1 == xor_result);
1✔
1145
            }),
4✔
1146
   };
4✔
1147
}
1✔
1148

1149
std::vector<Test::Result> test_bitvector_conditional_xor_workload(Botan::RandomNumberGenerator& /*rng*/) {
1✔
1150
   Test::Result res("Conditional XOR, Gauss Workload");
1✔
1151

1152
   auto rng = Test::new_rng("Conditional XOR, Gauss Workload");
1✔
1153

1154
   const size_t matrix_rows = 1664;
1✔
1155
   const size_t matrix_columns = 8192;
1✔
1156

1157
   std::vector<Botan::bitvector> bitvec_vec;
1✔
1158
   bitvec_vec.reserve(matrix_rows);
1✔
1159
   for(size_t i = 0; i < matrix_rows; ++i) {
1,665✔
1160
      bitvec_vec.push_back(Botan::bitvector(rng->random_vec(matrix_columns / 8)));
4,992✔
1161
   }
1162

1163
   // Simulate #ops of Gaussian Elimination
1164
   const size_t total_iter = matrix_rows * (3 * matrix_rows - 1) / 2;
1✔
1165
   const auto start = Test::timestamp();
1✔
1166
   for(size_t i = 0; i < total_iter; ++i) {
4,152,513✔
1167
      const auto choice = Botan::CT::Choice::from_int(static_cast<uint8_t>(rng->next_byte() % 2));
4,152,512✔
1168
      bitvec_vec.at(i % matrix_rows).ct_conditional_xor(choice, bitvec_vec.at(rng->next_byte() % matrix_rows));
4,152,512✔
1169
   }
1170
   res.set_ns_consumed(Test::timestamp() - start);
1✔
1171

1172
   res.test_is_true("Prevent compiler from optimizing away",
2✔
1173
                    bitvec_vec.at(0).any_vartime() || bitvec_vec.at(0).none_vartime());
1✔
1174
   return {res};
3✔
1175
}
3✔
1176

1177
std::vector<Test::Result> test_bitvector_iterators(Botan::RandomNumberGenerator& /*rng*/) {
1✔
1178
   return {
1✔
1179
      CHECK("Iterators: range-based for loop",
1180
            [](auto& result) {
1✔
1181
               Botan::bitvector bv(6);
1✔
1182
               bv.set(0).set(3).set(4);
1✔
1183

1184
               for(size_t i = 0; auto& ref : bv) {
8✔
1185
                  const bool expected = i == 0 || i == 3 || i == 4;
6✔
1186
                  result.test_bool_eq(Botan::fmt("bit {} is as expected", i), ref, expected);
6✔
1187
                  ++i;
6✔
1188
               }
1189

1190
               for(size_t i = 0; const auto& ref : bv) {
8✔
1191
                  const bool expected = i == 0 || i == 3 || i == 4;
6✔
1192
                  result.test_bool_eq(Botan::fmt("const bit {} is as expected", i), ref, expected);
6✔
1193
                  ++i;
6✔
1194
               }
1195

1196
               for(auto ref : bv) {
14✔
1197
                  ref = true;
6✔
1198
               }
1199

1200
               result.test_is_true("all bits are set", bv.all_vartime());
1✔
1201
            }),
1✔
1202

1203
      CHECK("Iterators: bare usage",
1204
            [](auto& result) {
1✔
1205
               Botan::bitvector bv(6);
1✔
1206
               bv.set(0).set(3).set(4);
1✔
1207

1208
               size_t i = 0;
1✔
1209
               for(auto itr = bv.begin(); itr != bv.end(); ++itr, ++i) {
7✔
1210
                  const bool expected = i == 0 || i == 3 || i == 4;
6✔
1211
                  result.test_bool_eq(Botan::fmt("bit {} is as expected", i), *itr, expected);
12✔
1212
               }
1213

1214
               i = 0;
1✔
1215
               for(auto itr = bv.cbegin(); itr != bv.cend(); itr++, ++i) {
7✔
1216
                  const bool expected = i == 0 || i == 3 || i == 4;
6✔
1217
                  result.test_bool_eq(Botan::fmt("const bit {} is as expected", i), itr->is_set(), expected);
12✔
1218
               }
1219

1220
               i = 6;
1✔
1221
               auto ritr = bv.end();
1✔
1222
               // NOLINTNEXTLINE(*-avoid-do-while)
1223
               do {
1224
                  --ritr;
6✔
1225
                  --i;
6✔
1226
                  const bool expected = i == 0 || i == 3 || i == 4;
6✔
1227
                  result.test_bool_eq(Botan::fmt("reverse bit {} is as expected", i), *ritr, expected);
6✔
1228
               } while(ritr != bv.begin());
11✔
1229

1230
               for(auto& itr : bv) {
8✔
1231
                  itr.flip();
6✔
1232
               }
1233

1234
               i = 0;
1✔
1235
               for(auto itr = bv.begin(); itr != bv.end(); ++itr, ++i) {
7✔
1236
                  const bool expected = i == 1 || i == 2 || i == 5;
6✔
1237
                  result.test_bool_eq(Botan::fmt("flipped bit {} is as expected", i), *itr, expected);
12✔
1238
               }
1239
            }),
1✔
1240

1241
      CHECK("Iterators: std::distance and std::advance",
1242
            [](auto& result) {
1✔
1243
               Botan::bitvector bv(6);
1✔
1244
               result.test_sz_eq("distance", static_cast<size_t>(std::distance(bv.begin(), bv.end())), 6);
2✔
1245
               result.test_sz_eq("const distance", static_cast<size_t>(std::distance(bv.cbegin(), bv.cend())), 6);
2✔
1246

1247
               auto b = bv.begin();
1✔
1248
               std::advance(b, 3);
1✔
1249
               result.test_sz_eq("half distance", static_cast<size_t>(std::distance(bv.begin(), b)), 3);
2✔
1250
            }),
1✔
1251

1252
      CHECK("Iterators: large bitvector",
1253
            [](auto& result) {
1✔
1254
               Botan::bitvector bv(500);
1✔
1255

1256
               for(auto itr = bv.begin(); itr != bv.end(); ++itr) {
1,001✔
1257
                  if(std::distance(bv.begin(), itr) % 2 == 0) {
1,000✔
1258
                     itr->set();
250✔
1259
                  }
1260
                  if(std::distance(bv.begin(), itr) % 3 == 0) {
1,000✔
1261
                     *itr = true;
167✔
1262
                  }
1263
               }
1264

1265
               for(size_t i = 0; const auto& bit : bv) {
502✔
1266
                  const bool expected = (i % 2 == 0) || (i % 3 == 0);
500✔
1267
                  result.test_bool_eq(Botan::fmt("bit {} is as expected", i), bit, expected);
500✔
1268
                  ++i;
500✔
1269
               }
1270
            }),
1✔
1271

1272
      CHECK("Iterators: satiesfies C++20 concepts",
1273
            [](auto& result) {
1✔
1274
               Botan::secure_bitvector bv(42);
1✔
1275
               auto ro_itr = bv.cbegin();
1✔
1276
               auto rw_itr = bv.begin();
1✔
1277

1278
               using ro = decltype(ro_itr);
1279
               using rw = decltype(rw_itr);
1280

1281
               result.test_is_true("ro input iterator", std::input_iterator<ro>);
1✔
1282
               result.test_is_true("rw input iterator", std::input_iterator<rw>);
1✔
1283
               result.test_is_true("ro is not an output iterator", !std::output_iterator<ro, bool>);
1✔
1284
               result.test_is_true("rw output iterator", std::output_iterator<rw, bool>);
1✔
1285
               result.test_is_true("ro bidirectional iterator", std::bidirectional_iterator<ro>);
1✔
1286
               result.test_is_true("rw bidirectional iterator", std::bidirectional_iterator<rw>);
1✔
1287
               result.test_is_true("ro not a contiguous iterator", !std::contiguous_iterator<ro>);
1✔
1288
               result.test_is_true("rw not a contiguous iterator", !std::contiguous_iterator<rw>);
1✔
1289
            }),
1✔
1290
   };
6✔
1291
}
1✔
1292

1293
using TestBitvector = Botan::Strong<Botan::bitvector, struct TestBitvector_>;
1294
using TestSecureBitvector = Botan::Strong<Botan::secure_bitvector, struct TestBitvector_>;
1295
using TestUInt32 = Botan::Strong<uint32_t, struct TestUInt32_>;
1296

1297
std::vector<Test::Result> test_bitvector_strongtype_adapter(Botan::RandomNumberGenerator& /*rng*/) {
1✔
1298
   Test::Result result("Bitvector in strong type");
1✔
1299

1300
   TestBitvector bv1(33);
1✔
1301

1302
   result.test_is_true("bv1 is not empty", !bv1.empty());
1✔
1303
   result.test_sz_eq("bv1 has size 33", bv1.size(), size_t(33));
1✔
1304

1305
   bv1[0] = true;
1✔
1306
   bv1.at(1) = true;
1✔
1307
   bv1.set(2);
2✔
1308
   bv1.unset(3);
2✔
1309
   bv1.flip(4);
2✔
1310
   bv1.push_back(true);
1✔
1311
   bv1.push_back(false);
1✔
1312
   bv1.pop_back();
1✔
1313

1314
   result.test_is_true("bv1 front is set", bv1.front());
1✔
1315
   result.test_is_true("bv1 back is set", bv1.back());
1✔
1316
   result.test_is_true("bv1 has some one bits", bv1.any_vartime());
1✔
1317
   result.test_is_true("bv1 is not all zero", !bv1.none_vartime());
1✔
1318
   result.test_is_true("bv1 is not all one", !bv1.all_vartime());
1✔
1319

1320
   result.test_is_true("hamming weight of bv1", bv1.has_odd_hamming_weight().as_bool());
1✔
1321

1322
   for(size_t i = 0; auto bit : bv1) {
36✔
1323
      const bool expected = (i == 0 || i == 1 || i == 2 || i == 4 || i == 33);
34✔
1324
      result.test_is_true(Botan::fmt("bv1 bit {} is set", i), bit == expected);
34✔
1325
      ++i;
34✔
1326
   }
1327

1328
   bv1.flip();
2✔
1329

1330
   for(size_t i = 0; auto bit : bv1) {
36✔
1331
      const bool expected = (i == 0 || i == 1 || i == 2 || i == 4 || i == 33);
34✔
1332
      result.test_is_true(Botan::fmt("bv1 bit {} is set", i), bit != expected);
34✔
1333
      ++i;
34✔
1334
   }
1335

1336
   auto bv2 = bv1.as<TestSecureBitvector>();
1✔
1337

1338
   auto bv3 = bv1 | bv2;
1✔
1339
   result.test_is_true("bv3 is a secure_bitvector", std::same_as<Botan::secure_bitvector, decltype(bv3)>);
1✔
1340

1341
   auto bv4 = bv2.subvector<TestSecureBitvector>(0, 5);
1✔
1342
   result.test_is_true("bv4 is a TestSecureBitvector", std::same_as<TestSecureBitvector, decltype(bv4)>);
1✔
1343

1344
   auto bv5 = bv2.subvector<TestUInt32>(1);
1✔
1345
   result.test_is_true("bv5 is a TestUInt32", std::same_as<TestUInt32, decltype(bv5)>);
1✔
1346
   result.test_u32_eq("bv5 has expected value", bv5.get(), 0xFFFFFFF4);
1✔
1347

1348
   const auto str = bv4.to_string();
1✔
1349
   result.test_str_eq("bv4 to_string", str, "00010");
1✔
1350

1351
   return {result};
3✔
1352
}
6✔
1353

1354
class BitVector_Tests final : public Test {
1✔
1355
   public:
1356
      std::vector<Test::Result> run() override {
1✔
1357
         std::vector<Test::Result> results;
1✔
1358
         auto& rng = Test::rng();
1✔
1359

1360
         const std::vector<std::function<std::vector<Test::Result>(Botan::RandomNumberGenerator&)>> funcs{
1✔
1361
            test_bitvector_bitwise_accessors,
1362
            test_bitvector_capacity,
1363
            test_bitvector_subvector,
1364
            test_bitvector_global_modifiers_and_predicates,
1365
            test_bitvector_binary_operators,
1366
            test_bitvector_serialization,
1367
            test_bitvector_constant_time_operations,
1368
            test_bitvector_conditional_xor_workload,
1369
            test_bitvector_iterators,
1370
            test_bitvector_strongtype_adapter,
1371
         };
11✔
1372

1373
         for(const auto& test_func : funcs) {
11✔
1374
            auto fn_results = test_func(rng);
10✔
1375
            results.insert(results.end(), fn_results.begin(), fn_results.end());
10✔
1376
         }
10✔
1377

1378
         return results;
1✔
1379
      }
2✔
1380
};
1381

1382
BOTAN_REGISTER_TEST("utils", "bitvector", BitVector_Tests);
1383

1384
#endif
1385

1386
}  // namespace
1387

1388
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc