Skip to content

Commit 80315f4

Browse files
Make uintvar more class like to be used as members in other classes. (#537)
* Make uintvar more class like to be used as members in other classes. * Undo swapbytes rename. * Formatting.
1 parent 729f086 commit 80315f4

File tree

3 files changed

+64
-30
lines changed

3 files changed

+64
-30
lines changed

benchmark/uintvar.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static void
4343
UIntVar_FromBytes(benchmark::State& state)
4444
{
4545
constexpr auto var = quicr::UintVar(0x123456789);
46-
auto bytes = std::span{ var };
46+
constexpr auto bytes = std::bit_cast<std::array<std::uint8_t, sizeof(uint64_t)>>(var);
4747
for ([[maybe_unused]] const auto& _ : state) {
4848
auto value = quicr::UintVar(bytes);
4949
benchmark::DoNotOptimize(value);

include/quicr/detail/uintvar.h

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@
1212

1313
namespace quicr {
1414
namespace {
15-
constexpr bool kIsBigEndian = std::endian::native == std::endian::big;
16-
1715
constexpr std::uint16_t SwapBytes(const std::uint16_t value)
1816
{
19-
if constexpr (kIsBigEndian)
17+
if constexpr (std::endian::native == std::endian::big)
2018
return value;
2119

2220
return ((value >> 8) & 0x00FF) | ((value << 8) & 0xFF00);
2321
}
2422

2523
constexpr std::uint32_t SwapBytes(const std::uint32_t value)
2624
{
27-
if constexpr (kIsBigEndian)
25+
if constexpr (std::endian::native == std::endian::big)
2826
return value;
2927

3028
return ((value >> 24) & 0x000000FF) | ((value >> 8) & 0x0000FF00) | ((value << 8) & 0x00FF0000) |
@@ -33,7 +31,7 @@ namespace quicr {
3331

3432
constexpr std::uint64_t SwapBytes(const std::uint64_t value)
3533
{
36-
if constexpr (kIsBigEndian)
34+
if constexpr (std::endian::native == std::endian::big)
3735
return value;
3836

3937
return ((value >> 56) & 0x00000000000000FF) | ((value >> 40) & 0x000000000000FF00) |
@@ -47,42 +45,65 @@ namespace quicr {
4745
{
4846
public:
4947
constexpr UintVar(uint64_t value)
50-
: be_value_{ SwapBytes(value) }
48+
: be_value_{ std::bit_cast<std::array<std::uint8_t, sizeof(std::uint64_t)>>(SwapBytes(value)) }
5149
{
52-
constexpr uint64_t kLen1 = (static_cast<uint64_t>(-1) << (64 - 6) >> (64 - 6));
53-
constexpr uint64_t kLen2 = (static_cast<uint64_t>(-1) << (64 - 14) >> (64 - 14));
54-
constexpr uint64_t kLen4 = (static_cast<uint64_t>(-1) << (64 - 30) >> (64 - 30));
50+
constexpr uint64_t k14bitLength = (static_cast<uint64_t>(-1) << (64 - 6) >> (64 - 6));
51+
constexpr uint64_t k30bitLength = (static_cast<uint64_t>(-1) << (64 - 14) >> (64 - 14));
52+
constexpr uint64_t k62bitLength = (static_cast<uint64_t>(-1) << (64 - 30) >> (64 - 30));
5553

56-
if (static_cast<uint8_t>(be_value_) & 0xC0u) { // Check if invalid
54+
if (be_value_.front() & 0xC0u) { // Check if invalid
5755
throw std::invalid_argument("Value greater than uintvar maximum");
5856
}
5957

60-
if (value > kLen4) { // 62 bit encoding (8 bytes)
61-
be_value_ |= 0xC0ull;
62-
} else if (value > kLen2) { // 30 bit encoding (4 bytes)
63-
be_value_ >>= 32;
64-
be_value_ |= 0x80ull;
65-
} else if (value > kLen1) { // 14 bit encoding (2 bytes)
66-
be_value_ >>= 48;
67-
be_value_ |= 0x40ull;
58+
std::uint64_t be_v = std::bit_cast<std::uint64_t>(be_value_);
59+
if (value > k62bitLength) { // 62 bit encoding (8 bytes)
60+
be_v |= 0xC0ull;
61+
} else if (value > k30bitLength) { // 30 bit encoding (4 bytes)
62+
be_v >>= 32;
63+
be_v |= 0x80ull;
64+
} else if (value > k14bitLength) { // 14 bit encoding (2 bytes)
65+
be_v >>= 48;
66+
be_v |= 0x40ull;
6867
} else {
69-
be_value_ >>= 56;
68+
be_v >>= 56;
7069
}
70+
71+
be_value_ = std::bit_cast<std::array<std::uint8_t, sizeof(std::uint64_t)>>(be_v);
7172
}
7273

73-
UintVar(std::span<const uint8_t> bytes)
74+
constexpr UintVar(std::span<const std::uint8_t> bytes)
7475
: be_value_{ 0 }
7576
{
76-
if (bytes.empty() || bytes.size() < Size(bytes[0])) {
77+
if (bytes.empty() || bytes.size() < Size(bytes.front())) {
7778
throw std::invalid_argument("Invalid bytes for uintvar");
7879
}
7980

80-
std::memcpy(&be_value_, bytes.data(), Size(bytes[0]));
81+
const std::size_t size = Size(bytes.front());
82+
if (std::is_constant_evaluated()) {
83+
for (std::size_t i = 0; i < size; ++i) {
84+
be_value_[i] = bytes.data()[i];
85+
}
86+
} else {
87+
std::memcpy(&be_value_, bytes.data(), size);
88+
}
89+
}
90+
91+
constexpr UintVar(const UintVar&) noexcept = default;
92+
constexpr UintVar(UintVar&&) noexcept = default;
93+
constexpr UintVar& operator=(const UintVar&) noexcept = default;
94+
constexpr UintVar& operator=(UintVar&&) noexcept = default;
95+
96+
constexpr UintVar& operator=(std::uint64_t value)
97+
{
98+
UintVar t(value);
99+
this->be_value_ = t.be_value_;
100+
return *this;
81101
}
82102

83-
explicit constexpr operator uint64_t() const noexcept
103+
constexpr std::uint64_t Get() const noexcept
84104
{
85-
return SwapBytes((be_value_ & SwapBytes(uint64_t(~(~0x3Full << 56)))) << (sizeof(uint64_t) - Size()) * 8);
105+
return SwapBytes((std::bit_cast<std::uint64_t>(be_value_) & SwapBytes(uint64_t(~(~0x3Full << 56))))
106+
<< (sizeof(uint64_t) - Size()) * 8);
86107
}
87108

88109
static constexpr std::size_t Size(uint8_t msb_bytes) noexcept
@@ -98,16 +119,20 @@ namespace quicr {
98119
return sizeof(uint8_t);
99120
}
100121

101-
constexpr std::size_t Size() const noexcept { return UintVar::Size(static_cast<uint8_t>(be_value_)); }
122+
constexpr std::size_t Size() const noexcept { return UintVar::Size(be_value_.front()); }
102123

103124
// NOLINTBEGIN(readability-identifier-naming)
104-
auto data() const noexcept { return reinterpret_cast<const uint8_t*>(&be_value_); }
125+
constexpr const std::uint8_t* data() const noexcept { return be_value_.data(); }
105126
constexpr std::size_t size() const noexcept { return Size(); }
106-
auto begin() const noexcept { return data(); }
107-
auto end() const noexcept { return data() + Size(); }
127+
constexpr auto begin() const noexcept { return data(); }
128+
constexpr auto end() const noexcept { return data() + Size(); }
108129
// NOLINTEND(readability-identifier-naming)
109130

131+
explicit constexpr operator uint64_t() const noexcept { return Get(); }
132+
133+
constexpr auto operator<=>(const UintVar&) const noexcept = default;
134+
110135
private:
111-
uint64_t be_value_;
136+
std::array<std::uint8_t, sizeof(std::uint64_t)> be_value_;
112137
};
113138
}

test/uintvar.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ namespace var {
2222
const std::vector<uint8_t> kValue8ByteEncoded = { 0xC0, 0, 0, 0x1, 0x23, 0x45, 0x67, 0x89 };
2323
}
2424

25+
TEST_CASE("Check UintVar")
26+
{
27+
CHECK_EQ(sizeof(std::uint64_t), sizeof(quicr::UintVar));
28+
CHECK(std::is_trivially_copy_constructible_v<quicr::UintVar>);
29+
CHECK(std::is_trivially_copy_assignable_v<quicr::UintVar>);
30+
CHECK(std::is_trivially_move_constructible_v<quicr::UintVar>);
31+
CHECK(std::is_trivially_move_assignable_v<quicr::UintVar>);
32+
}
33+
2534
TEST_CASE("Encode/Decode UintVar Uint64")
2635
{
2736
CHECK_EQ(var::kValue1Byte, uint64_t(quicr::UintVar(var::kValue1Byte)));

0 commit comments

Comments
 (0)