// Copyright 2018 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "absl/container/internal/layout.h" // We need ::max_align_t because some libstdc++ versions don't provide // std::max_align_t #include #include #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/log/check.h" #include "absl/types/span.h" #include "absl/utility/utility.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { namespace { using ::absl::Span; using ::testing::ElementsAre; size_t Distance(const void* from, const void* to) { CHECK_LE(from, to) << "Distance must be non-negative"; return static_cast(to) - static_cast(from); } template Expected Type(Actual val) { static_assert(std::is_same(), ""); return val; } // Helper classes to test different size and alignments. struct alignas(8) Int128 { uint64_t a, b; friend bool operator==(Int128 lhs, Int128 rhs) { return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b); } static std::string Name() { return internal_layout::adl_barrier::TypeName(); } }; // int64_t is *not* 8-byte aligned on all platforms! struct alignas(8) Int64 { int64_t a; friend bool operator==(Int64 lhs, Int64 rhs) { return lhs.a == rhs.a; } }; // Properties of types that this test relies on. static_assert(sizeof(int8_t) == 1, ""); static_assert(alignof(int8_t) == 1, ""); static_assert(sizeof(int16_t) == 2, ""); static_assert(alignof(int16_t) == 2, ""); static_assert(sizeof(int32_t) == 4, ""); static_assert(alignof(int32_t) == 4, ""); static_assert(sizeof(Int64) == 8, ""); static_assert(alignof(Int64) == 8, ""); static_assert(sizeof(Int128) == 16, ""); static_assert(alignof(Int128) == 8, ""); template void SameType() { static_assert(std::is_same(), ""); } TEST(Layout, ElementType) { { using L = Layout; SameType>(); SameType>(); SameType>(); } { using L = Layout; SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); } { using L = Layout; SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); SameType>(); } } TEST(Layout, ElementTypes) { { using L = Layout; SameType, L::ElementTypes>(); SameType, decltype(L::Partial())::ElementTypes>(); SameType, decltype(L::Partial(0))::ElementTypes>(); } { using L = Layout; SameType, L::ElementTypes>(); SameType, decltype(L::Partial())::ElementTypes>(); SameType, decltype(L::Partial(0))::ElementTypes>(); } { using L = Layout; SameType, L::ElementTypes>(); SameType, decltype(L::Partial())::ElementTypes>(); SameType, decltype(L::Partial(0))::ElementTypes>(); SameType, decltype(L::Partial(0, 0))::ElementTypes>(); SameType, decltype(L::Partial(0, 0, 0))::ElementTypes>(); } } TEST(Layout, OffsetByIndex) { { using L = Layout; EXPECT_EQ(0, L::Partial().Offset<0>()); EXPECT_EQ(0, L::Partial(3).Offset<0>()); EXPECT_EQ(0, L(3).Offset<0>()); } { using L = Layout; EXPECT_EQ(0, L::Partial().Offset<0>()); EXPECT_EQ(0, L::Partial(3).Offset<0>()); EXPECT_EQ(12, L::Partial(3).Offset<1>()); EXPECT_EQ(0, L::Partial(3, 5).Offset<0>()); EXPECT_EQ(12, L::Partial(3, 5).Offset<1>()); EXPECT_EQ(0, L(3, 5).Offset<0>()); EXPECT_EQ(12, L(3, 5).Offset<1>()); } { using L = Layout; EXPECT_EQ(0, L::Partial().Offset<0>()); EXPECT_EQ(0, L::Partial(0).Offset<0>()); EXPECT_EQ(0, L::Partial(0).Offset<1>()); EXPECT_EQ(0, L::Partial(1).Offset<0>()); EXPECT_EQ(4, L::Partial(1).Offset<1>()); EXPECT_EQ(0, L::Partial(5).Offset<0>()); EXPECT_EQ(8, L::Partial(5).Offset<1>()); EXPECT_EQ(0, L::Partial(0, 0).Offset<0>()); EXPECT_EQ(0, L::Partial(0, 0).Offset<1>()); EXPECT_EQ(0, L::Partial(0, 0).Offset<2>()); EXPECT_EQ(0, L::Partial(1, 0).Offset<0>()); EXPECT_EQ(4, L::Partial(1, 0).Offset<1>()); EXPECT_EQ(8, L::Partial(1, 0).Offset<2>()); EXPECT_EQ(0, L::Partial(5, 3).Offset<0>()); EXPECT_EQ(8, L::Partial(5, 3).Offset<1>()); EXPECT_EQ(24, L::Partial(5, 3).Offset<2>()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<0>()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<1>()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<2>()); EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<0>()); EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<1>()); EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<2>()); EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<0>()); EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<2>()); EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<1>()); EXPECT_EQ(0, L(5, 3, 1).Offset<0>()); EXPECT_EQ(24, L(5, 3, 1).Offset<2>()); EXPECT_EQ(8, L(5, 3, 1).Offset<1>()); } } TEST(Layout, OffsetByType) { { using L = Layout; EXPECT_EQ(0, L::Partial().Offset()); EXPECT_EQ(0, L::Partial(3).Offset()); EXPECT_EQ(0, L(3).Offset()); } { using L = Layout; EXPECT_EQ(0, L::Partial().Offset()); EXPECT_EQ(0, L::Partial(0).Offset()); EXPECT_EQ(0, L::Partial(0).Offset()); EXPECT_EQ(0, L::Partial(1).Offset()); EXPECT_EQ(4, L::Partial(1).Offset()); EXPECT_EQ(0, L::Partial(5).Offset()); EXPECT_EQ(8, L::Partial(5).Offset()); EXPECT_EQ(0, L::Partial(0, 0).Offset()); EXPECT_EQ(0, L::Partial(0, 0).Offset()); EXPECT_EQ(0, L::Partial(0, 0).Offset()); EXPECT_EQ(0, L::Partial(1, 0).Offset()); EXPECT_EQ(4, L::Partial(1, 0).Offset()); EXPECT_EQ(8, L::Partial(1, 0).Offset()); EXPECT_EQ(0, L::Partial(5, 3).Offset()); EXPECT_EQ(8, L::Partial(5, 3).Offset()); EXPECT_EQ(24, L::Partial(5, 3).Offset()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); EXPECT_EQ(0, L::Partial(0, 0, 0).Offset()); EXPECT_EQ(0, L::Partial(1, 0, 0).Offset()); EXPECT_EQ(4, L::Partial(1, 0, 0).Offset()); EXPECT_EQ(8, L::Partial(1, 0, 0).Offset()); EXPECT_EQ(0, L::Partial(5, 3, 1).Offset()); EXPECT_EQ(24, L::Partial(5, 3, 1).Offset()); EXPECT_EQ(8, L::Partial(5, 3, 1).Offset()); EXPECT_EQ(0, L(5, 3, 1).Offset()); EXPECT_EQ(24, L(5, 3, 1).Offset()); EXPECT_EQ(8, L(5, 3, 1).Offset()); } } TEST(Layout, Offsets) { { using L = Layout; EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0)); EXPECT_THAT(L(3).Offsets(), ElementsAre(0)); } { using L = Layout; EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0, 12)); EXPECT_THAT(L::Partial(3, 5).Offsets(), ElementsAre(0, 12)); EXPECT_THAT(L(3, 5).Offsets(), ElementsAre(0, 12)); } { using L = Layout; EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0)); EXPECT_THAT(L::Partial(1).Offsets(), ElementsAre(0, 4)); EXPECT_THAT(L::Partial(5).Offsets(), ElementsAre(0, 8)); EXPECT_THAT(L::Partial(0, 0).Offsets(), ElementsAre(0, 0, 0)); EXPECT_THAT(L::Partial(1, 0).Offsets(), ElementsAre(0, 4, 8)); EXPECT_THAT(L::Partial(5, 3).Offsets(), ElementsAre(0, 8, 24)); EXPECT_THAT(L::Partial(0, 0, 0).Offsets(), ElementsAre(0, 0, 0)); EXPECT_THAT(L::Partial(1, 0, 0).Offsets(), ElementsAre(0, 4, 8)); EXPECT_THAT(L::Partial(5, 3, 1).Offsets(), ElementsAre(0, 8, 24)); EXPECT_THAT(L(5, 3, 1).Offsets(), ElementsAre(0, 8, 24)); } } TEST(Layout, AllocSize) { { using L = Layout; EXPECT_EQ(0, L::Partial(0).AllocSize()); EXPECT_EQ(12, L::Partial(3).AllocSize()); EXPECT_EQ(12, L(3).AllocSize()); } { using L = Layout; EXPECT_EQ(32, L::Partial(3, 5).AllocSize()); EXPECT_EQ(32, L(3, 5).AllocSize()); } { using L = Layout; EXPECT_EQ(0, L::Partial(0, 0, 0).AllocSize()); EXPECT_EQ(8, L::Partial(1, 0, 0).AllocSize()); EXPECT_EQ(8, L::Partial(0, 1, 0).AllocSize()); EXPECT_EQ(16, L::Partial(0, 0, 1).AllocSize()); EXPECT_EQ(24, L::Partial(1, 1, 1).AllocSize()); EXPECT_EQ(136, L::Partial(3, 5, 7).AllocSize()); EXPECT_EQ(136, L(3, 5, 7).AllocSize()); } } TEST(Layout, SizeByIndex) { { using L = Layout; EXPECT_EQ(0, L::Partial(0).Size<0>()); EXPECT_EQ(3, L::Partial(3).Size<0>()); EXPECT_EQ(3, L(3).Size<0>()); } { using L = Layout; EXPECT_EQ(0, L::Partial(0).Size<0>()); EXPECT_EQ(3, L::Partial(3).Size<0>()); EXPECT_EQ(3, L::Partial(3, 5).Size<0>()); EXPECT_EQ(5, L::Partial(3, 5).Size<1>()); EXPECT_EQ(3, L(3, 5).Size<0>()); EXPECT_EQ(5, L(3, 5).Size<1>()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Size<0>()); EXPECT_EQ(3, L::Partial(3, 5).Size<0>()); EXPECT_EQ(5, L::Partial(3, 5).Size<1>()); EXPECT_EQ(3, L::Partial(3, 5, 7).Size<0>()); EXPECT_EQ(5, L::Partial(3, 5, 7).Size<1>()); EXPECT_EQ(7, L::Partial(3, 5, 7).Size<2>()); EXPECT_EQ(3, L(3, 5, 7).Size<0>()); EXPECT_EQ(5, L(3, 5, 7).Size<1>()); EXPECT_EQ(7, L(3, 5, 7).Size<2>()); } } TEST(Layout, SizeByType) { { using L = Layout; EXPECT_EQ(0, L::Partial(0).Size()); EXPECT_EQ(3, L::Partial(3).Size()); EXPECT_EQ(3, L(3).Size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Size()); EXPECT_EQ(3, L::Partial(3, 5).Size()); EXPECT_EQ(5, L::Partial(3, 5).Size()); EXPECT_EQ(3, L::Partial(3, 5, 7).Size()); EXPECT_EQ(5, L::Partial(3, 5, 7).Size()); EXPECT_EQ(7, L::Partial(3, 5, 7).Size()); EXPECT_EQ(3, L(3, 5, 7).Size()); EXPECT_EQ(5, L(3, 5, 7).Size()); EXPECT_EQ(7, L(3, 5, 7).Size()); } } TEST(Layout, Sizes) { { using L = Layout; EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); EXPECT_THAT(L(3).Sizes(), ElementsAre(3)); } { using L = Layout; EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5)); EXPECT_THAT(L(3, 5).Sizes(), ElementsAre(3, 5)); } { using L = Layout; EXPECT_THAT(L::Partial().Sizes(), ElementsAre()); EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3)); EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5)); EXPECT_THAT(L::Partial(3, 5, 7).Sizes(), ElementsAre(3, 5, 7)); EXPECT_THAT(L(3, 5, 7).Sizes(), ElementsAre(3, 5, 7)); } } TEST(Layout, PointerByIndex) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L(3).Pointer<0>(p)))); } { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type(L::Partial(3).Pointer<1>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(3, 5).Pointer<0>(p)))); EXPECT_EQ( 12, Distance(p, Type(L::Partial(3, 5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L(3, 5).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type(L(3, 5).Pointer<1>(p)))); } { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer<0>(p)))); EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer<0>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<0>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0).Pointer<0>(p)))); EXPECT_EQ( 4, Distance(p, Type(L::Partial(1, 0).Pointer<1>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3).Pointer<0>(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(5, 3).Pointer<1>(p)))); EXPECT_EQ(24, Distance(p, Type(L::Partial(5, 3).Pointer<2>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<0>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<1>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<2>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(1, 0, 0).Pointer<0>(p)))); EXPECT_EQ( 4, Distance(p, Type(L::Partial(1, 0, 0).Pointer<1>(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(1, 0, 0).Pointer<2>(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ( 24, Distance(p, Type(L::Partial(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(5, 3, 1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer<1>(p)))); } } TEST(Layout, PointerByType) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ( 0, Distance(p, Type(L::Partial().Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(3).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L(3).Pointer(p)))); } { using L = Layout; EXPECT_EQ( 0, Distance(p, Type(L::Partial().Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(1).Pointer(p)))); EXPECT_EQ( 4, Distance(p, Type(L::Partial(1).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(5).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(5).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); EXPECT_EQ(4, Distance(p, Type( L::Partial(1, 0).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type( L::Partial(5, 3).Pointer(p)))); EXPECT_EQ( 24, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ(4, Distance(p, Type( L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type( L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type( L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ(24, Distance(p, Type( L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type( L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L(5, 3, 1).Pointer(p)))); } } TEST(Layout, MutablePointerByIndex) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L(3).Pointer<0>(p)))); } { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type(L::Partial(3).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3, 5).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type(L::Partial(3, 5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L(3, 5).Pointer<0>(p)))); EXPECT_EQ(12, Distance(p, Type(L(3, 5).Pointer<1>(p)))); } { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer<0>(p)))); EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer<0>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0).Pointer<0>(p)))); EXPECT_EQ(4, Distance(p, Type(L::Partial(1, 0).Pointer<1>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3).Pointer<0>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(5, 3).Pointer<1>(p)))); EXPECT_EQ(24, Distance(p, Type(L::Partial(5, 3).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<0>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0, 0).Pointer<0>(p)))); EXPECT_EQ(4, Distance(p, Type(L::Partial(1, 0, 0).Pointer<1>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0, 0).Pointer<2>(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type(L::Partial(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(5, 3, 1).Pointer<1>(p)))); EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer<0>(p)))); EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer<2>(p)))); EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer<1>(p)))); } } TEST(Layout, MutablePointerByType) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(3).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L(3).Pointer(p)))); } { using L = Layout; EXPECT_EQ(0, Distance(p, Type(L::Partial().Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1).Pointer(p)))); EXPECT_EQ(4, Distance(p, Type(L::Partial(1).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(5).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(0, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); EXPECT_EQ( 4, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type(L::Partial(1, 0).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); EXPECT_EQ(24, Distance(p, Type(L::Partial(5, 3).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(0, 0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ( 4, Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(1, 0, 0).Pointer(p)))); EXPECT_EQ( 0, Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ( 24, Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ( 8, Distance(p, Type(L::Partial(5, 3, 1).Pointer(p)))); EXPECT_EQ(0, Distance(p, Type(L(5, 3, 1).Pointer(p)))); EXPECT_EQ(24, Distance(p, Type(L(5, 3, 1).Pointer(p)))); EXPECT_EQ(8, Distance(p, Type(L(5, 3, 1).Pointer(p)))); } } TEST(Layout, Pointers) { alignas(max_align_t) const unsigned char p[100] = {0}; using L = Layout; { const auto x = L::Partial(); EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)), Type>(x.Pointers(p))); } { const auto x = L::Partial(1); EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)), (Type>(x.Pointers(p)))); } { const auto x = L::Partial(1, 2); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>( x.Pointers(p)))); } { const auto x = L::Partial(1, 2, 3); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>( x.Pointers(p)))); } { const L x(1, 2, 3); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>( x.Pointers(p)))); } } TEST(Layout, MutablePointers) { alignas(max_align_t) unsigned char p[100] = {0}; using L = Layout; { const auto x = L::Partial(); EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)), Type>(x.Pointers(p))); } { const auto x = L::Partial(1); EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)), (Type>(x.Pointers(p)))); } { const auto x = L::Partial(1, 2); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>(x.Pointers(p)))); } { const auto x = L::Partial(1, 2, 3); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>(x.Pointers(p)))); } { const L x(1, 2, 3); EXPECT_EQ( std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)), (Type>(x.Pointers(p)))); } } TEST(Layout, SliceByIndexSize) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size()); EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(3, L(3).Slice<0>(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); EXPECT_EQ(5, L(3, 5).Slice<1>(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size()); EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size()); EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size()); EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size()); EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size()); } } TEST(Layout, SliceByTypeSize) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, L::Partial(0).Slice(p).size()); EXPECT_EQ(3, L::Partial(3).Slice(p).size()); EXPECT_EQ(3, L(3).Slice(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice(p).size()); EXPECT_EQ(3, L::Partial(3, 5).Slice(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice(p).size()); EXPECT_EQ(3, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(5, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(7, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(3, L(3, 5, 7).Slice(p).size()); EXPECT_EQ(5, L(3, 5, 7).Slice(p).size()); EXPECT_EQ(7, L(3, 5, 7).Slice(p).size()); } } TEST(Layout, MutableSliceByIndexSize) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size()); EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(3, L(3).Slice<0>(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); EXPECT_EQ(5, L(3, 5).Slice<1>(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size()); EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size()); EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size()); EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size()); EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size()); EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size()); EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size()); EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size()); } } TEST(Layout, MutableSliceByTypeSize) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ(0, L::Partial(0).Slice(p).size()); EXPECT_EQ(3, L::Partial(3).Slice(p).size()); EXPECT_EQ(3, L(3).Slice(p).size()); } { using L = Layout; EXPECT_EQ(3, L::Partial(3).Slice(p).size()); EXPECT_EQ(3, L::Partial(3, 5).Slice(p).size()); EXPECT_EQ(5, L::Partial(3, 5).Slice(p).size()); EXPECT_EQ(3, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(5, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(7, L::Partial(3, 5, 7).Slice(p).size()); EXPECT_EQ(3, L(3, 5, 7).Slice(p).size()); EXPECT_EQ(5, L(3, 5, 7).Slice(p).size()); EXPECT_EQ(7, L(3, 5, 7).Slice(p).size()); } } TEST(Layout, SliceByIndexData) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ(0, Distance(p, Type>(L(3).Slice<0>(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(3, 5).Slice<0>(p)).data())); EXPECT_EQ( 12, Distance( p, Type>(L::Partial(3, 5).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L(3, 5).Slice<0>(p)).data())); EXPECT_EQ( 12, Distance(p, Type>(L(3, 5).Slice<1>(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, Distance( p, Type>(L::Partial(1, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5, 3).Slice<0>(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(5, 3).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, Distance( p, Type>(L::Partial(1, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(1, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance( p, Type>(L::Partial(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(5, 3, 1).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance(p, Type>(L(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ( 8, Distance(p, Type>(L(5, 3, 1).Slice<1>(p)).data())); } } TEST(Layout, SliceByTypeData) { alignas(max_align_t) const unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(3).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L(3).Slice(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0, 0).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(0, 0).Slice(p)) .data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(1, 0).Slice(p)) .data())); EXPECT_EQ(4, Distance(p, Type>( L::Partial(1, 0).Slice(p)) .data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(5, 3).Slice(p)) .data())); EXPECT_EQ(8, Distance(p, Type>( L::Partial(5, 3).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(0, 0, 0).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(0, 0, 0).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(0, 0, 0).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(1, 0, 0).Slice(p)) .data())); EXPECT_EQ(4, Distance(p, Type>( L::Partial(1, 0, 0).Slice(p)) .data())); EXPECT_EQ(8, Distance(p, Type>( L::Partial(1, 0, 0).Slice(p)) .data())); EXPECT_EQ(0, Distance(p, Type>( L::Partial(5, 3, 1).Slice(p)) .data())); EXPECT_EQ(24, Distance(p, Type>( L::Partial(5, 3, 1).Slice(p)) .data())); EXPECT_EQ(8, Distance(p, Type>( L::Partial(5, 3, 1).Slice(p)) .data())); EXPECT_EQ( 0, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 24, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L(5, 3, 1).Slice(p)).data())); } } TEST(Layout, MutableSliceByIndexData) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ(0, Distance(p, Type>(L(3).Slice<0>(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance(p, Type>(L::Partial(3).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(3, 5).Slice<0>(p)).data())); EXPECT_EQ( 12, Distance(p, Type>(L::Partial(3, 5).Slice<1>(p)).data())); EXPECT_EQ(0, Distance(p, Type>(L(3, 5).Slice<0>(p)).data())); EXPECT_EQ(12, Distance(p, Type>(L(3, 5).Slice<1>(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(1).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(5).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0, 0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(1, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, Distance(p, Type>(L::Partial(1, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(5, 3).Slice<0>(p)).data())); EXPECT_EQ( 8, Distance(p, Type>(L::Partial(5, 3).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1, 0, 0).Slice<0>(p)).data())); EXPECT_EQ( 4, Distance( p, Type>(L::Partial(1, 0, 0).Slice<1>(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(1, 0, 0).Slice<2>(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ( 24, Distance( p, Type>(L::Partial(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(5, 3, 1).Slice<1>(p)).data())); EXPECT_EQ(0, Distance(p, Type>(L(5, 3, 1).Slice<0>(p)).data())); EXPECT_EQ(24, Distance(p, Type>(L(5, 3, 1).Slice<2>(p)).data())); EXPECT_EQ(8, Distance(p, Type>(L(5, 3, 1).Slice<1>(p)).data())); } } TEST(Layout, MutableSliceByTypeData) { alignas(max_align_t) unsigned char p[100] = {0}; { using L = Layout; EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(3).Slice(p)).data())); EXPECT_EQ(0, Distance(p, Type>(L(3).Slice(p)).data())); } { using L = Layout; EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(1).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(5).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(1, 0).Slice(p)).data())); EXPECT_EQ( 4, Distance( p, Type>(L::Partial(1, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L::Partial(5, 3).Slice(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(5, 3).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(0, 0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(1, 0, 0).Slice(p)).data())); EXPECT_EQ( 4, Distance( p, Type>(L::Partial(1, 0, 0).Slice(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(1, 0, 0).Slice(p)).data())); EXPECT_EQ( 0, Distance( p, Type>(L::Partial(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 24, Distance( p, Type>(L::Partial(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 8, Distance( p, Type>(L::Partial(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 0, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 24, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); EXPECT_EQ( 8, Distance(p, Type>(L(5, 3, 1).Slice(p)).data())); } } MATCHER_P(IsSameSlice, slice, "") { return arg.size() == slice.size() && arg.data() == slice.data(); } template class TupleMatcher { public: explicit TupleMatcher(M... matchers) : matchers_(std::move(matchers)...) {} template bool MatchAndExplain(const Tuple& p, testing::MatchResultListener* /* listener */) const { static_assert(std::tuple_size::value == sizeof...(M), ""); return MatchAndExplainImpl( p, absl::make_index_sequence::value>{}); } // For the matcher concept. Left empty as we don't really need the diagnostics // right now. void DescribeTo(::std::ostream* os) const {} void DescribeNegationTo(::std::ostream* os) const {} private: template bool MatchAndExplainImpl(const Tuple& p, absl::index_sequence) const { // Using std::min as a simple variadic "and". return std::min( {true, testing::SafeMatcherCast< const typename std::tuple_element::type&>( std::get(matchers_)) .Matches(std::get(p))...}); } std::tuple matchers_; }; template testing::PolymorphicMatcher> Tuple(M... matchers) { return testing::MakePolymorphicMatcher( TupleMatcher(std::move(matchers)...)); } TEST(Layout, Slices) { alignas(max_align_t) const unsigned char p[100] = {0}; using L = Layout; { const auto x = L::Partial(); EXPECT_THAT(Type>(x.Slices(p)), Tuple()); } { const auto x = L::Partial(1); EXPECT_THAT(Type>>(x.Slices(p)), Tuple(IsSameSlice(x.Slice<0>(p)))); } { const auto x = L::Partial(1, 2); EXPECT_THAT( (Type, Span>>(x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)))); } { const auto x = L::Partial(1, 2, 3); EXPECT_THAT((Type, Span, Span>>(x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), IsSameSlice(x.Slice<2>(p)))); } { const L x(1, 2, 3); EXPECT_THAT((Type, Span, Span>>(x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), IsSameSlice(x.Slice<2>(p)))); } } TEST(Layout, MutableSlices) { alignas(max_align_t) unsigned char p[100] = {0}; using L = Layout; { const auto x = L::Partial(); EXPECT_THAT(Type>(x.Slices(p)), Tuple()); } { const auto x = L::Partial(1); EXPECT_THAT(Type>>(x.Slices(p)), Tuple(IsSameSlice(x.Slice<0>(p)))); } { const auto x = L::Partial(1, 2); EXPECT_THAT((Type, Span>>(x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)))); } { const auto x = L::Partial(1, 2, 3); EXPECT_THAT((Type, Span, Span>>( x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), IsSameSlice(x.Slice<2>(p)))); } { const L x(1, 2, 3); EXPECT_THAT((Type, Span, Span>>( x.Slices(p))), Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)), IsSameSlice(x.Slice<2>(p)))); } } TEST(Layout, UnalignedTypes) { constexpr Layout x(1, 2, 3); alignas(max_align_t) unsigned char p[x.AllocSize() + 1]; EXPECT_THAT(x.Pointers(p + 1), Tuple(p + 1, p + 2, p + 4)); } TEST(Layout, CustomAlignment) { constexpr Layout> x(1, 2); alignas(max_align_t) unsigned char p[x.AllocSize()]; EXPECT_EQ(10, x.AllocSize()); EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 8)); } TEST(Layout, OverAligned) { constexpr size_t M = alignof(max_align_t); constexpr Layout> x(1, 3); #ifdef __GNUC__ // Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357 __attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()]; #else alignas(2 * M) unsigned char p[x.AllocSize()]; #endif EXPECT_EQ(2 * M + 3, x.AllocSize()); EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M)); } TEST(Layout, Alignment) { static_assert(Layout::Alignment() == 1, ""); static_assert(Layout::Alignment() == 4, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout>::Alignment() == 64, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout::Alignment() == 8, ""); static_assert(Layout::Alignment() == 8, ""); } TEST(Layout, ConstexprPartial) { constexpr size_t M = alignof(max_align_t); constexpr Layout> x(1, 3); static_assert(x.Partial(1).template Offset<1>() == 2 * M, ""); } // [from, to) struct Region { size_t from; size_t to; }; void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) { #ifdef ABSL_HAVE_ADDRESS_SANITIZER for (size_t i = 0; i != n; ++i) { EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i)); } #endif } template void ExpectPoisoned(const unsigned char (&buf)[N], std::initializer_list reg) { size_t prev = 0; for (const Region& r : reg) { ExpectRegionPoisoned(buf + prev, r.from - prev, false); ExpectRegionPoisoned(buf + r.from, r.to - r.from, true); prev = r.to; } ExpectRegionPoisoned(buf + prev, N - prev, false); } TEST(Layout, PoisonPadding) { using L = Layout; constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize(); { constexpr auto x = L::Partial(); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {}); } { constexpr auto x = L::Partial(1); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {{1, 8}}); } { constexpr auto x = L::Partial(1, 2); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {{1, 8}}); } { constexpr auto x = L::Partial(1, 2, 3); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {{1, 8}, {36, 40}}); } { constexpr auto x = L::Partial(1, 2, 3, 4); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {{1, 8}, {36, 40}}); } { constexpr L x(1, 2, 3, 4); alignas(max_align_t) const unsigned char c[n] = {}; x.PoisonPadding(c); EXPECT_EQ(x.Slices(c), x.Slices(c)); ExpectPoisoned(c, {{1, 8}, {36, 40}}); } } TEST(Layout, DebugString) { { constexpr auto x = Layout::Partial(); EXPECT_EQ("@0(1)", x.DebugString()); } { constexpr auto x = Layout::Partial(1); EXPECT_EQ("@0(1)[1]; @4(4)", x.DebugString()); } { constexpr auto x = Layout::Partial(1, 2); EXPECT_EQ("@0(1)[1]; @4(4)[2]; @12(1)", x.DebugString()); } { constexpr auto x = Layout::Partial(1, 2, 3); EXPECT_EQ( "@0(1)[1]; @4(4)[2]; @12(1)[3]; " "@16" + Int128::Name() + "(16)", x.DebugString()); } { constexpr auto x = Layout::Partial(1, 2, 3, 4); EXPECT_EQ( "@0(1)[1]; @4(4)[2]; @12(1)[3]; " "@16" + Int128::Name() + "(16)[4]", x.DebugString()); } { constexpr Layout x(1, 2, 3, 4); EXPECT_EQ( "@0(1)[1]; @4(4)[2]; @12(1)[3]; " "@16" + Int128::Name() + "(16)[4]", x.DebugString()); } } TEST(Layout, CharTypes) { constexpr Layout x(1); alignas(max_align_t) char c[x.AllocSize()] = {}; alignas(max_align_t) unsigned char uc[x.AllocSize()] = {}; alignas(max_align_t) signed char sc[x.AllocSize()] = {}; alignas(max_align_t) const char cc[x.AllocSize()] = {}; alignas(max_align_t) const unsigned char cuc[x.AllocSize()] = {}; alignas(max_align_t) const signed char csc[x.AllocSize()] = {}; Type(x.Pointer<0>(c)); Type(x.Pointer<0>(uc)); Type(x.Pointer<0>(sc)); Type(x.Pointer<0>(cc)); Type(x.Pointer<0>(cuc)); Type(x.Pointer<0>(csc)); Type(x.Pointer(c)); Type(x.Pointer(uc)); Type(x.Pointer(sc)); Type(x.Pointer(cc)); Type(x.Pointer(cuc)); Type(x.Pointer(csc)); Type>(x.Pointers(c)); Type>(x.Pointers(uc)); Type>(x.Pointers(sc)); Type>(x.Pointers(cc)); Type>(x.Pointers(cuc)); Type>(x.Pointers(csc)); Type>(x.Slice<0>(c)); Type>(x.Slice<0>(uc)); Type>(x.Slice<0>(sc)); Type>(x.Slice<0>(cc)); Type>(x.Slice<0>(cuc)); Type>(x.Slice<0>(csc)); Type>>(x.Slices(c)); Type>>(x.Slices(uc)); Type>>(x.Slices(sc)); Type>>(x.Slices(cc)); Type>>(x.Slices(cuc)); Type>>(x.Slices(csc)); } TEST(Layout, ConstElementType) { constexpr Layout x(1); alignas(int32_t) char c[x.AllocSize()] = {}; const char* cc = c; const int32_t* p = reinterpret_cast(cc); EXPECT_EQ(alignof(int32_t), x.Alignment()); EXPECT_EQ(0, x.Offset<0>()); EXPECT_EQ(0, x.Offset()); EXPECT_THAT(x.Offsets(), ElementsAre(0)); EXPECT_EQ(1, x.Size<0>()); EXPECT_EQ(1, x.Size()); EXPECT_THAT(x.Sizes(), ElementsAre(1)); EXPECT_EQ(sizeof(int32_t), x.AllocSize()); EXPECT_EQ(p, Type(x.Pointer<0>(c))); EXPECT_EQ(p, Type(x.Pointer<0>(cc))); EXPECT_EQ(p, Type(x.Pointer(c))); EXPECT_EQ(p, Type(x.Pointer(cc))); EXPECT_THAT(Type>(x.Pointers(c)), Tuple(p)); EXPECT_THAT(Type>(x.Pointers(cc)), Tuple(p)); EXPECT_THAT(Type>(x.Slice<0>(c)), IsSameSlice(Span(p, 1))); EXPECT_THAT(Type>(x.Slice<0>(cc)), IsSameSlice(Span(p, 1))); EXPECT_THAT(Type>(x.Slice(c)), IsSameSlice(Span(p, 1))); EXPECT_THAT(Type>(x.Slice(cc)), IsSameSlice(Span(p, 1))); EXPECT_THAT(Type>>(x.Slices(c)), Tuple(IsSameSlice(Span(p, 1)))); EXPECT_THAT(Type>>(x.Slices(cc)), Tuple(IsSameSlice(Span(p, 1)))); } namespace example { // Immutable move-only string with sizeof equal to sizeof(void*). The string // size and the characters are kept in the same heap allocation. class CompactString { public: CompactString(const char* s = "") { // NOLINT const size_t size = strlen(s); // size_t[1], followed by char[size + 1]. // This statement doesn't allocate memory. const L layout(1, size + 1); // AllocSize() tells us how much memory we need to allocate for all our // data. p_.reset(new unsigned char[layout.AllocSize()]); // If running under ASAN, mark the padding bytes, if any, to catch memory // errors. layout.PoisonPadding(p_.get()); // Store the size in the allocation. // Pointer() is a synonym for Pointer<0>(). *layout.Pointer(p_.get()) = size; // Store the characters in the allocation. memcpy(layout.Pointer(p_.get()), s, size + 1); } size_t size() const { // Equivalent to reinterpret_cast(*p). return *L::Partial().Pointer(p_.get()); } const char* c_str() const { // Equivalent to reinterpret_cast(p.get() + sizeof(size_t)). // The argument in Partial(1) specifies that we have size_t[1] in front of // the characters. return L::Partial(1).Pointer(p_.get()); } private: // Our heap allocation contains a size_t followed by an array of chars. using L = Layout; std::unique_ptr p_; }; TEST(CompactString, Works) { CompactString s = "hello"; EXPECT_EQ(5, s.size()); EXPECT_STREQ("hello", s.c_str()); } } // namespace example } // namespace } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl