diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel index 5dc0a64bf5..ae7428e838 100644 --- a/src/google/protobuf/BUILD.bazel +++ b/src/google/protobuf/BUILD.bazel @@ -533,6 +533,17 @@ cc_library( ], ) +cc_test( + name = "has_bits_test", + srcs = ["has_bits_test.cc"], + copts = COPTS, + deps = [ + ":protobuf_lite", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "protobuf", copts = COPTS, diff --git a/src/google/protobuf/has_bits.h b/src/google/protobuf/has_bits.h index 75aac3e1f6..b6cbac693d 100644 --- a/src/google/protobuf/has_bits.h +++ b/src/google/protobuf/has_bits.h @@ -31,6 +31,8 @@ #ifndef GOOGLE_PROTOBUF_HAS_BITS_H__ #define GOOGLE_PROTOBUF_HAS_BITS_H__ +#include + #include "google/protobuf/stubs/common.h" #include "google/protobuf/port.h" @@ -50,6 +52,10 @@ class HasBits { public: PROTOBUF_NDEBUG_INLINE constexpr HasBits() : has_bits_{} {} + constexpr HasBits(std::initializer_list has_bits) : has_bits_{} { + Copy(has_bits_, &*has_bits.begin(), has_bits.size()); + } + PROTOBUF_NDEBUG_INLINE void Clear() { memset(has_bits_, 0, sizeof(has_bits_)); } @@ -77,6 +83,21 @@ class HasBits { bool empty() const; private: + // Unfortunately, older GCC compilers (and perhaps others) fail on initializer + // arguments for an std::array<> or any type of array constructor. Below is a + // handrolled constexpr 'Copy' function that we use to make a constexpr + // constructor that accepts a `std::initializer` list. + static inline constexpr void Copy(uint32_t* dst, const uint32_t* src, + size_t n) { + assert(n <= doublewords); + for (size_t ix = 0; ix < n; ++ix) { + dst[ix] = src[ix]; + } + for (size_t ix = n; ix < doublewords; ++ix) { + dst[ix] = 0; + } + } + uint32_t has_bits_[doublewords]; }; @@ -102,8 +123,8 @@ inline bool HasBits<4>::empty() const { template inline bool HasBits::empty() const { - for (int i = 0; i < doublewords; ++i) { - if (has_bits_[i]) return false; + for (uint32_t bits : has_bits_) { + if (bits) return false; } return true; } diff --git a/src/google/protobuf/has_bits_test.cc b/src/google/protobuf/has_bits_test.cc new file mode 100644 index 0000000000..f58fce44f1 --- /dev/null +++ b/src/google/protobuf/has_bits_test.cc @@ -0,0 +1,131 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "google/protobuf/has_bits.h" + +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +using ::testing::Eq; + +template +void TestDefaultInit() { + HasBits bits; + EXPECT_TRUE(bits.empty()); + for (int i = 0; i < n; ++i) { + EXPECT_THAT(bits[i], Eq(0)); + } +} + +TEST(HasBits, DefaultInit) { + TestDefaultInit<1>(); + TestDefaultInit<2>(); + TestDefaultInit<3>(); + TestDefaultInit<4>(); +} + +TEST(HasBits, ValueInit) { + { + HasBits<4> bits; + EXPECT_TRUE(bits.empty()); + } + { + HasBits<4> bits({}); + EXPECT_TRUE(bits.empty()); + } + { + HasBits<4> bits({1}); + EXPECT_FALSE(bits.empty()); + EXPECT_THAT(bits[0], Eq(1)); + } + { + HasBits<4> bits({1, 2, 3, 4}); + EXPECT_FALSE(bits.empty()); + EXPECT_THAT(bits[0], Eq(1)); + EXPECT_THAT(bits[1], Eq(2)); + EXPECT_THAT(bits[2], Eq(3)); + EXPECT_THAT(bits[3], Eq(4)); + } +} + +TEST(HasBits, ConstexprValueInit) { + { + constexpr HasBits<4> bits; + EXPECT_TRUE(bits.empty()); + } + { + constexpr HasBits<4> bits({}); + EXPECT_TRUE(bits.empty()); + } + { + constexpr HasBits<4> bits({1}); + EXPECT_FALSE(bits.empty()); + EXPECT_THAT(bits[0], Eq(1)); + } + { + constexpr HasBits<4> bits({1, 2, 3, 4}); + EXPECT_FALSE(bits.empty()); + EXPECT_THAT(bits[0], Eq(1)); + EXPECT_THAT(bits[1], Eq(2)); + EXPECT_THAT(bits[2], Eq(3)); + EXPECT_THAT(bits[3], Eq(4)); + } +} + +TEST(HasBits, operator_equal) { + EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({0, 2, 3, 4})); + EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 0, 3, 4})); + EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 0, 4})); + EXPECT_FALSE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 3, 0})); + EXPECT_TRUE(HasBits<4>({1, 2, 3, 4}) == HasBits<4>({1, 2, 3, 4})); +} + +TEST(HasBits, Or) { + HasBits<4> bits1({1, 2, 4, 8}); + HasBits<4> bits2({16, 32, 64, 128}); + bits1.Or(bits2); + EXPECT_TRUE(bits1 == HasBits<4>({17, 34, 68, 136})); +} + +TEST(HasBits, Copy) { + HasBits<4> bits1({1, 2, 4, 8}); + HasBits<4> bits2(bits1); + EXPECT_TRUE(bits1 == bits2); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google