C++ 使用 constexpr 、查表法、分治法加速位镜像翻转

代码

cpp 复制代码
		///
		/// @brief 左右翻转位。
		///
		/// @note 翻转后,最低位位将变为最高位,最高位将变为最低位。
		///
		///
		template <typename T>
			requires(std::is_same_v<T, uint8_t>)
		constexpr T Reverse(T value)
		{
			int32_t bit_count = sizeof(T) * 8;
			for (int32_t i = 0; i < bit_count / 2; i++)
			{
				int32_t left_index = bit_count - 1 - i;
				int32_t right_index = i;
				bool left_bit = base::bit::ReadBit(value, left_index);
				bool right_bit = base::bit::ReadBit(value, right_index);
				base::bit::WriteBit(value, left_index, right_bit);
				base::bit::WriteBit(value, right_index, left_bit);
			}

			return value;
		}

		///
		/// @brief 左右翻转位。
		///
		/// @note 翻转后,最低位位将变为最高位,最高位将变为最低位。
		///
		///
		template <typename T>
			requires(std::is_integral_v<T> && !std::is_same_v<T, uint8_t>)
		constexpr T Reverse(T value)
		{
			if (std::is_constant_evaluated())
			{
				// 编译时计算路径
				int32_t bit_count = sizeof(T) * 8;
				for (int32_t i = 0; i < bit_count / 2; i++)
				{
					int32_t left_index = bit_count - 1 - i;
					int32_t right_index = i;
					bool left_bit = base::bit::ReadBit(value, left_index);
					bool right_bit = base::bit::ReadBit(value, right_index);
					base::bit::WriteBit(value, left_index, right_bit);
					base::bit::WriteBit(value, right_index, left_bit);
				}

				return value;
			}

			// 运行时计算路径
			class Table
			{
			private:
				std::array<uint8_t, 256> _table{};

			public:
				constexpr Table()
				{
					for (int32_t i = 0; i < 256; i++)
					{
						_table[i] = base::bit::Reverse(static_cast<uint8_t>(i));
					}
				}

				constexpr uint8_t operator[](uint8_t index) const
				{
					return _table[index];
				}
			};

			constexpr Table table{};
			uint8_t *bytes = reinterpret_cast<uint8_t *>(&value);

			for (size_t i = 0; i < sizeof(T); i++)
			{
				bytes[i] = table[bytes[i]];
			}

			std::reverse(bytes, bytes + sizeof(T));
			return value;
		}

先准备一个针对 uint8_t 的版本

cpp 复制代码
		template <typename T>
			requires(std::is_same_v<T, uint8_t>)
		constexpr T Reverse(T value)

后续表格的建立基于这个版本。

翻转一个 8 位以上的整型的位,等价于逐个字节翻转,然后翻转字节序。这是可以用查表法优化的理论基础。

使用 constexpr 生成一个表格

cpp 复制代码
			// 运行时计算路径
			class Table
			{
			private:
				std::array<uint8_t, 256> _table{};

			public:
				constexpr Table()
				{
					for (int32_t i = 0; i < 256; i++)
					{
						_table[i] = base::bit::Reverse(static_cast<uint8_t>(i));
					}
				}

				constexpr uint8_t operator[](uint8_t index) const
				{
					return _table[index];
				}
			};

			constexpr Table table{};

翻转一个字节只需查表即可完成。对于 64 位整型,需要查表 8 次,然后翻转 8 个字节。

针对非 uint8_t 的版本

cpp 复制代码
		template <typename T>
			requires(std::is_integral_v<T> && !std::is_same_v<T, uint8_t>)
		constexpr T Reverse(T value)

中使用了 std::is_constant_evaluated() ,为编译时计算和运行时计算分别定制了逻辑。

测试

cpp 复制代码
		// 0b10100111 = 0xa7 重复 8 次。
		// 0xa7a7a7a7a7a7a7a7
		constexpr uint64_t num = base::bit::Reverse(static_cast<uint64_t>(0b1010011110100111101001111010011110100111101001111010011110100111));
		std::cout << base::ToHexString(num) << std::endl;

运行结果

bash 复制代码
0xa84
0xe5e5e5e5e5e5e5e5

使用计算器验证