cpp-common/bt2c: add `DataLen` class (general-purpose data length)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 5 Feb 2024 17:39:07 +0000 (12:39 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 17 Apr 2024 17:57:53 +0000 (13:57 -0400)
This patch adds the `bt2c::DataLen` class so that you can make sure your
function accepts an expected data length instead of specifying that you
need bits or bytes:

    Something buildSomething(bt2c::DataLen len, ...)
    {
        using namespace bt2c::literals::datalen;

        if (...) {
            len += 16_MiBytes;
        } else {
            len *= 2;
        }

        // ...
    }

    // ...

    const auto something = buildSomething(DataLen::fromBytes(sz), ...);

As you can see above, you can use the `bt2c::literals::datalen`
namespace to access handy data length user literals.

The `bt2c::DataLen` constructor is private: you need to use the
bt2c::DataLen::fromBits() and bt2c::DataLen::fromBytes()
static methods to build an instance.

Because a `bt2c::DataLen` instance is just an `unsigned long long`
value in memory, the intention of this API is to copy data length
instances as much as possible. Therefore, I suppose it's cheaper to
accept a `const bt2c::DataLen` parameter than
`const bt2c::DataLen&` which would add an indirection.

The constructor and many methods are `constexpr` to make this useable in
constant context.

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

src/Makefile.am
src/cpp-common/bt2c/data-len.hpp [new file with mode: 0644]

index 6b98e341782c1e51fb080cdcb05b137d0e6f84a4..05feb3e741b026bfec2a82591f0dabee1bf8894e 100644 (file)
@@ -161,6 +161,7 @@ cpp_common_libcpp_common_la_SOURCES = \
        cpp-common/bt2c/c-string-view.hpp \
        cpp-common/bt2c/call.hpp \
        cpp-common/bt2c/contains.hpp \
+       cpp-common/bt2c/data-len.hpp \
        cpp-common/bt2c/dummy.cpp \
        cpp-common/bt2c/endian.hpp \
        cpp-common/bt2c/exc.hpp \
diff --git a/src/cpp-common/bt2c/data-len.hpp b/src/cpp-common/bt2c/data-len.hpp
new file mode 100644 (file)
index 0000000..55760f4
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP
+
+#include "safe-ops.hpp"
+
+namespace bt2c {
+
+/*
+ * A data length is a quantity of binary data (bits).
+ *
+ * This class can make some code clearer and safer because its
+ * constructor is private so that you need to call DataLen::fromBits()
+ * or DataLen::fromBytes() to create an instance.
+ *
+ * With a `DataLen` instance `len`, use `*len` or `len.bits()` to get
+ * the quantity in bits and `len.bytes()` to get it in bytes (floored).
+ *
+ * You can add, subtract, and compare data lengths.
+ */
+class DataLen final
+{
+private:
+    constexpr explicit DataLen(const unsigned long long lenBits) noexcept : _mLenBits {lenBits}
+    {
+    }
+
+public:
+    /*
+     * Creates and returns a data length instance representing `lenBits`
+     * bits.
+     */
+    static constexpr DataLen fromBits(const unsigned long long lenBits) noexcept
+    {
+        return DataLen {lenBits};
+    }
+
+    /*
+     * Creates and returns a data length instance representing
+     * `lenBytes` bytes.
+     */
+    static DataLen fromBytes(const unsigned long long lenBytes) noexcept
+    {
+        return DataLen {safeMul(lenBytes, 8ULL)};
+    }
+
+    /*
+     * Number of bits of this data length.
+     */
+    constexpr unsigned long long operator*() const noexcept
+    {
+        return _mLenBits;
+    }
+
+    /*
+     * Number of bits of this data length.
+     */
+    constexpr unsigned long long bits() const noexcept
+    {
+        return _mLenBits;
+    }
+
+    /*
+     * Number of bytes (floor) of this data length.
+     */
+    constexpr unsigned long long bytes() const noexcept
+    {
+        return _mLenBits / 8;
+    }
+
+    /*
+     * Whether or not this data length represents a multiple of eight
+     * bits.
+     */
+    constexpr bool hasExtraBits() const noexcept
+    {
+        return this->extraBitCount() > 0;
+    }
+
+    /*
+     * Remainder of this data length, in bits, divided by eight.
+     */
+    constexpr unsigned int extraBitCount() const noexcept
+    {
+        return _mLenBits & 7;
+    }
+
+    /*
+     * Returns whether or not this data length is a power of two bits.
+     */
+    constexpr bool isPowOfTwo() const noexcept
+    {
+        return ((_mLenBits & (_mLenBits - 1)) == 0) && _mLenBits > 0;
+    }
+
+    constexpr bool operator==(const DataLen& other) const noexcept
+    {
+        return _mLenBits == other._mLenBits;
+    }
+
+    constexpr bool operator!=(const DataLen& other) const noexcept
+    {
+        return !(*this == other);
+    }
+
+    constexpr bool operator<(const DataLen& other) const noexcept
+    {
+        return _mLenBits < other._mLenBits;
+    }
+
+    constexpr bool operator<=(const DataLen& other) const noexcept
+    {
+        return (*this == other) || (*this < other);
+    }
+
+    constexpr bool operator>(const DataLen& other) const noexcept
+    {
+        return !(*this <= other);
+    }
+
+    constexpr bool operator>=(const DataLen& other) const noexcept
+    {
+        return (*this > other) || (*this == other);
+    }
+
+    DataLen& operator+=(const DataLen len) noexcept
+    {
+        _mLenBits = safeAdd(_mLenBits, len._mLenBits);
+        return *this;
+    }
+
+    DataLen& operator-=(const DataLen len) noexcept
+    {
+        _mLenBits = safeSub(_mLenBits, len._mLenBits);
+        return *this;
+    }
+
+    DataLen& operator*=(const unsigned long long mul) noexcept
+    {
+        _mLenBits = safeMul(_mLenBits, mul);
+        return *this;
+    }
+
+private:
+    unsigned long long _mLenBits = 0;
+};
+
+static inline DataLen operator+(const DataLen lenA, const DataLen lenB) noexcept
+{
+    return DataLen::fromBits(safeAdd(*lenA, *lenB));
+}
+
+static inline DataLen operator-(const DataLen lenA, const DataLen lenB) noexcept
+{
+    return DataLen::fromBits(safeSub(*lenA, *lenB));
+}
+
+static inline DataLen operator*(const DataLen len, const unsigned long long mul) noexcept
+{
+    return DataLen::fromBits(safeMul(*len, mul));
+}
+
+/*
+ * Use this namespace to access handy data length user literals, for
+ * example:
+ *
+ *     using namespace bt2c::literals::datalen;
+ *
+ *     const auto bufSize = 64_MiBytes + 8_KiBits;
+ */
+namespace literals {
+namespace datalen {
+
+static inline DataLen operator""_bits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(val);
+}
+
+static inline DataLen operator""_KiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL));
+}
+
+static inline DataLen operator""_MiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL * 1024));
+}
+
+static inline DataLen operator""_GiBits(const unsigned long long val) noexcept
+{
+    return DataLen::fromBits(safeMul(val, 1024ULL * 1024 * 1024));
+}
+
+static inline DataLen operator""_bytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(val);
+}
+
+static inline DataLen operator""_KiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL));
+}
+
+static inline DataLen operator""_MiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL * 1024));
+}
+
+static inline DataLen operator""_GiBytes(const unsigned long long val) noexcept
+{
+    return DataLen::fromBytes(safeMul(val, 1024ULL * 1024 * 1024));
+}
+
+} /* namespace datalen */
+} /* namespace literals */
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_DATA_LEN_HPP */
This page took 0.026676 seconds and 4 git commands to generate.