From ca92fb3746f7333a28549a39f148eab9c8402f89 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Mon, 5 Feb 2024 12:39:07 -0500 Subject: [PATCH] cpp-common/bt2c: add `DataLen` class (general-purpose data length) 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 Change-Id: I1375fb5697c0d952f3669dc587603839d511dd18 Reviewed-on: https://review.lttng.org/c/babeltrace/+/7726 Reviewed-on: https://review.lttng.org/c/babeltrace/+/12250 --- src/Makefile.am | 1 + src/cpp-common/bt2c/data-len.hpp | 223 +++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 src/cpp-common/bt2c/data-len.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 6b98e341..05feb3e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..55760f49 --- /dev/null +++ b/src/cpp-common/bt2c/data-len.hpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022 Philippe Proulx + * + * 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 */ -- 2.34.1