Botan 3.13.0
Crypto and TLS for C&
bitvector.h
Go to the documentation of this file.
1/*
2 * An abstraction for an arbitrarily large bitvector that can
3 * optionally use the secure_allocator. All bitwise accesses and all
4 * constructors are implemented in constant time. Otherwise, only methods
5 * with the "ct_" pre-fix run in constant time.
6 *
7 * (C) 2023-2024 Jack Lloyd
8 * (C) 2023-2024 René Meusel, Rohde & Schwarz Cybersecurity
9 *
10 * Botan is released under the Simplified BSD License (see license.txt)
11 */
12
13#ifndef BOTAN_BIT_VECTOR_H_
14#define BOTAN_BIT_VECTOR_H_
15
16#include <botan/concepts.h>
17#include <botan/exceptn.h>
18#include <botan/mem_ops.h>
19#include <botan/secmem.h>
20#include <botan/strong_type.h>
21#include <botan/internal/bit_ops.h>
22#include <botan/internal/ct_utils.h>
23#include <botan/internal/int_utils.h>
24#include <botan/internal/loadstor.h>
25#include <botan/internal/stl_util.h>
26
27#include <memory>
28#include <optional>
29#include <span>
30#include <sstream>
31#include <string>
32#include <utility>
33#include <vector>
34
35namespace Botan {
36
37template <template <typename> typename AllocatorT>
38class bitvector_base;
39
40template <typename T>
41struct is_bitvector : std::false_type {};
42
43template <template <typename> typename T>
44struct is_bitvector<bitvector_base<T>> : std::true_type {};
45
46template <typename T>
47constexpr static bool is_bitvector_v = is_bitvector<T>::value;
48
49template <typename T>
50concept bitvectorish = is_bitvector_v<strong_type_wrapped_type<T>>;
51
52namespace detail {
53
54template <typename T0, typename... Ts>
55struct first_type {
56 using type = T0;
57};
58
59// get the first type from a parameter pack
60// TODO: C++26 will bring Parameter Pack indexing:
61// using first_t = Ts...[0];
62template <typename... Ts>
63 requires(sizeof...(Ts) > 0)
65
66// get the first object from a parameter pack
67// TODO: C++26 will bring Parameter Pack indexing:
68// auto first = s...[0];
69template <typename T0, typename... Ts>
70constexpr static first_t<T0, Ts...> first(T0&& t, Ts&&... /*rest*/) {
71 return std::forward<T0>(t);
72}
73
74template <typename OutT, typename>
75using as = OutT;
76
77template <typename FnT, std::unsigned_integral BlockT, typename... ParamTs>
78using blockwise_processing_callback_return_type = std::invoke_result_t<FnT, as<BlockT, ParamTs>...>;
79
80template <typename FnT, typename BlockT, typename... ParamTs>
82 std::unsigned_integral<BlockT> &&
83 (std::same_as<BlockT, blockwise_processing_callback_return_type<FnT, BlockT, ParamTs...>> ||
84 std::same_as<bool, blockwise_processing_callback_return_type<FnT, BlockT, ParamTs...>> ||
85 std::same_as<void, blockwise_processing_callback_return_type<FnT, BlockT, ParamTs...>>);
86
87template <typename FnT, typename... ParamTs>
89 is_blockwise_processing_callback_return_type<FnT, uint8_t, ParamTs...> &&
90 is_blockwise_processing_callback_return_type<FnT, uint16_t, ParamTs...> &&
91 is_blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs...> &&
92 is_blockwise_processing_callback_return_type<FnT, uint64_t, ParamTs...>;
93
94template <typename FnT, typename... ParamTs>
96 is_blockwise_processing_callback_return_type<FnT, uint8_t, ParamTs..., uint8_t /* mask */> &&
97 is_blockwise_processing_callback_return_type<FnT, uint16_t, ParamTs..., uint16_t /* mask */> &&
98 is_blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs..., uint32_t /* mask */> &&
99 is_blockwise_processing_callback_return_type<FnT, uint64_t, ParamTs..., uint64_t /* mask */>;
100
101/**
102 * Defines the callback constraints for the BitRangeOperator. For further
103 * details, see bitvector_base::range_operation().
104 */
105template <typename FnT, typename... ParamTs>
108
109template <typename FnT, typename... ParamTs>
112 std::same_as<uint32_t, blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs...>>) ||
114 std::same_as<uint32_t, blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs..., first_t<ParamTs...>>>);
115
116template <typename FnT, typename... ParamTs>
119 std::same_as<bool, blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs...>>) ||
121 std::same_as<bool, blockwise_processing_callback_return_type<FnT, uint32_t, ParamTs..., first_t<ParamTs...>>>);
122
123template <typename T>
125 private:
126 using size_type = typename T::size_type;
127
128 public:
129 using difference_type = std::make_signed_t<size_type>;
130 using value_type = std::remove_const_t<decltype(std::declval<T>().at(0))>;
133
134 // TODO: technically, this could be a random access iterator
135 using iterator_category = std::bidirectional_iterator_tag;
136
137 public:
140
141 bitvector_iterator(T* bitvector, size_t offset) : m_bitvector(bitvector) { update(offset); }
142
143 bitvector_iterator(const bitvector_iterator& other) noexcept : m_bitvector(other.m_bitvector) {
144 update(other.m_offset);
145 }
146
147 bitvector_iterator(bitvector_iterator&& other) noexcept : m_bitvector(other.m_bitvector) {
148 update(other.m_offset);
149 }
150
152 if(this != &other) {
153 m_bitvector = other.m_bitvector;
154 update(other.m_offset);
155 }
156 return *this;
157 }
158
160 m_bitvector = other.m_bitvector;
161 update(other.m_offset);
162 return *this;
163 }
164
166 update(signed_offset() + 1);
167 return *this;
168 }
169
171 auto copy = *this;
172 update(signed_offset() + 1);
173 return copy;
174 }
175
177 update(signed_offset() - 1);
178 return *this;
179 }
180
182 auto copy = *this;
183 update(signed_offset() - 1);
184 return copy;
185 }
186
187 std::partial_ordering operator<=>(const bitvector_iterator& other) const noexcept {
188 if(m_bitvector == other.m_bitvector) {
189 return m_offset <=> other.m_offset;
190 } else {
191 return std::partial_ordering::unordered;
192 }
193 }
194
195 bool operator==(const bitvector_iterator& other) const noexcept {
196 return m_bitvector == other.m_bitvector && m_offset == other.m_offset;
197 }
198
199 reference operator*() const { return m_bitref.value(); }
200
201 pointer operator->() const { return &(m_bitref.value()); }
202
203 private:
204 void update(size_type new_offset) {
205 m_offset = new_offset;
206 if(m_offset < m_bitvector->size()) {
207 m_bitref.emplace((*m_bitvector)[m_offset]);
208 } else {
209 // end() iterator
210 m_bitref.reset();
211 }
212 }
213
214 difference_type signed_offset() const { return static_cast<difference_type>(m_offset); }
215
216 private:
217 T* m_bitvector;
218 size_type m_offset;
219 mutable std::optional<value_type> m_bitref;
220};
221
222} // namespace detail
223
224/**
225 * An arbitrarily large bitvector with typical bit manipulation and convenient
226 * bitwise access methods. Don't use `bitvector_base` directly, but the type
227 * aliases::
228 *
229 * * bitvector - with a standard allocator
230 * * secure_bitvector - with a secure allocator that auto-scrubs the memory
231 */
232template <template <typename> typename AllocatorT>
233class bitvector_base final {
234 public:
235 using block_type = uint8_t;
236 using size_type = size_t;
237 using allocator_type = AllocatorT<block_type>;
241
242 static constexpr size_type block_size_bytes = sizeof(block_type);
244 static constexpr bool uses_secure_allocator = std::is_same_v<allocator_type, secure_allocator<block_type>>;
245
246 private:
247 template <template <typename> typename FriendAllocatorT>
248 friend class bitvector_base;
249
250 static constexpr block_type one = block_type(1);
251
252 static constexpr size_type block_offset_shift = size_type(3) + ceil_log2(block_size_bytes);
253 static constexpr size_type block_index_mask = (one << block_offset_shift) - 1;
254
255 static constexpr size_type block_index(size_type pos) { return pos >> block_offset_shift; }
256
257 static constexpr size_type block_offset(size_type pos) { return pos & block_index_mask; }
258
259 private:
260 /**
261 * Internal helper to wrap a single bit in the bitvector and provide
262 * certain convenience access methods.
263 */
264 template <typename BlockT>
265 requires std::same_as<block_type, std::remove_cv_t<BlockT>>
266 class bitref_base {
267 private:
268 friend class bitvector_base<AllocatorT>;
269
270 constexpr bitref_base(std::span<BlockT> blocks, size_type pos) noexcept :
271 m_block(blocks[block_index(pos)]), m_mask(one << block_offset(pos)) {}
272
273 public:
274 bitref_base() = delete;
275 bitref_base(const bitref_base&) noexcept = default;
276 bitref_base(bitref_base&&) noexcept = default;
277 bitref_base& operator=(const bitref_base&) = delete;
278 bitref_base& operator=(bitref_base&&) = delete;
279
280 ~bitref_base() = default;
281
282 public:
283 // NOLINTNEXTLINE(*-explicit-conversions)
284 constexpr operator bool() const noexcept { return is_set(); }
285
286 constexpr bool is_set() const noexcept { return (m_block & m_mask) > 0; }
287
288 template <std::unsigned_integral T>
289 constexpr T as() const noexcept {
290 return static_cast<T>(is_set());
291 }
292
293 constexpr CT::Choice as_choice() const noexcept {
294 return CT::Choice::from_int(static_cast<BlockT>(m_block & m_mask));
295 }
296
297 protected:
298 BlockT& m_block; // NOLINT(*-non-private-member-variable*)
299 BlockT m_mask; // NOLINT(*-non-private-member-variable*)
300 };
301
302 public:
303 /**
304 * Wraps a constant reference into the bitvector. Bit can be accessed
305 * but not modified.
306 */
307 template <typename BlockT>
308 class bitref final : public bitref_base<BlockT> {
309 public:
310 using bitref_base<BlockT>::bitref_base;
311 };
312
313 /**
314 * Wraps a modifiable reference into the bitvector. Bit may be accessed
315 * and modified (e.g. flipped or XOR'ed).
316 *
317 * Constant-time operations are used for the bit manipulations. The
318 * location of the bit in the bit vector may be leaked, though.
319 */
320 template <typename BlockT>
321 requires(!std::is_const_v<BlockT>)
322 class bitref<BlockT> : public bitref_base<BlockT> {
323 public:
324 using bitref_base<BlockT>::bitref_base;
325
326 ~bitref() = default;
327 bitref(const bitref&) noexcept = default;
328 bitref(bitref&&) noexcept = default;
329
330 constexpr bitref& set() noexcept {
331 this->m_block |= this->m_mask;
332 return *this;
333 }
334
335 constexpr bitref& unset() noexcept {
336 this->m_block &= ~this->m_mask;
337 return *this;
338 }
339
340 constexpr bitref& flip() noexcept {
341 this->m_block ^= this->m_mask;
342 return *this;
343 }
344
345 // NOLINTBEGIN
346
347 constexpr bitref& operator=(bool bit) noexcept {
348 this->m_block =
349 CT::Mask<BlockT>::expand(bit).select(this->m_mask | this->m_block, this->m_block & ~this->m_mask);
350 return *this;
351 }
352
353 constexpr bitref& operator=(CT::Choice bit) noexcept {
354 const auto mask = CT::Mask<BlockT>::from_choice(bit);
355 this->m_block = mask.select(this->m_mask | this->m_block, this->m_block & ~this->m_mask);
356 return *this;
357 }
358
359 constexpr bitref& operator=(const bitref& bit) noexcept { return *this = bit.is_set(); }
360
361 constexpr bitref& operator=(bitref&& bit) noexcept { return *this = bit.is_set(); }
362
363 // NOLINTEND
364
365 constexpr bitref& operator&=(bool other) noexcept {
366 this->m_block &= ~CT::Mask<BlockT>::expand(other).if_not_set_return(this->m_mask);
367 return *this;
368 }
369
370 constexpr bitref& operator&=(CT::Choice other) noexcept {
371 this->m_block &= ~CT::Mask<BlockT>::from_choice(other).if_not_set_return(this->m_mask);
372 return *this;
373 }
374
375 constexpr bitref& operator|=(bool other) noexcept {
376 this->m_block |= CT::Mask<BlockT>::expand(other).if_set_return(this->m_mask);
377 return *this;
378 }
379
380 constexpr bitref& operator|=(CT::Choice other) noexcept {
381 this->m_block |= CT::Mask<BlockT>::from_choice(other).if_set_return(this->m_mask);
382 return *this;
383 }
384
385 constexpr bitref& operator^=(bool other) noexcept {
386 this->m_block ^= CT::Mask<BlockT>::expand(other).if_set_return(this->m_mask);
387 return *this;
388 }
389
390 constexpr bitref& operator^=(CT::Choice other) noexcept {
391 this->m_block ^= CT::Mask<BlockT>::from_choice(other).if_set_return(this->m_mask);
392 return *this;
393 }
394 };
395
396 public:
397 bitvector_base() : m_bits(0) {}
398
399 explicit bitvector_base(size_type bits) : m_bits(bits), m_blocks(ceil_toblocks(bits)) {}
400
401 /**
402 * Initialize the bitvector from a byte-array. Bits are taken byte-wise
403 * from least significant to most significant. Example::
404 *
405 * bitvector[0] -> LSB(Byte[0])
406 * bitvector[1] -> LSB+1(Byte[0])
407 * ...
408 * bitvector[8] -> LSB(Byte[1])
409 *
410 * @param bytes The byte vector to be loaded
411 * @param bits The number of bits to be loaded. This must not be more
412 * than the number of bytes in @p bytes.
413 */
414 bitvector_base(std::span<const uint8_t> bytes, /* NOLINT(*-explicit-conversions) */
415 std::optional<size_type> bits = std::nullopt) :
416 m_bits() {
417 from_bytes(bytes, bits);
418 }
419
420 bitvector_base(std::initializer_list<block_type> blocks, std::optional<size_type> bits = std::nullopt) :
421 m_bits(bits.value_or(blocks.size() * block_size_bits)), m_blocks(blocks.begin(), blocks.end()) {}
422
423 bool empty() const { return m_bits == 0; }
424
425 size_type size() const { return m_bits; }
426
427 /**
428 * @returns true iff the number of 1-bits in this is odd, false otherwise (constant time)
429 */
431 uint64_t acc = 0;
432 full_range_operation([&](std::unsigned_integral auto block) { acc ^= block; }, *this);
433
434 for(size_t i = (sizeof(acc) * 8) >> 1; i > 0; i >>= 1) {
435 acc ^= acc >> i;
436 }
437
438 return CT::Choice::from_int(acc & one);
439 }
440
441 /**
442 * Counts the number of 1-bits in the bitvector in constant time.
443 * @returns the "population count" (or hamming weight) of the bitvector
444 */
446 size_type acc = 0;
447 full_range_operation([&](std::unsigned_integral auto block) { acc += ct_popcount(block); }, *this);
448 return acc;
449 }
450
451 /**
452 * @returns copies this bitvector into a new bitvector of type @p OutT
453 */
454 template <bitvectorish OutT>
455 OutT as() const {
456 return subvector<OutT>(0, size());
457 }
458
459 /**
460 * @returns true if @p other contains the same bit pattern as this
461 */
462 template <bitvectorish OtherT>
463 bool equals_vartime(const OtherT& other) const noexcept {
464 return size() == other.size() &&
465 full_range_operation([]<std::unsigned_integral BlockT>(BlockT lhs, BlockT rhs) { return lhs == rhs; },
466 *this,
467 unwrap_strong_type(other));
468 }
469
470 /**
471 * @returns true if @p other contains the same bit pattern as this
472 */
473 template <bitvectorish OtherT>
474 bool equals(const OtherT& other) const noexcept {
475 if(size() != other.size()) {
476 return false;
477 }
478
479 uint64_t acc = 0;
480 full_range_operation(
481 [&]<std::unsigned_integral BlockT>(BlockT lhs, BlockT rhs) { acc |= static_cast<uint64_t>(lhs ^ rhs); },
482 *this,
483 unwrap_strong_type(other));
484 return !CT::Choice::from_int(acc).as_bool();
485 }
486
487 /// @name Serialization
488 /// @{
489
490 /**
491 * Re-initialize the bitvector with the given bytes. See the respective
492 * constructor for details. This should be used only when trying to save
493 * allocations. Otherwise, use the constructor.
494 *
495 * @param bytes the byte range to load bits from
496 * @param bits (optional) if not all @p bytes should be loaded in full
497 */
498 void from_bytes(std::span<const uint8_t> bytes, std::optional<size_type> bits = std::nullopt) {
499 const size_type new_bits = bits.has_value()
500 ? bits.value()
501 : mul_or_throw<size_t>(8, bytes.size_bytes(), "bitvector input is too large");
502 const size_type bytes_needed = (new_bits / 8) + (new_bits % 8 != 0 ? 1 : 0);
503 BOTAN_ARG_CHECK(bytes_needed <= bytes.size_bytes(), "not enough data to load so many bits");
504 resize(new_bits);
505
506 // load as much aligned data as possible
507 const auto verbatim_blocks = new_bits / block_size_bits;
508 const auto verbatim_bytes = verbatim_blocks * block_size_bytes;
509 if(verbatim_blocks > 0) {
510 typecast_copy(std::span{m_blocks}.first(verbatim_blocks), bytes.first(verbatim_bytes));
511 }
512
513 // load remaining unaligned data
514 for(size_type i = verbatim_bytes * 8; i < new_bits; ++i) {
515 ref(i) = ((bytes[i >> 3] & (uint8_t(1) << (i & 7))) != 0);
516 }
517 }
518
519 /**
520 * Renders the bitvector into a byte array. By default, this will use
521 * `std::vector<uint8_t>` or `Botan::secure_vector<uint8_t>`, depending on
522 * the allocator used by the bitvector. The rendering is compatible with
523 * the bit layout explained in the respective constructor.
524 */
525 template <concepts::resizable_byte_buffer OutT =
526 std::conditional_t<uses_secure_allocator, secure_vector<uint8_t>, std::vector<uint8_t>>>
527 OutT to_bytes() const {
528 OutT out(ceil_tobytes(m_bits));
529 to_bytes(out);
530 return out;
531 }
532
533 /**
534 * Renders the bitvector into a properly sized byte range.
535 *
536 * @param out a byte range that has a length of at least `ceil_tobytes(size())`.
537 */
538 void to_bytes(std::span<uint8_t> out) const {
539 const auto bytes_needed = ceil_tobytes(m_bits);
540 BOTAN_ARG_CHECK(bytes_needed <= out.size_bytes(), "Not enough space to render bitvector");
541
542 // copy as much aligned data as possible
543 const auto verbatim_blocks = m_bits / block_size_bits;
544 const auto verbatim_bytes = verbatim_blocks * block_size_bytes;
545 if(verbatim_blocks > 0) {
546 typecast_copy(out.first(verbatim_bytes), std::span{m_blocks}.first(verbatim_blocks));
547 }
548
549 // copy remaining unaligned data
550 clear_mem(out.subspan(verbatim_bytes));
551 for(size_type i = verbatim_bytes * 8; i < m_bits; ++i) {
552 out[i >> 3] |= ref(i).template as<uint8_t>() << (i & 7);
553 }
554 }
555
556 /**
557 * Renders this bitvector into a sequence of "0"s and "1"s.
558 * This is meant for debugging purposes and is not efficient.
559 */
560 std::string to_string() const {
561 std::stringstream ss;
562 for(size_type i = 0; i < size(); ++i) {
563 ss << ref(i);
564 }
565 return ss.str();
566 }
567
568 /// @}
569
570 /// @name Capacity Accessors and Modifiers
571 /// @{
572
573 size_type capacity() const { return m_blocks.capacity() * block_size_bits; }
574
575 void reserve(size_type bits) { m_blocks.reserve(ceil_toblocks(bits)); }
576
577 void resize(size_type bits) {
578 const auto new_number_of_blocks = ceil_toblocks(bits);
579 const auto old_number_of_blocks = m_blocks.size();
580 if(new_number_of_blocks > old_number_of_blocks) {
581 m_blocks.insert(m_blocks.end(), new_number_of_blocks - old_number_of_blocks, block_type(0));
582 } else if(new_number_of_blocks < old_number_of_blocks) {
583 m_blocks.erase(m_blocks.begin() + new_number_of_blocks, m_blocks.end());
584 }
585
586 m_bits = bits;
587 zero_unused_bits();
588 }
589
590 void push_back(bool bit) {
591 const auto i = size();
592 resize(i + 1);
593 ref(i) = bit;
594 }
595
597 const auto i = size();
598 resize(i + 1);
599 ref(i) = bit;
600 }
601
602 void pop_back() {
603 if(!empty()) {
604 resize(size() - 1);
605 }
606 }
607
608 /// @}
609
610 /// @name Bitwise and Global Accessors and Modifiers
611 /// @{
612
613 auto at(size_type pos) {
614 check_offset(pos);
615 return ref(pos);
616 }
617
618 // TODO C++23: deducing this
619 auto at(size_type pos) const {
620 check_offset(pos);
621 return ref(pos);
622 }
623
624 auto front() { return ref(0); }
625
626 // TODO C++23: deducing this
627 auto front() const { return ref(0); }
628
629 auto back() { return ref(size() - 1); }
630
631 // TODO C++23: deducing this
632 auto back() const { return ref(size() - 1); }
633
634 /**
635 * Sets the bit at position @p pos.
636 * @throws Botan::Invalid_Argument if @p pos is out of range
637 */
639 check_offset(pos);
640 ref(pos).set();
641 return *this;
642 }
643
644 /**
645 * Sets all currently allocated bits.
646 */
648 full_range_operation(
649 [](std::unsigned_integral auto block) -> decltype(block) {
650 return static_cast<decltype(block)>(~static_cast<decltype(block)>(0));
651 },
652 *this);
653 zero_unused_bits();
654 return *this;
655 }
656
657 /**
658 * Unsets the bit at position @p pos.
659 * @throws Botan::Invalid_Argument if @p pos is out of range
660 */
662 check_offset(pos);
663 ref(pos).unset();
664 return *this;
665 }
666
667 /**
668 * Unsets all currently allocated bits.
669 */
671 full_range_operation(
672 [](std::unsigned_integral auto block) -> decltype(block) { return static_cast<decltype(block)>(0); },
673 *this);
674 return *this;
675 }
676
677 /**
678 * Flips the bit at position @p pos.
679 * @throws Botan::Invalid_Argument if @p pos is out of range
680 */
682 check_offset(pos);
683 ref(pos).flip();
684 return *this;
685 }
686
687 /**
688 * Flips all currently allocated bits.
689 */
691 full_range_operation([](std::unsigned_integral auto block) -> decltype(block) { return ~block; }, *this);
692 zero_unused_bits();
693 return *this;
694 }
695
696 /**
697 * @returns true iff no bit is set
698 */
699 bool none_vartime() const {
700 return full_range_operation([](std::unsigned_integral auto block) { return block == 0; }, *this);
701 }
702
703 /**
704 * @returns true iff no bit is set in constant time
705 */
706 bool none() const { return hamming_weight() == 0; }
707
708 /**
709 * @returns true iff at least one bit is set
710 */
711 bool any_vartime() const { return !none_vartime(); }
712
713 /**
714 * @returns true iff at least one bit is set in constant time
715 */
716 bool any() const { return !none(); }
717
718 /**
719 * @returns true iff all bits are set
720 */
721 bool all_vartime() const {
722 return full_range_operation(
723 []<std::unsigned_integral BlockT>(BlockT block, BlockT mask) { return block == mask; }, *this);
724 }
725
726 /**
727 * @returns true iff all bits are set in constant time
728 */
729 bool all() const { return hamming_weight() == m_bits; }
730
731 auto operator[](size_type pos) { return ref(pos); }
732
733 // TODO C++23: deducing this
734 auto operator[](size_type pos) const { return ref(pos); }
735
736 /// @}
737
738 /// @name Subvectors
739 /// @{
740
741 /**
742 * Creates a new bitvector with a subsection of this bitvector starting at
743 * @p pos copying exactly @p length bits.
744 */
745 template <bitvectorish OutT = bitvector_base<AllocatorT>>
746 auto subvector(size_type pos, std::optional<size_type> length = std::nullopt) const {
747 const size_type bitlen = length.value_or(size() - pos);
748 BOTAN_ARG_CHECK(pos + bitlen <= size(), "Not enough bits to copy");
749
750 OutT newvector(bitlen);
751
752 // Handle bitvectors that are wrapped in strong types
753 auto& newvector_unwrapped = unwrap_strong_type(newvector);
754
755 if(bitlen > 0) {
756 if(pos % 8 == 0) {
757 copy_mem(
758 newvector_unwrapped.m_blocks,
759 std::span{m_blocks}.subspan(block_index(pos), block_index(pos + bitlen - 1) - block_index(pos) + 1));
760 } else {
761 const BitRangeOperator<const bitvector_base<AllocatorT>, BitRangeAlignment::no_alignment> from_op(
762 *this, pos, bitlen);
763 const BitRangeOperator<strong_type_wrapped_type<OutT>> to_op(
764 unwrap_strong_type(newvector_unwrapped), 0, bitlen);
765 range_operation([](auto /* to */, auto from) { return from; }, to_op, from_op);
766 }
767
768 newvector_unwrapped.zero_unused_bits();
769 }
770
771 return newvector;
772 }
773
774 /**
775 * Extracts a subvector of bits as an unsigned integral type @p OutT
776 * starting from bit @p pos and copying exactly sizeof(OutT)*8 bits.
777 *
778 * Hint: The bits are in big-endian order, i.e. the least significant bit
779 * is the 0th bit and the most significant bit it the n-th. Hence,
780 * addressing the bits with bitwise operations is done like so:
781 * bool bit = (out_int >> pos) & 1;
782 */
783 template <typename OutT>
784 requires(std::unsigned_integral<strong_type_wrapped_type<OutT>> &&
785 !std::same_as<bool, strong_type_wrapped_type<OutT>>)
786 OutT subvector(size_type pos) const {
787 using result_t = strong_type_wrapped_type<OutT>;
788 constexpr size_t bits = sizeof(result_t) * 8;
789 BOTAN_ARG_CHECK(pos + bits <= size(), "Not enough bits to copy");
790 result_t out = 0;
791
792 if(pos % 8 == 0) {
793 out = load_le<result_t>(std::span{m_blocks}.subspan(block_index(pos)).template first<sizeof(result_t)>());
794 } else {
795 const BitRangeOperator<const bitvector_base<AllocatorT>, BitRangeAlignment::no_alignment> op(
796 *this, pos, bits);
797 range_operation(
798 [&](std::unsigned_integral auto integer) {
799 if constexpr(std::same_as<result_t, decltype(integer)>) {
800 out = integer;
801 }
802 },
803 op);
804 }
805
806 return wrap_strong_type<OutT>(out);
807 }
808
809 /**
810 * Replaces a subvector of bits with the bits of another bitvector @p value
811 * starting at bit @p pos. The number of bits to replace is determined by
812 * the size of @p value.
813 *
814 * @note This is currently supported for byte-aligned @p pos only.
815 *
816 * @throws Not_Implemented when called with @p pos not divisible by 8.
817 *
818 * @param pos the position to start replacing bits
819 * @param value the bitvector to copy bits from
820 */
821 template <typename InT>
822 requires(std::unsigned_integral<strong_type_wrapped_type<InT>> && !std::same_as<bool, InT>)
823 void subvector_replace(size_type pos, InT value) {
825 constexpr size_t bits = sizeof(in_t) * 8;
826 BOTAN_ARG_CHECK(pos + bits <= size(), "Not enough bits to replace");
827
828 if(pos % 8 == 0) {
829 store_le(std::span{m_blocks}.subspan(block_index(pos)).template first<sizeof(in_t)>(),
830 unwrap_strong_type(value));
831 } else {
832 const BitRangeOperator<bitvector_base<AllocatorT>, BitRangeAlignment::no_alignment> op(*this, pos, bits);
833 range_operation(
834 [&]<std::unsigned_integral BlockT>(BlockT block) -> BlockT {
835 if constexpr(std::same_as<in_t, BlockT>) {
836 return unwrap_strong_type(value);
837 } else {
838 // This should never be reached. BOTAN_ASSERT_UNREACHABLE()
839 // caused warning "unreachable code" on MSVC, though. You
840 // don't say!
841 //
842 // Returning the given block back, is the most reasonable
843 // thing to do in this case, though.
844 return block;
845 }
846 },
847 op);
848 }
849 }
850
851 /// @}
852
853 /// @name Operators
854 ///
855 /// @{
856
857 auto operator~() {
858 auto newbv = *this;
859 newbv.flip();
860 return newbv;
861 }
862
863 template <bitvectorish OtherT>
864 auto& operator|=(const OtherT& other) {
865 full_range_operation([]<std::unsigned_integral BlockT>(BlockT lhs, BlockT rhs) -> BlockT { return lhs | rhs; },
866 *this,
867 unwrap_strong_type(other));
868 return *this;
869 }
870
871 template <bitvectorish OtherT>
872 auto& operator&=(const OtherT& other) {
873 full_range_operation([]<std::unsigned_integral BlockT>(BlockT lhs, BlockT rhs) -> BlockT { return lhs & rhs; },
874 *this,
875 unwrap_strong_type(other));
876 return *this;
877 }
878
879 template <bitvectorish OtherT>
880 auto& operator^=(const OtherT& other) {
881 full_range_operation([]<std::unsigned_integral BlockT>(BlockT lhs, BlockT rhs) -> BlockT { return lhs ^ rhs; },
882 *this,
883 unwrap_strong_type(other));
884 return *this;
885 }
886
887 /// @}
888
889 /// @name Constant Time Operations
890 ///
891 /// @{
892
893 /**
894 * Implements::
895 *
896 * if(condition) {
897 * *this ^= other;
898 * }
899 *
900 * omitting runtime dependence on any of the parameters.
901 */
902 template <bitvectorish OtherT>
903 void ct_conditional_xor(CT::Choice condition, const OtherT& other) {
904 BOTAN_ASSERT_NOMSG(m_bits == other.m_bits);
905 BOTAN_ASSERT_NOMSG(m_blocks.size() == other.m_blocks.size());
906
907 auto maybe_xor = overloaded{
908 [m = CT::Mask<uint64_t>::from_choice(condition)](uint64_t lhs, uint64_t rhs) -> uint64_t {
909 return lhs ^ m.if_set_return(rhs);
910 },
911 [m = CT::Mask<uint32_t>::from_choice(condition)](uint32_t lhs, uint32_t rhs) -> uint32_t {
912 return lhs ^ m.if_set_return(rhs);
913 },
914 [m = CT::Mask<uint16_t>::from_choice(condition)](uint16_t lhs, uint16_t rhs) -> uint16_t {
915 return lhs ^ m.if_set_return(rhs);
916 },
917 [m = CT::Mask<uint8_t>::from_choice(condition)](uint8_t lhs, uint8_t rhs) -> uint8_t {
918 return lhs ^ m.if_set_return(rhs);
919 },
920 };
921
922 full_range_operation(maybe_xor, *this, unwrap_strong_type(other));
923 }
924
925 constexpr void _const_time_poison() const { CT::poison(m_blocks); }
926
927 constexpr void _const_time_unpoison() const { CT::unpoison(m_blocks); }
928
929 /// @}
930
931 /// @name Iterators
932 ///
933 /// @{
934
935 iterator begin() noexcept { return iterator(this, 0); }
936
937 const_iterator begin() const noexcept { return const_iterator(this, 0); }
938
939 const_iterator cbegin() const noexcept { return const_iterator(this, 0); }
940
941 iterator end() noexcept { return iterator(this, size()); }
942
943 const_iterator end() const noexcept { return const_iterator(this, size()); }
944
945 const_iterator cend() noexcept { return const_iterator(this, size()); }
946
947 /// @}
948
949 private:
950 void check_offset(size_type pos) const {
951 // BOTAN_ASSERT_NOMSG(!CT::is_poisoned(&m_bits, sizeof(m_bits)));
952 // BOTAN_ASSERT_NOMSG(!CT::is_poisoned(&pos, sizeof(pos)));
953 BOTAN_ARG_CHECK(pos < m_bits, "Out of range");
954 }
955
956 void zero_unused_bits() {
957 const auto first_unused_bit = size();
958
959 // Zero out any unused bits in the last block
960 if(first_unused_bit % block_size_bits != 0) {
961 const block_type mask = (one << block_offset(first_unused_bit)) - one;
962 m_blocks[block_index(first_unused_bit)] &= mask;
963 }
964 }
965
966 static constexpr size_type ceil_toblocks(size_type bits) {
967 return add_or_throw(bits, block_size_bits - 1, "bitvector size is too large") / block_size_bits;
968 }
969
970 auto ref(size_type pos) const { return bitref<const block_type>(m_blocks, pos); }
971
972 auto ref(size_type pos) { return bitref<block_type>(m_blocks, pos); }
973
974 private:
975 enum class BitRangeAlignment : uint8_t { byte_aligned, no_alignment };
976
977 /**
978 * Helper construction to implement bit range operations on the bitvector.
979 * It basically implements an iterator to read and write blocks of bits
980 * from the underlying bitvector. Where "blocks of bits" are unsigned
981 * integers of varying bit lengths.
982 *
983 * If the iteration starts at a byte boundary in the underlying bitvector,
984 * this applies certain optimizations (i.e. loading blocks of bits straight
985 * from the underlying byte buffer). The optimizations are enabled at
986 * compile time (with the template parameter `alignment`).
987 */
988 template <typename BitvectorT, auto alignment = BitRangeAlignment::byte_aligned>
989 requires is_bitvector_v<std::remove_cvref_t<BitvectorT>>
990 class BitRangeOperator {
991 private:
992 constexpr static bool is_const() { return std::is_const_v<BitvectorT>; }
993
994 struct UnalignedDataHelper {
995 const uint8_t padding_bits;
996 const uint8_t bits_to_byte_alignment;
997 };
998
999 public:
1000 BitRangeOperator(BitvectorT& source, size_type start_bitoffset, size_type bitlength) :
1001 m_source(source),
1002 m_start_bitoffset(start_bitoffset),
1003 m_bitlength(bitlength),
1004 m_unaligned_helper({.padding_bits = static_cast<uint8_t>(start_bitoffset % 8),
1005 .bits_to_byte_alignment = static_cast<uint8_t>(8 - (start_bitoffset % 8))}),
1006 m_read_bitpos(start_bitoffset),
1007 m_write_bitpos(start_bitoffset) {
1008 BOTAN_ASSERT(is_byte_aligned() == (m_start_bitoffset % 8 == 0), "byte alignment guarantee");
1009 BOTAN_ASSERT(m_source.size() >= m_start_bitoffset + m_bitlength, "enough bytes in underlying source");
1010 }
1011
1012 explicit BitRangeOperator(BitvectorT& source) : BitRangeOperator(source, 0, source.size()) {}
1013
1014 static constexpr bool is_byte_aligned() { return alignment == BitRangeAlignment::byte_aligned; }
1015
1016 /**
1017 * @returns the overall number of bits to be iterated with this operator
1018 */
1019 size_type size() const { return m_bitlength; }
1020
1021 /**
1022 * @returns the number of bits not yet read from this operator
1023 */
1024 size_type bits_to_read() const { return m_bitlength - m_read_bitpos + m_start_bitoffset; }
1025
1026 /**
1027 * @returns the number of bits still to be written into this operator
1028 */
1029 size_type bits_to_write() const { return m_bitlength - m_write_bitpos + m_start_bitoffset; }
1030
1031 /**
1032 * Loads the next block of bits from the underlying bitvector. No
1033 * bounds checks are performed. The caller can define the size of
1034 * the resulting unsigned integer block.
1035 */
1036 template <std::unsigned_integral BlockT>
1037 BlockT load_next() const {
1038 constexpr size_type block_size = sizeof(BlockT);
1039 constexpr size_type block_bits = block_size * 8;
1040 const auto bits_remaining = bits_to_read();
1041
1042 BlockT result_block = 0;
1043 if constexpr(is_byte_aligned()) {
1044 result_block = load_le(m_source.as_byte_span().subspan(read_bytepos()).template first<block_size>());
1045 } else {
1046 const size_type byte_pos = read_bytepos();
1047 const size_type bits_to_collect = std::min(block_bits, bits_to_read());
1048
1049 const uint8_t first_byte = m_source.as_byte_span()[byte_pos];
1050
1051 // Initialize the left-most bits from the first byte.
1052 result_block = BlockT(first_byte) >> m_unaligned_helper.padding_bits;
1053
1054 // If more bits are needed, we pull them from the remaining bytes.
1055 if(m_unaligned_helper.bits_to_byte_alignment < bits_to_collect) {
1056 const BlockT block =
1057 load_le(m_source.as_byte_span().subspan(byte_pos + 1).template first<block_size>());
1058 result_block |= block << m_unaligned_helper.bits_to_byte_alignment;
1059 }
1060 }
1061
1062 m_read_bitpos += std::min(block_bits, bits_remaining);
1063 return result_block;
1064 }
1065
1066 /**
1067 * Stores the next block of bits into the underlying bitvector.
1068 * No bounds checks are performed. Storing bit blocks that are not
1069 * aligned at a byte-boundary in the underlying bitvector is
1070 * currently not implemented.
1071 */
1072 template <std::unsigned_integral BlockT>
1073 requires(!is_const())
1074 void store_next(BlockT block) {
1075 constexpr size_type block_size = sizeof(BlockT);
1076 constexpr size_type block_bits = block_size * 8;
1077
1078 if constexpr(is_byte_aligned()) {
1079 auto sink = m_source.as_byte_span().subspan(write_bytepos()).template first<block_size>();
1080 store_le(sink, block);
1081 } else {
1082 const size_type byte_pos = write_bytepos();
1083 const size_type bits_to_store = std::min(block_bits, bits_to_write());
1084
1085 uint8_t& first_byte = m_source.as_byte_span()[byte_pos];
1086
1087 // Set the left-most bits in the first byte, leaving all others unchanged
1088 first_byte = (first_byte & uint8_t(0xFF >> m_unaligned_helper.bits_to_byte_alignment)) |
1089 uint8_t(block << m_unaligned_helper.padding_bits);
1090
1091 // If more bits are provided, we store them in the remaining bytes.
1092 if(m_unaligned_helper.bits_to_byte_alignment < bits_to_store) {
1093 const auto remaining_bytes =
1094 m_source.as_byte_span().subspan(byte_pos + 1).template first<block_size>();
1095 const BlockT padding_mask = ~(BlockT(-1) >> m_unaligned_helper.bits_to_byte_alignment);
1096 const BlockT new_bytes =
1097 (load_le(remaining_bytes) & padding_mask) | block >> m_unaligned_helper.bits_to_byte_alignment;
1098 store_le(remaining_bytes, new_bytes);
1099 }
1100 }
1101
1102 m_write_bitpos += std::min(block_bits, bits_to_write());
1103 }
1104
1105 template <std::unsigned_integral BlockT>
1106 requires(is_byte_aligned() && !is_const())
1107 std::span<BlockT> span(size_type blocks) const {
1108 BOTAN_DEBUG_ASSERT(blocks == 0 || is_memory_aligned_to<BlockT>());
1109 BOTAN_DEBUG_ASSERT(read_bytepos() % sizeof(BlockT) == 0);
1110 // Intermittently casting to void* to avoid a compiler warning
1111 void* ptr = reinterpret_cast<void*>(m_source.as_byte_span().data() + read_bytepos());
1112 return {reinterpret_cast<BlockT*>(ptr), blocks};
1113 }
1114
1115 template <std::unsigned_integral BlockT>
1116 requires(is_byte_aligned() && is_const())
1117 std::span<const BlockT> span(size_type blocks) const {
1118 BOTAN_DEBUG_ASSERT(blocks == 0 || is_memory_aligned_to<BlockT>());
1119 BOTAN_DEBUG_ASSERT(read_bytepos() % sizeof(BlockT) == 0);
1120 // Intermittently casting to void* to avoid a compiler warning
1121 const void* ptr = reinterpret_cast<const void*>(m_source.as_byte_span().data() + read_bytepos());
1122 return {reinterpret_cast<const BlockT*>(ptr), blocks};
1123 }
1124
1125 void advance(size_type bytes)
1126 requires(is_byte_aligned())
1127 {
1128 m_read_bitpos += bytes * 8;
1129 m_write_bitpos += bytes * 8;
1130 }
1131
1132 template <std::unsigned_integral BlockT>
1133 requires(is_byte_aligned())
1134 size_t is_memory_aligned_to() const {
1135 const void* cptr = m_source.as_byte_span().data() + read_bytepos();
1136 const void* ptr_before = cptr;
1137
1138 // std::align takes `ptr` as a reference (!), i.e. `void*&` and
1139 // uses it as an out-param. Though, `cptr` is const because this
1140 // method is const-qualified, hence the const_cast<>.
1141 void* ptr = const_cast<void*>(cptr); // NOLINT(*-const-correctness)
1142 size_t size = sizeof(BlockT);
1143 return ptr_before != nullptr && std::align(alignof(BlockT), size, ptr, size) == ptr_before;
1144 }
1145
1146 private:
1147 size_type read_bytepos() const { return m_read_bitpos / 8; }
1148
1149 size_type write_bytepos() const { return m_write_bitpos / 8; }
1150
1151 private:
1152 BitvectorT& m_source;
1153 size_type m_start_bitoffset;
1154 size_type m_bitlength;
1155
1156 UnalignedDataHelper m_unaligned_helper;
1157
1158 mutable size_type m_read_bitpos;
1159 mutable size_type m_write_bitpos;
1160 };
1161
1162 /**
1163 * Helper struct for the low-level handling of blockwise operations
1164 *
1165 * This has two main code paths: Optimized for byte-aligned ranges that
1166 * can simply be taken from memory as-is. And a generic implementation
1167 * that must assemble blocks from unaligned bits before processing.
1168 */
1169 template <typename FnT, typename... ParamTs>
1170 requires detail::blockwise_processing_callback<FnT, ParamTs...>
1171 class blockwise_processing_callback_trait {
1172 public:
1173 constexpr static bool needs_mask = detail::blockwise_processing_callback_with_mask<FnT, ParamTs...>;
1174 constexpr static bool is_manipulator = detail::manipulating_blockwise_processing_callback<FnT, ParamTs...>;
1175 constexpr static bool is_predicate = detail::predicate_blockwise_processing_callback<FnT, ParamTs...>;
1176 static_assert(!is_manipulator || !is_predicate, "cannot be manipulator and predicate at the same time");
1177
1178 /**
1179 * Applies @p fn to the blocks provided in @p blocks by simply reading from
1180 * memory without re-arranging any bits across byte-boundaries.
1181 */
1182 template <std::unsigned_integral... BlockTs>
1183 requires(all_same_v<std::remove_cv_t<BlockTs>...> && sizeof...(BlockTs) == sizeof...(ParamTs))
1184 constexpr static bool apply_on_full_blocks(FnT fn, std::span<BlockTs>... blocks) {
1185 constexpr size_type bits = sizeof(detail::first_t<BlockTs...>) * 8;
1186 const size_type iterations = detail::first(blocks...).size();
1187 for(size_type i = 0; i < iterations; ++i) {
1188 if constexpr(is_predicate) {
1189 if(!apply(fn, bits, blocks[i]...)) {
1190 return false;
1191 }
1192 } else if constexpr(is_manipulator) {
1193 detail::first(blocks...)[i] = apply(fn, bits, blocks[i]...);
1194 } else {
1195 apply(fn, bits, blocks[i]...);
1196 }
1197 }
1198 return true;
1199 }
1200
1201 /**
1202 * Applies @p fn to as many blocks as @p ops provide for the given type.
1203 */
1204 template <std::unsigned_integral BlockT, typename... BitRangeOperatorTs>
1205 requires(sizeof...(BitRangeOperatorTs) == sizeof...(ParamTs))
1206 constexpr static bool apply_on_unaligned_blocks(FnT fn, BitRangeOperatorTs&... ops) {
1207 constexpr size_type block_bits = sizeof(BlockT) * 8;
1208 auto bits = detail::first(ops...).bits_to_read();
1209 if(bits == 0) {
1210 return true;
1211 }
1212
1213 bits += block_bits; // avoid unsigned integer underflow in the following loop
1214 while(bits > block_bits * 2 - 8) {
1215 bits -= block_bits;
1216 if constexpr(is_predicate) {
1217 if(!apply(fn, bits, ops.template load_next<BlockT>()...)) {
1218 return false;
1219 }
1220 } else if constexpr(is_manipulator) {
1221 detail::first(ops...).store_next(apply(fn, bits, ops.template load_next<BlockT>()...));
1222 } else {
1223 apply(fn, bits, ops.template load_next<BlockT>()...);
1224 }
1225 }
1226 return true;
1227 }
1228
1229 private:
1230 template <std::unsigned_integral... BlockTs>
1231 requires(all_same_v<std::remove_cv_t<BlockTs>...>)
1232 constexpr static auto apply(FnT fn, size_type bits, BlockTs... blocks) {
1233 if constexpr(needs_mask) {
1234 return fn(blocks..., make_mask<detail::first_t<BlockTs...>>(bits));
1235 } else {
1236 return fn(blocks...);
1237 }
1238 }
1239 };
1240
1241 /**
1242 * Helper function of `full_range_operation` and `range_operation` that
1243 * calls @p fn on a given aligned unsigned integer block as long as the
1244 * underlying bit range contains enough bits to fill the block fully.
1245 *
1246 * This uses bare memory access to gain a speed up for aligned data.
1247 */
1248 template <std::unsigned_integral BlockT, typename FnT, typename... BitRangeOperatorTs>
1249 requires(detail::blockwise_processing_callback<FnT, BitRangeOperatorTs...> &&
1250 sizeof...(BitRangeOperatorTs) > 0)
1251 static bool _process_in_fully_aligned_blocks_of(FnT fn, BitRangeOperatorTs&... ops) {
1252 constexpr size_type block_bytes = sizeof(BlockT);
1253 constexpr size_type block_bits = block_bytes * 8;
1254 const size_type blocks = detail::first(ops...).bits_to_read() / block_bits;
1255
1256 using callback_trait = blockwise_processing_callback_trait<FnT, BitRangeOperatorTs...>;
1257 const auto result = callback_trait::apply_on_full_blocks(fn, ops.template span<BlockT>(blocks)...);
1258 (ops.advance(block_bytes * blocks), ...);
1259 return result;
1260 }
1261
1262 /**
1263 * Helper function of `full_range_operation` and `range_operation` that
1264 * calls @p fn on a given unsigned integer block size as long as the
1265 * underlying bit range contains enough bits to fill the block.
1266 */
1267 template <std::unsigned_integral BlockT, typename FnT, typename... BitRangeOperatorTs>
1268 requires(detail::blockwise_processing_callback<FnT, BitRangeOperatorTs...>)
1269 static bool _process_in_unaligned_blocks_of(FnT fn, BitRangeOperatorTs&... ops) {
1270 using callback_trait = blockwise_processing_callback_trait<FnT, BitRangeOperatorTs...>;
1271 return callback_trait::template apply_on_unaligned_blocks<BlockT>(fn, ops...);
1272 }
1273
1274 /**
1275 * Apply @p fn to all bits in the ranges defined by @p ops. If more than
1276 * one range operator is passed to @p ops, @p fn receives corresponding
1277 * blocks of bits from each operator. Therefore, all @p ops have to define
1278 * the exact same length of their underlying ranges.
1279 *
1280 * @p fn may return a bit block that will be stored into the _first_ bit
1281 * range passed into @p ops. If @p fn returns a boolean, and its value is
1282 * `false`, the range operation is cancelled and `false` is returned.
1283 *
1284 * The implementation ensures to pull bits in the largest bit blocks
1285 * possible and reverts to smaller bit blocks only when needed.
1286 */
1287 template <typename FnT, typename... BitRangeOperatorTs>
1288 requires(detail::blockwise_processing_callback<FnT, BitRangeOperatorTs...> &&
1289 sizeof...(BitRangeOperatorTs) > 0)
1290 static bool range_operation(FnT fn, BitRangeOperatorTs... ops) {
1291 BOTAN_ASSERT(has_equal_lengths(ops...), "all BitRangeOperators have the same length");
1292
1293 if constexpr((BitRangeOperatorTs::is_byte_aligned() && ...)) {
1294 // Note: At the moment we can assume that this will always be used
1295 // on the _entire_ bitvector. Therefore, we can safely assume
1296 // that the bitvectors' underlying buffers are properly aligned.
1297 // If this assumption changes, we need to add further handling
1298 // to process a byte padding at the beginning of the bitvector
1299 // until a memory alignment boundary is reached.
1300 //
1301 // An empty range has no blocks to process and a possibly-null
1302 // underlying buffer, so the alignment check does not apply.
1303 if(detail::first(ops...).size() != 0) {
1304 const bool alignment = (ops.template is_memory_aligned_to<uint64_t>() && ...);
1305 BOTAN_ASSERT_NOMSG(alignment);
1306 }
1307
1308 return _process_in_fully_aligned_blocks_of<uint64_t>(fn, ops...) &&
1309 _process_in_fully_aligned_blocks_of<uint32_t>(fn, ops...) &&
1310 _process_in_fully_aligned_blocks_of<uint16_t>(fn, ops...) &&
1311 _process_in_unaligned_blocks_of<uint8_t>(fn, ops...);
1312 } else {
1313 return _process_in_unaligned_blocks_of<uint64_t>(fn, ops...) &&
1314 _process_in_unaligned_blocks_of<uint32_t>(fn, ops...) &&
1315 _process_in_unaligned_blocks_of<uint16_t>(fn, ops...) &&
1316 _process_in_unaligned_blocks_of<uint8_t>(fn, ops...);
1317 }
1318 }
1319
1320 /**
1321 * Apply @p fn to all bit blocks in the bitvector(s).
1322 */
1323 template <typename FnT, typename... BitvectorTs>
1324 requires(detail::blockwise_processing_callback<FnT, BitvectorTs...> &&
1325 (is_bitvector_v<std::remove_cvref_t<BitvectorTs>> && ... && true))
1326 static bool full_range_operation(FnT&& fn, BitvectorTs&... bitvecs) {
1327 BOTAN_ASSERT(has_equal_lengths(bitvecs...), "all bitvectors have the same length");
1328 return range_operation(std::forward<FnT>(fn), BitRangeOperator<BitvectorTs>(bitvecs)...);
1329 }
1330
1331 template <typename SomeT, typename... SomeTs>
1332 static bool has_equal_lengths(const SomeT& v, const SomeTs&... vs) {
1333 return ((v.size() == vs.size()) && ... && true);
1334 }
1335
1336 template <std::unsigned_integral T>
1337 static constexpr T make_mask(size_type bits) {
1338 const bool max = bits >= sizeof(T) * 8;
1339 bits &= T(max - 1);
1340 return (T(!max) << bits) - 1;
1341 }
1342
1343 auto as_byte_span() { return std::span{m_blocks.data(), m_blocks.size() * sizeof(block_type)}; }
1344
1345 auto as_byte_span() const { return std::span{m_blocks.data(), m_blocks.size() * sizeof(block_type)}; }
1346
1347 private:
1348 size_type m_bits;
1349 std::vector<block_type, allocator_type> m_blocks;
1350};
1351
1354
1355namespace detail {
1356
1357/**
1358 * If one of the allocators is a Botan::secure_allocator, this will always
1359 * prefer it. Otherwise, the allocator of @p lhs will be used as a default.
1360 */
1361template <bitvectorish T1, bitvectorish T2>
1362constexpr auto copy_lhs_allocator_aware(const T1& lhs, const T2& /*rhs*/) {
1363 constexpr bool needs_secure_allocator =
1365
1366 if constexpr(needs_secure_allocator) {
1367 return lhs.template as<secure_bitvector>();
1368 } else {
1369 return lhs.template as<bitvector>();
1370 }
1371}
1372
1373} // namespace detail
1374
1375template <bitvectorish T1, bitvectorish T2>
1376auto operator|(const T1& lhs, const T2& rhs) {
1377 auto res = detail::copy_lhs_allocator_aware(lhs, rhs);
1378 res |= rhs;
1379 return res;
1380}
1381
1382template <bitvectorish T1, bitvectorish T2>
1383auto operator&(const T1& lhs, const T2& rhs) {
1384 auto res = detail::copy_lhs_allocator_aware(lhs, rhs);
1385 res &= rhs;
1386 return res;
1387}
1388
1389template <bitvectorish T1, bitvectorish T2>
1390auto operator^(const T1& lhs, const T2& rhs) {
1391 auto res = detail::copy_lhs_allocator_aware(lhs, rhs);
1392 res ^= rhs;
1393 return res;
1394}
1395
1396template <bitvectorish T1, bitvectorish T2>
1397bool operator==(const T1& lhs, const T2& rhs) {
1398 return lhs.equals_vartime(rhs);
1399}
1400
1401namespace detail {
1402
1403/**
1404 * A Strong<> adapter for arbitrarily large bitvectors
1405 */
1406template <concepts::container T>
1407 requires is_bitvector_v<T>
1409 public:
1410 using size_type = typename T::size_type;
1411
1412 public:
1414
1415 auto at(size_type i) const { return this->get().at(i); }
1416
1417 auto at(size_type i) { return this->get().at(i); }
1418
1419 auto set(size_type i) { return this->get().set(i); }
1420
1421 auto unset(size_type i) { return this->get().unset(i); }
1422
1423 auto flip(size_type i) { return this->get().flip(i); }
1424
1425 auto flip() { return this->get().flip(); }
1426
1427 template <typename OutT>
1428 auto as() const {
1429 return this->get().template as<OutT>();
1430 }
1431
1432 template <bitvectorish OutT = T>
1433 auto subvector(size_type pos, std::optional<size_type> length = std::nullopt) const {
1434 return this->get().template subvector<OutT>(pos, length);
1435 }
1436
1437 template <typename OutT>
1438 requires(std::unsigned_integral<strong_type_wrapped_type<OutT>> &&
1439 !std::same_as<bool, strong_type_wrapped_type<OutT>>)
1440 auto subvector(size_type pos) const {
1441 return this->get().template subvector<OutT>(pos);
1442 }
1443
1444 template <typename InT>
1445 requires(std::unsigned_integral<strong_type_wrapped_type<InT>> && !std::same_as<bool, InT>)
1446 void subvector_replace(size_type pos, InT value) {
1447 return this->get().subvector_replace(pos, value);
1448 }
1449
1450 template <bitvectorish OtherT>
1451 auto equals(const OtherT& other) const {
1452 return this->get().equals(other);
1453 }
1454
1455 auto push_back(bool b) { return this->get().push_back(b); }
1456
1457 auto push_back(CT::Choice b) { return this->get().push_back(b); }
1458
1459 auto pop_back() { return this->get().pop_back(); }
1460
1461 auto front() const { return this->get().front(); }
1462
1463 auto front() { return this->get().front(); }
1464
1465 auto back() const { return this->get().back(); }
1466
1467 auto back() { return this->get().back(); }
1468
1469 auto any_vartime() const { return this->get().any_vartime(); }
1470
1471 auto all_vartime() const { return this->get().all_vartime(); }
1472
1473 auto none_vartime() const { return this->get().none_vartime(); }
1474
1475 auto has_odd_hamming_weight() const { return this->get().has_odd_hamming_weight(); }
1476
1477 auto hamming_weight() const { return this->get().hamming_weight(); }
1478
1479 auto from_bytes(std::span<const uint8_t> bytes, std::optional<size_type> bits = std::nullopt) {
1480 return this->get().from_bytes(bytes, bits);
1481 }
1482
1483 template <typename OutT = T>
1484 auto to_bytes() const {
1485 return this->get().template to_bytes<OutT>();
1486 }
1487
1488 auto to_bytes(std::span<uint8_t> out) const { return this->get().to_bytes(out); }
1489
1490 auto to_string() const { return this->get().to_string(); }
1491
1492 auto capacity() const { return this->get().capacity(); }
1493
1494 auto reserve(size_type n) { return this->get().reserve(n); }
1495
1496 constexpr void _const_time_poison() const { this->get()._const_time_poison(); }
1497
1498 constexpr void _const_time_unpoison() const { this->get()._const_time_unpoison(); }
1499};
1500
1501} // namespace detail
1502
1503} // namespace Botan
1504
1505#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
static constexpr Choice from_int(T v)
Definition ct_utils.h:268
constexpr bool as_bool() const
Definition ct_utils.h:329
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
static constexpr Mask< T > from_choice(Choice c)
Definition ct_utils.h:402
bitref(bitref &&) noexcept=default
constexpr bitref & operator&=(bool other) noexcept
Definition bitvector.h:365
constexpr bitref & operator=(bitref &&bit) noexcept
Definition bitvector.h:361
constexpr bitref & flip() noexcept
Definition bitvector.h:340
constexpr bitref & operator=(bool bit) noexcept
Definition bitvector.h:347
constexpr bitref & operator=(const bitref &bit) noexcept
Definition bitvector.h:359
constexpr bitref & operator&=(CT::Choice other) noexcept
Definition bitvector.h:370
constexpr bitref & operator^=(bool other) noexcept
Definition bitvector.h:385
constexpr bitref & unset() noexcept
Definition bitvector.h:335
constexpr bitref & operator=(CT::Choice bit) noexcept
Definition bitvector.h:353
bitref(const bitref &) noexcept=default
constexpr bitref & operator|=(CT::Choice other) noexcept
Definition bitvector.h:380
constexpr bitref & set() noexcept
Definition bitvector.h:330
constexpr bitref & operator|=(bool other) noexcept
Definition bitvector.h:375
constexpr bitref & operator^=(CT::Choice other) noexcept
Definition bitvector.h:390
const_iterator begin() const noexcept
Definition bitvector.h:937
bitvector_base & flip()
Definition bitvector.h:690
bitvector_base & flip(size_type pos)
Definition bitvector.h:681
void push_back(CT::Choice bit)
Definition bitvector.h:596
bitvector_base & unset()
Definition bitvector.h:670
void subvector_replace(size_type pos, InT value)
Definition bitvector.h:823
void reserve(size_type bits)
Definition bitvector.h:575
OutT subvector(size_type pos) const
Definition bitvector.h:786
bitvector_base & unset(size_type pos)
Definition bitvector.h:661
bool none() const
Definition bitvector.h:706
auto at(size_type pos) const
Definition bitvector.h:619
auto operator[](size_type pos) const
Definition bitvector.h:734
CT::Choice has_odd_hamming_weight() const
Definition bitvector.h:430
constexpr void _const_time_unpoison() const
Definition bitvector.h:927
std::string to_string() const
Definition bitvector.h:560
bitvector_base & set(size_type pos)
Definition bitvector.h:638
static constexpr size_type block_size_bytes
Definition bitvector.h:242
void from_bytes(std::span< const uint8_t > bytes, std::optional< size_type > bits=std::nullopt)
Definition bitvector.h:498
bitvector_base(size_type bits)
Definition bitvector.h:399
size_type hamming_weight() const
Definition bitvector.h:445
auto & operator&=(const OtherT &other)
Definition bitvector.h:872
block_type value_type
Definition bitvector.h:238
const_iterator cbegin() const noexcept
Definition bitvector.h:939
bitvector_base & set()
Definition bitvector.h:647
static constexpr bool uses_secure_allocator
Definition bitvector.h:244
detail::bitvector_iterator< bitvector_base< AllocatorT > > iterator
Definition bitvector.h:239
void ct_conditional_xor(CT::Choice condition, const OtherT &other)
Definition bitvector.h:903
bool none_vartime() const
Definition bitvector.h:699
AllocatorT< block_type > allocator_type
Definition bitvector.h:237
auto operator[](size_type pos)
Definition bitvector.h:731
bool all_vartime() const
Definition bitvector.h:721
void to_bytes(std::span< uint8_t > out) const
Definition bitvector.h:538
size_type capacity() const
Definition bitvector.h:573
friend class bitvector_base
Definition bitvector.h:248
auto & operator|=(const OtherT &other)
Definition bitvector.h:864
auto subvector(size_type pos, std::optional< size_type > length=std::nullopt) const
Definition bitvector.h:746
auto at(size_type pos)
Definition bitvector.h:613
bool empty() const
Definition bitvector.h:423
static constexpr size_type block_size_bits
Definition bitvector.h:243
detail::bitvector_iterator< const bitvector_base< AllocatorT > > const_iterator
Definition bitvector.h:240
void resize(size_type bits)
Definition bitvector.h:577
const_iterator end() const noexcept
Definition bitvector.h:943
bitvector_base(std::span< const uint8_t > bytes, std::optional< size_type > bits=std::nullopt)
Definition bitvector.h:414
bool any_vartime() const
Definition bitvector.h:711
void push_back(bool bit)
Definition bitvector.h:590
auto back() const
Definition bitvector.h:632
constexpr void _const_time_poison() const
Definition bitvector.h:925
OutT to_bytes() const
Definition bitvector.h:527
auto & operator^=(const OtherT &other)
Definition bitvector.h:880
bool equals(const OtherT &other) const noexcept
Definition bitvector.h:474
auto front() const
Definition bitvector.h:627
const_iterator cend() noexcept
Definition bitvector.h:945
bitvector_base(std::initializer_list< block_type > blocks, std::optional< size_type > bits=std::nullopt)
Definition bitvector.h:420
bool equals_vartime(const OtherT &other) const noexcept
Definition bitvector.h:463
typename T::size_type size_type
Definition bitvector.h:1410
auto equals(const OtherT &other) const
Definition bitvector.h:1451
constexpr void _const_time_poison() const
Definition bitvector.h:1496
void subvector_replace(size_type pos, InT value)
Definition bitvector.h:1446
auto subvector(size_type pos) const
Definition bitvector.h:1440
auto from_bytes(std::span< const uint8_t > bytes, std::optional< size_type > bits=std::nullopt)
Definition bitvector.h:1479
auto subvector(size_type pos, std::optional< size_type > length=std::nullopt) const
Definition bitvector.h:1433
Strong_Adapter(std::span< const typename Container_Strong_Adapter_Base< T >::value_type > span)
auto at(size_type i) const
Definition bitvector.h:1415
auto to_bytes(std::span< uint8_t > out) const
Definition bitvector.h:1488
constexpr void _const_time_unpoison() const
Definition bitvector.h:1498
constexpr T & get() &
std::bidirectional_iterator_tag iterator_category
Definition bitvector.h:135
bitvector_iterator & operator=(bitvector_iterator &&other) noexcept
Definition bitvector.h:159
std::remove_const_t< decltype(std::declval< T >().at(0))> value_type
Definition bitvector.h:130
std::partial_ordering operator<=>(const bitvector_iterator &other) const noexcept
Definition bitvector.h:187
bitvector_iterator & operator=(const bitvector_iterator &other) noexcept
Definition bitvector.h:151
bool operator==(const bitvector_iterator &other) const noexcept
Definition bitvector.h:195
bitvector_iterator operator--(int) noexcept
Definition bitvector.h:181
bitvector_iterator & operator++() noexcept
Definition bitvector.h:165
bitvector_iterator & operator--() noexcept
Definition bitvector.h:176
bitvector_iterator(bitvector_iterator &&other) noexcept
Definition bitvector.h:147
bitvector_iterator(T *bitvector, size_t offset)
Definition bitvector.h:141
std::make_signed_t< size_type > difference_type
Definition bitvector.h:129
bitvector_iterator operator++(int) noexcept
Definition bitvector.h:170
bitvector_iterator(const bitvector_iterator &other) noexcept
Definition bitvector.h:143
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:67
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:56
std::invoke_result_t< FnT, as< BlockT, ParamTs >... > blockwise_processing_callback_return_type
Definition bitvector.h:78
constexpr auto copy_lhs_allocator_aware(const T1 &lhs, const T2 &)
Definition bitvector.h:1362
typename first_type< Ts... >::type first_t
Definition bitvector.h:64
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
ASN1_Type operator|(ASN1_Type x, ASN1_Type y)
Definition asn1_obj.h:84
constexpr T mul_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:81
bitvector_base< secure_allocator > secure_bitvector
Definition bitvector.h:1352
OctetString operator^(const OctetString &k1, const OctetString &k2)
Definition symkey.cpp:109
bitvector_base< std::allocator > bitvector
Definition bitvector.h:1353
constexpr void typecast_copy(ToR &&out, const FromR &in)
Definition mem_ops.h:176
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
constexpr uint8_t ceil_log2(T x)
Definition bit_ops.h:140
constexpr decltype(auto) unwrap_strong_type(T &&t)
Generically unwraps a strong type to its underlying type.
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:736
typename detail::wrapped_type_helper< std::remove_cvref_t< T > >::type strong_type_wrapped_type
Extracts the wrapped type from a strong type.
BOTAN_FORCE_INLINE constexpr T ceil_tobytes(T bits)
Definition bit_ops.h:175
constexpr decltype(auto) wrap_strong_type(ParamT &&t)
Wraps a value into a caller-defined (strong) type.
BOTAN_FORCE_INLINE constexpr uint8_t ct_popcount(T x)
Definition bit_ops.h:273
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:495
bool operator==(const AlgorithmIdentifier &x, const AlgorithmIdentifier &y)
Definition alg_id.cpp:54
constexpr auto bitlen(size_t x)
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118
ECIES_Flags operator&(ECIES_Flags a, ECIES_Flags b)
Definition ecies.h:70