Add bt2c::reverseFixedLenIntBits()
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 31 May 2024 19:43:23 +0000 (15:43 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 4 Sep 2024 19:05:14 +0000 (15:05 -0400)
This new function reverses the first N least significant bits of a
64-bit integer value, sign-extending if needed, and returns the result.

I basically read [1], which shows the version of Knuth, and adapted that
code to our coding style and requirements.

[1]: https://matthewarcus.wordpress.com/2012/11/18/reversing-a-64-bit-word/

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: Ib67fd5e8b94e0a82f46527534d47617c87013418
Reviewed-on: https://review.lttng.org/c/babeltrace/+/12706

src/Makefile.am
src/cpp-common/bt2c/reverse-fixed-len-int-bits.hpp [new file with mode: 0644]

index e9c655627bc8677336c4ec1e07a7d4ba1fcb1278..24efd77e0adc6badfdec6100e2f97ed9bc2d54d0 100644 (file)
@@ -185,6 +185,7 @@ cpp_common_libcpp_common_la_SOURCES = \
        cpp-common/bt2c/prio-heap.hpp \
        cpp-common/bt2c/read-fixed-len-int.hpp \
        cpp-common/bt2c/regex.hpp \
+       cpp-common/bt2c/reverse-fixed-len-int-bits.hpp \
        cpp-common/bt2c/safe-ops.hpp \
        cpp-common/bt2c/std-int.hpp \
        cpp-common/bt2c/str-scanner.cpp \
diff --git a/src/cpp-common/bt2c/reverse-fixed-len-int-bits.hpp b/src/cpp-common/bt2c/reverse-fixed-len-int-bits.hpp
new file mode 100644 (file)
index 0000000..b4068c8
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2024 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_REVERSE_FIXED_LEN_INT_BITS_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_REVERSE_FIXED_LEN_INT_BITS_HPP
+
+#include <cstddef>
+#include <cstdint>
+
+#include "common/assert.h"
+
+#include "data-len.hpp"
+
+namespace bt2c {
+namespace internal {
+
+/*
+ * Swaps the bits.
+ *
+ * And then returns the result.
+ *
+ * See <https://matthewarcus.wordpress.com/2012/11/18/reversing-a-64-bit-word/>.
+ */
+template <typename T, T MaskV, std::size_t ShiftV>
+T swapBits(const T p) noexcept
+{
+    const auto q = ((p >> ShiftV) ^ p) & MaskV;
+
+    return p ^ q ^ (q << ShiftV);
+}
+
+} /* namespace internal */
+
+/*
+ * Based on Knuth's.
+ *
+ * For example, given an unsigned `val` with the value 0b111011010 and
+ * `*len` set to 9, the returned value is 0b010110111.
+ *
+ * Given a _signed_ `val` with the value 0b01011 and `*len` set to 5,
+ * the returned value is
+ * 0b1111111111111111111111111111111111111111111111111111111111111010
+ * (sign extended).
+ *
+ * `*len` must be less than or equal to 64.
+ */
+template <typename ValT>
+ValT reverseFixedLenIntBits(const ValT val, const DataLen len)
+{
+    static_assert(sizeof(val) == sizeof(std::uint64_t), "`val` is a 64-bit integer");
+
+    BT_ASSERT_DBG(*len <= 64);
+
+    static constexpr std::uint64_t m0 = 0x5555555555555555ULL;
+
+    /* Work with the unsigned version to perform the reversal */
+    auto uVal = static_cast<std::uint64_t>(val);
+
+    uVal = ((uVal >> 1) & m0) | (uVal & m0) << 1;
+    uVal = internal::swapBits<std::uint64_t, 0x0300c0303030c303ULL, 4>(uVal);
+    uVal = internal::swapBits<std::uint64_t, 0x00c0300c03f0003fULL, 8>(uVal);
+    uVal = internal::swapBits<std::uint64_t, 0x00000ffc00003fffULL, 20>(uVal);
+    uVal = (uVal >> 34) | (uVal << 30);
+
+    /*
+     * Sign-extends when `ValT` is signed because in that case the sign
+     * bit (LSB of `val`) is at position 63 before this shift.
+     */
+    return static_cast<ValT>(uVal) >> (64 - *len);
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_REVERSE_FIXED_LEN_INT_BITS_HPP */
This page took 0.027771 seconds and 4 git commands to generate.