Mjolnir Core
Core functionality of the Mjolnir API
bit_operations.h
Go to the documentation of this file.
1 
11 
12 #pragma once
13 
15 
16 #include <concepts>
17 #include <limits>
18 
19 
20 namespace mjolnir
21 {
24 
25 
31 template <typename T_Type>
32 inline constexpr UST num_bits = sizeof(T_Type) * CHAR_BIT;
33 
34 
54 template <std::unsigned_integral T_Type, UST... t_bit_values>
55 [[nodiscard]] consteval auto bit_construct([[maybe_unused]] bool left_is_low = false) noexcept -> T_Type;
56 
57 
79 template <UST t_num_int_bits, std::unsigned_integral T_Type, UST... t_integers>
80 [[nodiscard]] consteval auto bit_construct_from_ints(bool left_is_low = false) noexcept -> T_Type;
81 
82 
96 template <std::unsigned_integral T_Type, UST t_num_bits>
97 [[nodiscard]] constexpr auto bit_construct_set_first_n_bits() noexcept -> T_Type;
98 
99 
110 template <std::unsigned_integral T_Type>
111 constexpr void clear_bit(T_Type& integer, UST index) noexcept;
112 
113 
126 template <UST t_num_bits, std::unsigned_integral T_Type>
127 constexpr void clear_bits(T_Type& integer, UST index) noexcept;
128 
129 
148 template <UST t_index,
149  I32 t_shift = 0,
150  std::unsigned_integral T_Type = UST,
151  std::unsigned_integral T_ReturnType = T_Type>
152 [[nodiscard]] constexpr auto get_bit(T_Type integer) noexcept -> T_ReturnType;
153 
154 
173 template <std::unsigned_integral T_Type = UST, std::unsigned_integral T_ReturnType = T_Type>
174 [[nodiscard]] constexpr auto get_bit(T_Type integer, UST index, I32 shift = 0) noexcept -> T_ReturnType;
175 
176 
194 template <UST t_index,
195  bool t_shift_right = true,
196  std::unsigned_integral T_Type = UST,
197  std::unsigned_integral T_ReturnType = T_Type>
198 [[nodiscard]] constexpr auto get_bit_shift_max(T_Type integer) noexcept -> T_ReturnType;
199 
200 
218 template <bool t_shift_right = true, std::unsigned_integral T_Type = UST, std::unsigned_integral T_ReturnType = T_Type>
219 [[nodiscard]] constexpr auto get_bit_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType;
220 
221 
243 template <UST t_index,
244  UST t_num_bits,
245  I32 t_shift = 0,
246  std::unsigned_integral T_Type = UST,
247  std::unsigned_integral T_ReturnType = T_Type>
248 [[nodiscard]] constexpr auto get_bits(T_Type integer) noexcept -> T_ReturnType;
249 
250 
272 template <UST t_num_bits, std::unsigned_integral T_Type = UST, std::unsigned_integral T_ReturnType = T_Type>
273 [[nodiscard]] constexpr auto get_bits(T_Type integer, UST index, I32 shift = 0) noexcept -> T_ReturnType;
274 
275 
295 template <UST t_index,
296  UST t_num_bits,
297  bool t_shift_right = true,
298  std::unsigned_integral T_Type = UST,
299  std::unsigned_integral T_ReturnType = T_Type>
300 [[nodiscard]] constexpr auto get_bits_shift_max(T_Type integer) noexcept -> T_ReturnType;
301 
302 
322 template <UST t_num_bits,
323  bool t_shift_right = true,
324  std::unsigned_integral T_Type = UST,
325  std::unsigned_integral T_ReturnType = T_Type>
326 [[nodiscard]] constexpr auto get_bits_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType;
327 
341 template <typename T_Type>
342 [[nodiscard]] constexpr auto is_bit_set(T_Type integer, UST index) noexcept -> bool;
343 
344 
355 template <std::unsigned_integral T_Type>
356 constexpr void set_bit(T_Type& integer, UST index) noexcept;
357 
358 
371 template <UST t_value, std::unsigned_integral T_Type>
372 constexpr void set_bit_to(T_Type& integer, UST index) noexcept;
373 
374 
387 template <UST t_num_bits, std::unsigned_integral T_Type>
388 constexpr void set_bits(T_Type& integer, UST index) noexcept;
389 
390 
408 template <UST t_num_bits, bool t_clear_bits = true, std::unsigned_integral T_Type>
409 constexpr void set_bits_with_int(T_Type& integer, UST index, UST value) noexcept;
410 
411 
413 } // namespace mjolnir
414 
415 
416 // ====================================================================================================================
417 
418 
419 #include <initializer_list>
420 
421 #include <algorithm>
422 #include <cassert>
423 #include <limits>
424 
425 namespace mjolnir
426 {
427 // ---internal definitions --------------------------------------------------------------------------------------------
428 
430 namespace internal
431 {
434 template <std::integral T_Type>
435 [[nodiscard]] constexpr auto abs(T_Type value) noexcept -> T_Type
436 {
437  if constexpr (std::is_unsigned_v<T_Type>)
438  {
439  return value;
440  }
441  else
442  {
443  // source: https://stackoverflow.com/a/9772647/6700329
444  // regarding nolint: https://stackoverflow.com/a/58845898/6700329
445  int const mask = value >> (sizeof(T_Type) * CHAR_BIT - 1); // NOLINT(hicpp-signed-bitwise)
446  return (value + mask) ^ mask; // NOLINT(hicpp-signed-bitwise)
447  }
448 }
449 
450 
451 } // namespace internal
453 
454 
455 // --- public definitions ---------------------------------------------------------------------------------------------
456 
457 
458 template <std::unsigned_integral T_Type, UST... t_bit_values>
459 [[nodiscard]] consteval auto bit_construct([[maybe_unused]] bool left_is_low) noexcept -> T_Type
460 {
461  static_assert(sizeof...(t_bit_values) <= num_bits<T_Type>, "Number of bit values exceeds number of type bits.");
462 
463  T_Type integer = 0;
464  if constexpr (sizeof...(t_bit_values) != 0)
465  {
466  if (left_is_low)
467  {
468  UST bit_index = 0;
469  (void) std::initializer_list<I32>{(set_bit_to<t_bit_values>(integer, bit_index++), 0)...};
470  }
471  else
472  {
473  UST bit_index = sizeof...(t_bit_values) - 1;
474  (void) std::initializer_list<I32>{(set_bit_to<t_bit_values>(integer, bit_index--), 0)...};
475  }
476  }
477  return integer; // NOLINT(readability-misleading-indentation) --- clang-tidy bug
478 }
479 
480 
481 // --------------------------------------------------------------------------------------------------------------------
482 
483 template <std::unsigned_integral T_Type, UST t_num_bits>
484 [[nodiscard]] constexpr auto bit_construct_set_first_n_bits() noexcept -> T_Type
485 {
486  // INFO:
487  // The static_cast for the `true` case is actually needed because the `~` operator seems to perform a promotion to
488  // `int`. See the following link: https://stackoverflow.com/q/30473958/6700329
489  if constexpr (t_num_bits >= num_bits<T_Type>)
490  return static_cast<T_Type>(~(T_Type(0)));
491  else
492  return static_cast<T_Type>(((UST(1)) << t_num_bits) - 1);
493 }
494 
495 
496 // --------------------------------------------------------------------------------------------------------------------
497 
498 template <UST t_num_int_bits, std::unsigned_integral T_Type, UST... t_integers>
499 [[nodiscard]] consteval auto bit_construct_from_ints(bool left_is_low) noexcept -> T_Type
500 {
501  static_assert(sizeof...(t_integers) * t_num_int_bits <= num_bits<T_Type>,
502  "Totoal number of provided bits exceeds number of type bits.");
503 
504  T_Type integer = 0;
505  if (left_is_low)
506  {
507  UST bit_index = 0;
508  (void) std::initializer_list<I32>{(set_bits_with_int<t_num_int_bits, false>(integer, bit_index, t_integers),
509  bit_index += t_num_int_bits,
510  0)...};
511  }
512  else
513  {
514  UST bit_index = (sizeof...(t_integers) - 1) * t_num_int_bits;
515  (void) std::initializer_list<I32>{(set_bits_with_int<t_num_int_bits, false>(integer, bit_index, t_integers),
516  bit_index -= t_num_int_bits,
517  0)...};
518  }
519 
520  return integer;
521 }
522 
523 
524 // --------------------------------------------------------------------------------------------------------------------
525 
526 template <std::unsigned_integral T_Type>
527 constexpr void clear_bit(T_Type& integer, UST index) noexcept
528 {
529  assert(index < num_bits<T_Type> && "Index exceeds number of bits."); // NOLINT
530 
531  integer &= static_cast<T_Type>(~(UST(1) << index));
532 }
533 
534 
535 // --------------------------------------------------------------------------------------------------------------------
536 
537 template <UST t_num_bits, std::unsigned_integral T_Type>
538 constexpr void clear_bits(T_Type& integer, UST index) noexcept
539 {
540  assert(index + t_num_bits <= num_bits<T_Type>); // NOLINT
541 
542  constexpr UST bits = bit_construct_set_first_n_bits<UST, t_num_bits>();
543  integer &= static_cast<T_Type>(~(bits << index));
544 }
545 
546 
547 // --------------------------------------------------------------------------------------------------------------------
548 
549 template <UST t_index, I32 t_shift, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
550 [[nodiscard]] constexpr auto get_bit(T_Type integer) noexcept -> T_ReturnType
551 {
552  static_assert(t_index < num_bits<T_Type>, "Index exceeds number of bits");
553 
554  T_ReturnType bit = (integer & static_cast<T_Type>(UST(1) << t_index));
555 
556  if constexpr (t_shift == 0)
557  return bit;
558  else if constexpr (t_shift > 0)
559  {
560  static_assert(t_index + t_shift < num_bits<T_ReturnType>, "Shift exceeds highest bit.");
561  return bit << static_cast<UST>(t_shift);
562  }
563  else
564  {
565  static_assert(t_index >= -t_shift, "Shift exceeds lowest bit");
566  return bit >> static_cast<UST>(internal::abs(t_shift));
567  }
568 }
569 
570 
571 // --------------------------------------------------------------------------------------------------------------------
572 
573 
574 template <std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
575 [[nodiscard]] constexpr auto get_bit(T_Type integer, UST index, I32 shift) noexcept -> T_ReturnType
576 {
577  assert(index < num_bits<T_Type>); // NOLINT
578 
579  T_ReturnType bit = (integer & static_cast<T_Type>(UST(1) << index));
580 
581  if (shift >= 0)
582  {
583  assert(index + shift < num_bits<T_ReturnType>); // NOLINT
584  return bit << static_cast<UST>(shift);
585  }
586 
587  assert(index >= internal::abs(shift)); // NOLINT // LCOV_EXCL_LINE
588  return bit >> static_cast<UST>(internal::abs(shift));
589 }
590 
591 
592 // --------------------------------------------------------------------------------------------------------------------
593 
594 template <UST t_index, bool t_shift_right, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
595 [[nodiscard]] constexpr auto get_bit_shift_max(T_Type integer) noexcept -> T_ReturnType
596 {
597  // cppcheck-suppress unreadVariable // false positive
598  constexpr I32 shift =
599  t_shift_right ? -static_cast<I32>(t_index) : static_cast<I32>(num_bits<T_ReturnType> - t_index - 1);
600  return get_bit<t_index, shift, T_Type, T_ReturnType>(integer);
601 }
602 
603 
604 // --------------------------------------------------------------------------------------------------------------------
605 
606 template <bool t_shift_right, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
607 [[nodiscard]] constexpr auto get_bit_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType
608 {
609  if constexpr (t_shift_right)
610  return get_bit<T_Type, T_ReturnType>(integer, index, -static_cast<I32>(index));
611  else
612  return get_bit<T_Type, T_ReturnType>(integer, index, static_cast<I32>(num_bits<T_ReturnType> - index - 1));
613 }
614 
615 
616 // --------------------------------------------------------------------------------------------------------------------
617 
618 template <UST t_index, UST t_num_bits, I32 t_shift, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
619 [[nodiscard]] constexpr auto get_bits(T_Type integer) noexcept -> T_ReturnType
620 {
621  static_assert(t_num_bits > 1, "Number of bits must be larger than 0.");
622  static_assert(t_index + t_num_bits <= num_bits<T_Type>, "Required bits exceed maximum number of bits.");
623 
624  constexpr UST mask = bit_construct_set_first_n_bits<UST, t_num_bits>() << t_index;
625  auto bits = static_cast<T_ReturnType>(integer & mask);
626 
627  if constexpr (t_shift == 0)
628  {
629  static_assert(t_index + t_num_bits <= num_bits<T_ReturnType>, "Shifted bits exceed highest bit.");
630  return bits;
631  }
632  else if constexpr (t_shift > 0)
633  {
634  static_assert(t_index + t_num_bits + t_shift <= num_bits<T_ReturnType>, "Shifted bits exceed highest bit.");
635  return bits << static_cast<UST>(t_shift);
636  }
637  else
638  {
639  static_assert(t_index >= -t_shift, "Shifted bits exceed lowest bit.");
640  return bits >> static_cast<UST>(internal::abs(t_shift));
641  }
642 }
643 
644 
645 // --------------------------------------------------------------------------------------------------------------------
646 
647 template <UST t_num_bits, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
648 [[nodiscard]] constexpr auto get_bits(T_Type integer, UST index, I32 shift) noexcept -> T_ReturnType
649 {
650  static_assert(t_num_bits > 1, "Number of bits must be larger than 0.");
651  assert(index + t_num_bits <= num_bits<T_Type>); // NOLINT
652 
653  UST mask = bit_construct_set_first_n_bits<UST, t_num_bits>() << index;
654  auto bits = static_cast<T_ReturnType>(integer & mask);
655 
656  if (shift >= 0)
657  {
658  assert(index + t_num_bits + shift <= num_bits<T_ReturnType>); // NOLINT // LCOV_EXCL_LINE
659  return bits << static_cast<UST>(shift);
660  }
661 
662  assert(index >= internal::abs(shift)); // NOLINT // LCOV_EXCL_LINE
663  return bits >> static_cast<UST>(internal::abs(shift));
664 }
665 
666 
667 // --------------------------------------------------------------------------------------------------------------------
668 
669 template <UST t_index,
670  UST t_num_bits,
671  bool t_shift_right,
672  std::unsigned_integral T_Type,
673  std::unsigned_integral T_ReturnType>
674 [[nodiscard]] constexpr auto get_bits_shift_max(T_Type integer) noexcept -> T_ReturnType
675 {
676  // cppcheck-suppress unreadVariable // false positive
677  constexpr I32 shift = t_shift_right ? -static_cast<I32>(t_index)
678  : static_cast<I32>(num_bits<T_ReturnType> - t_index - t_num_bits);
679  return get_bits<t_index, t_num_bits, shift, T_Type, T_ReturnType>(integer);
680 }
681 
682 
683 // --------------------------------------------------------------------------------------------------------------------
684 
685 template <UST t_num_bits, bool t_shift_right, std::unsigned_integral T_Type, std::unsigned_integral T_ReturnType>
686 [[nodiscard]] constexpr auto get_bits_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType
687 {
688  if constexpr (t_shift_right)
689  return get_bits<t_num_bits, T_Type, T_ReturnType>(integer, index, -static_cast<I32>(index));
690  else
691  return get_bits<t_num_bits, T_Type, T_ReturnType>(
692  integer, index, static_cast<I32>(num_bits<T_ReturnType> - index - t_num_bits));
693 }
694 
695 
696 // --------------------------------------------------------------------------------------------------------------------
697 
698 template <typename T_Type>
699 [[nodiscard]] constexpr auto is_bit_set(T_Type integer, UST index) noexcept -> bool
700 {
701  return static_cast<bool>(get_bit(integer, index));
702 }
703 
704 
705 // --------------------------------------------------------------------------------------------------------------------
706 
707 template <std::unsigned_integral T_Type>
708 constexpr void set_bit(T_Type& integer, UST index) noexcept
709 {
710  assert(index < num_bits<T_Type> && "Index exceeds number of bits."); // NOLINT
711 
712  integer |= static_cast<T_Type>(UST(1) << index);
713 }
714 
715 
716 // --------------------------------------------------------------------------------------------------------------------
717 
718 template <UST t_value, std::unsigned_integral T_Type>
719 constexpr void set_bit_to(T_Type& integer, UST index) noexcept
720 {
721  static_assert(t_value <= 1UL, "Bit value must either be 0 or 1.");
722 
723  if constexpr (t_value == 1UL)
724  set_bit(integer, index);
725  else
726  clear_bit(integer, index);
727 }
728 
729 
730 // --------------------------------------------------------------------------------------------------------------------
731 
732 template <UST t_num_bits, std::unsigned_integral T_Type>
733 constexpr void set_bits(T_Type& integer, UST index) noexcept
734 {
735  assert(index + t_num_bits <= num_bits<T_Type>); // NOLINT
736 
737  constexpr UST bits = bit_construct_set_first_n_bits<UST, t_num_bits>();
738  integer |= static_cast<T_Type>(bits << index);
739 }
740 
741 
742 // --------------------------------------------------------------------------------------------------------------------
743 
744 template <UST t_num_bits, bool t_clear_bits, std::unsigned_integral T_Type>
745 constexpr void set_bits_with_int(T_Type& integer, UST index, UST value) noexcept
746 {
747  [[maybe_unused]] constexpr UST max_value = bit_construct_set_first_n_bits<UST, t_num_bits>();
748 
749  assert(value <= max_value && "Value doesn't fit into specified number of bits."); // NOLINT
750  assert(index + t_num_bits <= num_bits<T_Type>); // NOLINT
751 
752  if constexpr (t_clear_bits)
753  clear_bits<t_num_bits>(integer, index);
754 
755  integer |= static_cast<T_Type>(value << index);
756 }
757 
758 
759 } // namespace mjolnir
Defines the fundamental data types.
std::int32_t I32
32 bit signed integer type
Definition: fundamental_types.h:22
std::size_t UST
Unsigned integer type that is returned by sizeof operations.
Definition: fundamental_types.h:29
constexpr auto get_bit_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType
Extract a bit from an integer and shift it maximally.
Definition: bit_operations.h:607
constexpr auto get_bit(T_Type integer, UST index, I32 shift=0) noexcept -> T_ReturnType
Extract a bit from an integer and store it with an optional shift in a new integer.
Definition: bit_operations.h:575
consteval auto bit_construct([[maybe_unused]] bool left_is_low=false) noexcept -> T_Type
Construct an unsigned integer by setting its individual bits.
Definition: bit_operations.h:459
constexpr void set_bit_to(T_Type &integer, UST index) noexcept
Set a single bit of an unsigned integer to the specified value.
Definition: bit_operations.h:719
constexpr void clear_bit(T_Type &integer, UST index) noexcept
Clear a single specific bit of an unsigned integer.
Definition: bit_operations.h:527
constexpr void set_bit(T_Type &integer, UST index) noexcept
Set a single specific bit of an unsigned integer.
Definition: bit_operations.h:708
constexpr auto get_bits(T_Type integer, UST index, I32 shift=0) noexcept -> T_ReturnType
Extract a bit pattern from an integer and store it with an optional shift in a new integer.
Definition: bit_operations.h:648
constexpr auto is_bit_set(T_Type integer, UST index) noexcept -> bool
Return true if a specific bit is set and false otherwise.
Definition: bit_operations.h:699
constexpr auto get_bits_shift_max(T_Type integer, UST index) noexcept -> T_ReturnType
Extract a bit pattern from an integer and store it with a maximal shift in a new integer.
Definition: bit_operations.h:686
constexpr void set_bits_with_int(T_Type &integer, UST index, UST value) noexcept
Set multiple consecutive bits of an unsigned integer using the bit pattern of another integer value.
Definition: bit_operations.h:745
consteval auto bit_construct_from_ints(bool left_is_low=false) noexcept -> T_Type
Construct an unsigned integer from the bit patterns of multiple integer values.
Definition: bit_operations.h:499
constexpr auto bit_construct_set_first_n_bits() noexcept -> T_Type
Construct an unsigned integer with its first t_num_bits set to 1.
Definition: bit_operations.h:484
constexpr void set_bits(T_Type &integer, UST index) noexcept
Set multiple consecutive bits of an unsigned integer.
Definition: bit_operations.h:733
constexpr UST num_bits
The size of a type in bits.
Definition: bit_operations.h:32
constexpr void clear_bits(T_Type &integer, UST index) noexcept
Clear multiple consecutive bits of an unsigned integer.
Definition: bit_operations.h:538
auto abs(T_RegisterType src) noexcept -> T_RegisterType
Return a new register with the absolute values of the input register.
Definition: sign_manipulation.h:108