Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- weight/_dep/abseil-cpp/absl/container/internal/common_policy_traits_test.cc +134 -0
- weight/_dep/abseil-cpp/absl/container/internal/compressed_tuple_test.cc +419 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_function_defaults.h +209 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc +549 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_generator_testing.cc +78 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_generator_testing.h +182 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_policy_testing.h +184 -0
- weight/_dep/abseil-cpp/absl/container/internal/hash_policy_traits_test.cc +80 -0
- weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler.cc +285 -0
- weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler.h +257 -0
- weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc +424 -0
- weight/_dep/abseil-cpp/absl/container/internal/layout_benchmark.cc +122 -0
- weight/_dep/abseil-cpp/absl/container/internal/layout_test.cc +1646 -0
- weight/_dep/abseil-cpp/absl/container/internal/raw_hash_map.h +224 -0
- weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set.cc +380 -0
- weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set.h +0 -0
- weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set_test.cc +2684 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h +494 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_map_members_test.h +87 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_map_test.cc +50 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_set_constructor_test.h +496 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_set_lookup_test.h +91 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h +221 -0
- weight/_dep/abseil-cpp/absl/container/internal/unordered_set_test.cc +41 -0
- weight/_dep/abseil-cpp/absl/crc/crc32c.h +190 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc32c.h +39 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_cord_state.h +159 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_cord_state_test.cc +124 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy.h +122 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_fallback.cc +77 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_test.cc +177 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_x86_arm_combined.cc +450 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_non_temporal_memcpy.cc +93 -0
- weight/_dep/abseil-cpp/absl/crc/internal/crc_x86_arm_combined.cc +733 -0
- weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_arm_intrinsics.h +79 -0
- weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_memcpy.h +180 -0
- weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_memcpy_test.cc +88 -0
- weight/_dep/abseil-cpp/absl/debugging/BUILD.bazel +342 -0
- weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler.cc +405 -0
- weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler.h +121 -0
- weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler_test.cc +166 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/demangle.cc +2012 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/demangle.h +73 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/demangle_test.cc +255 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/elf_mem_image.cc +386 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/elf_mem_image.h +140 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/examine_stack.cc +320 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/examine_stack.h +64 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/stack_consumption.cc +206 -0
- weight/_dep/abseil-cpp/absl/debugging/internal/stack_consumption.h +50 -0
weight/_dep/abseil-cpp/absl/container/internal/common_policy_traits_test.cc
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/common_policy_traits.h"
|
| 16 |
+
|
| 17 |
+
#include <functional>
|
| 18 |
+
#include <memory>
|
| 19 |
+
#include <type_traits>
|
| 20 |
+
#include <utility>
|
| 21 |
+
|
| 22 |
+
#include "gmock/gmock.h"
|
| 23 |
+
#include "gtest/gtest.h"
|
| 24 |
+
#include "absl/base/config.h"
|
| 25 |
+
|
| 26 |
+
namespace absl {
|
| 27 |
+
ABSL_NAMESPACE_BEGIN
|
| 28 |
+
namespace container_internal {
|
| 29 |
+
namespace {
|
| 30 |
+
|
| 31 |
+
using ::testing::MockFunction;
|
| 32 |
+
using ::testing::AnyNumber;
|
| 33 |
+
using ::testing::ReturnRef;
|
| 34 |
+
|
| 35 |
+
using Slot = int;
|
| 36 |
+
|
| 37 |
+
struct PolicyWithoutOptionalOps {
|
| 38 |
+
using slot_type = Slot;
|
| 39 |
+
using key_type = Slot;
|
| 40 |
+
using init_type = Slot;
|
| 41 |
+
|
| 42 |
+
static std::function<void(void*, Slot*, Slot)> construct;
|
| 43 |
+
static std::function<void(void*, Slot*)> destroy;
|
| 44 |
+
|
| 45 |
+
static std::function<Slot&(Slot*)> element;
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
std::function<void(void*, Slot*, Slot)> PolicyWithoutOptionalOps::construct;
|
| 49 |
+
std::function<void(void*, Slot*)> PolicyWithoutOptionalOps::destroy;
|
| 50 |
+
|
| 51 |
+
std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::element;
|
| 52 |
+
|
| 53 |
+
struct PolicyWithOptionalOps : PolicyWithoutOptionalOps {
|
| 54 |
+
static std::function<void(void*, Slot*, Slot*)> transfer;
|
| 55 |
+
};
|
| 56 |
+
std::function<void(void*, Slot*, Slot*)> PolicyWithOptionalOps::transfer;
|
| 57 |
+
|
| 58 |
+
struct PolicyWithMemcpyTransfer : PolicyWithoutOptionalOps {
|
| 59 |
+
static std::function<std::true_type(void*, Slot*, Slot*)> transfer;
|
| 60 |
+
};
|
| 61 |
+
std::function<std::true_type(void*, Slot*, Slot*)>
|
| 62 |
+
PolicyWithMemcpyTransfer::transfer;
|
| 63 |
+
|
| 64 |
+
struct Test : ::testing::Test {
|
| 65 |
+
Test() {
|
| 66 |
+
PolicyWithoutOptionalOps::construct = [&](void* a1, Slot* a2, Slot a3) {
|
| 67 |
+
construct.Call(a1, a2, std::move(a3));
|
| 68 |
+
};
|
| 69 |
+
PolicyWithoutOptionalOps::destroy = [&](void* a1, Slot* a2) {
|
| 70 |
+
destroy.Call(a1, a2);
|
| 71 |
+
};
|
| 72 |
+
|
| 73 |
+
PolicyWithoutOptionalOps::element = [&](Slot* a1) -> Slot& {
|
| 74 |
+
return element.Call(a1);
|
| 75 |
+
};
|
| 76 |
+
|
| 77 |
+
PolicyWithOptionalOps::transfer = [&](void* a1, Slot* a2, Slot* a3) {
|
| 78 |
+
return transfer.Call(a1, a2, a3);
|
| 79 |
+
};
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
std::allocator<Slot> alloc;
|
| 83 |
+
int a = 53;
|
| 84 |
+
|
| 85 |
+
MockFunction<void(void*, Slot*, Slot)> construct;
|
| 86 |
+
MockFunction<void(void*, Slot*)> destroy;
|
| 87 |
+
|
| 88 |
+
MockFunction<Slot&(Slot*)> element;
|
| 89 |
+
|
| 90 |
+
MockFunction<void(void*, Slot*, Slot*)> transfer;
|
| 91 |
+
};
|
| 92 |
+
|
| 93 |
+
TEST_F(Test, construct) {
|
| 94 |
+
EXPECT_CALL(construct, Call(&alloc, &a, 53));
|
| 95 |
+
common_policy_traits<PolicyWithoutOptionalOps>::construct(&alloc, &a, 53);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
TEST_F(Test, destroy) {
|
| 99 |
+
EXPECT_CALL(destroy, Call(&alloc, &a));
|
| 100 |
+
common_policy_traits<PolicyWithoutOptionalOps>::destroy(&alloc, &a);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
TEST_F(Test, element) {
|
| 104 |
+
int b = 0;
|
| 105 |
+
EXPECT_CALL(element, Call(&a)).WillOnce(ReturnRef(b));
|
| 106 |
+
EXPECT_EQ(&b, &common_policy_traits<PolicyWithoutOptionalOps>::element(&a));
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
TEST_F(Test, without_transfer) {
|
| 110 |
+
int b = 42;
|
| 111 |
+
EXPECT_CALL(element, Call(&a)).Times(AnyNumber()).WillOnce(ReturnRef(a));
|
| 112 |
+
EXPECT_CALL(element, Call(&b)).WillOnce(ReturnRef(b));
|
| 113 |
+
EXPECT_CALL(construct, Call(&alloc, &a, b)).Times(AnyNumber());
|
| 114 |
+
EXPECT_CALL(destroy, Call(&alloc, &b)).Times(AnyNumber());
|
| 115 |
+
common_policy_traits<PolicyWithoutOptionalOps>::transfer(&alloc, &a, &b);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
TEST_F(Test, with_transfer) {
|
| 119 |
+
int b = 42;
|
| 120 |
+
EXPECT_CALL(transfer, Call(&alloc, &a, &b));
|
| 121 |
+
common_policy_traits<PolicyWithOptionalOps>::transfer(&alloc, &a, &b);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
TEST(TransferUsesMemcpy, Basic) {
|
| 125 |
+
EXPECT_FALSE(
|
| 126 |
+
common_policy_traits<PolicyWithOptionalOps>::transfer_uses_memcpy());
|
| 127 |
+
EXPECT_TRUE(
|
| 128 |
+
common_policy_traits<PolicyWithMemcpyTransfer>::transfer_uses_memcpy());
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
} // namespace
|
| 132 |
+
} // namespace container_internal
|
| 133 |
+
ABSL_NAMESPACE_END
|
| 134 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/compressed_tuple_test.cc
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/compressed_tuple.h"
|
| 16 |
+
|
| 17 |
+
#include <memory>
|
| 18 |
+
#include <string>
|
| 19 |
+
|
| 20 |
+
#include "gmock/gmock.h"
|
| 21 |
+
#include "gtest/gtest.h"
|
| 22 |
+
#include "absl/container/internal/test_instance_tracker.h"
|
| 23 |
+
#include "absl/memory/memory.h"
|
| 24 |
+
#include "absl/types/any.h"
|
| 25 |
+
#include "absl/types/optional.h"
|
| 26 |
+
#include "absl/utility/utility.h"
|
| 27 |
+
|
| 28 |
+
// These are declared at global scope purely so that error messages
|
| 29 |
+
// are smaller and easier to understand.
|
| 30 |
+
enum class CallType { kConstRef, kConstMove };
|
| 31 |
+
|
| 32 |
+
template <int>
|
| 33 |
+
struct Empty {
|
| 34 |
+
constexpr CallType value() const& { return CallType::kConstRef; }
|
| 35 |
+
constexpr CallType value() const&& { return CallType::kConstMove; }
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
template <typename T>
|
| 39 |
+
struct NotEmpty {
|
| 40 |
+
T value;
|
| 41 |
+
};
|
| 42 |
+
|
| 43 |
+
template <typename T, typename U>
|
| 44 |
+
struct TwoValues {
|
| 45 |
+
T value1;
|
| 46 |
+
U value2;
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
namespace absl {
|
| 51 |
+
ABSL_NAMESPACE_BEGIN
|
| 52 |
+
namespace container_internal {
|
| 53 |
+
namespace {
|
| 54 |
+
|
| 55 |
+
using absl::test_internal::CopyableMovableInstance;
|
| 56 |
+
using absl::test_internal::InstanceTracker;
|
| 57 |
+
|
| 58 |
+
TEST(CompressedTupleTest, Sizeof) {
|
| 59 |
+
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
|
| 60 |
+
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
|
| 61 |
+
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>));
|
| 62 |
+
EXPECT_EQ(sizeof(int),
|
| 63 |
+
sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>));
|
| 64 |
+
|
| 65 |
+
EXPECT_EQ(sizeof(TwoValues<int, double>),
|
| 66 |
+
sizeof(CompressedTuple<int, NotEmpty<double>>));
|
| 67 |
+
EXPECT_EQ(sizeof(TwoValues<int, double>),
|
| 68 |
+
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>));
|
| 69 |
+
EXPECT_EQ(sizeof(TwoValues<int, double>),
|
| 70 |
+
sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) {
|
| 74 |
+
InstanceTracker tracker;
|
| 75 |
+
CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1));
|
| 76 |
+
EXPECT_EQ(tracker.instances(), 1);
|
| 77 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 78 |
+
EXPECT_LE(tracker.moves(), 1);
|
| 79 |
+
EXPECT_EQ(x1.get<0>().value(), 1);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) {
|
| 83 |
+
InstanceTracker tracker;
|
| 84 |
+
|
| 85 |
+
CopyableMovableInstance i1(1);
|
| 86 |
+
CompressedTuple<CopyableMovableInstance> x1(std::move(i1));
|
| 87 |
+
EXPECT_EQ(tracker.instances(), 2);
|
| 88 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 89 |
+
EXPECT_LE(tracker.moves(), 1);
|
| 90 |
+
EXPECT_EQ(x1.get<0>().value(), 1);
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) {
|
| 94 |
+
InstanceTracker tracker;
|
| 95 |
+
CopyableMovableInstance i1(1);
|
| 96 |
+
CopyableMovableInstance i2(2);
|
| 97 |
+
Empty<0> empty;
|
| 98 |
+
CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
|
| 99 |
+
x1(std::move(i1), i2, empty);
|
| 100 |
+
EXPECT_EQ(x1.get<0>().value(), 1);
|
| 101 |
+
EXPECT_EQ(x1.get<1>().value(), 2);
|
| 102 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 103 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
struct IncompleteType;
|
| 107 |
+
CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>
|
| 108 |
+
MakeWithIncomplete(CopyableMovableInstance i1,
|
| 109 |
+
IncompleteType& t, // NOLINT
|
| 110 |
+
Empty<0> empty) {
|
| 111 |
+
return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{
|
| 112 |
+
std::move(i1), t, empty};
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
struct IncompleteType {};
|
| 116 |
+
TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) {
|
| 117 |
+
InstanceTracker tracker;
|
| 118 |
+
CopyableMovableInstance i1(1);
|
| 119 |
+
Empty<0> empty;
|
| 120 |
+
struct DerivedType : IncompleteType {int value = 0;};
|
| 121 |
+
DerivedType fd;
|
| 122 |
+
fd.value = 7;
|
| 123 |
+
|
| 124 |
+
CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 =
|
| 125 |
+
MakeWithIncomplete(std::move(i1), fd, empty);
|
| 126 |
+
|
| 127 |
+
EXPECT_EQ(x1.get<0>().value(), 1);
|
| 128 |
+
EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7);
|
| 129 |
+
|
| 130 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 131 |
+
EXPECT_EQ(tracker.moves(), 2);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
TEST(CompressedTupleTest,
|
| 135 |
+
OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) {
|
| 136 |
+
InstanceTracker tracker;
|
| 137 |
+
CopyableMovableInstance i1(1);
|
| 138 |
+
CopyableMovableInstance i2(2);
|
| 139 |
+
CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
|
| 140 |
+
x1(std::move(i1), i2, {}); // NOLINT
|
| 141 |
+
EXPECT_EQ(x1.get<0>().value(), 1);
|
| 142 |
+
EXPECT_EQ(x1.get<1>().value(), 2);
|
| 143 |
+
EXPECT_EQ(tracker.instances(), 3);
|
| 144 |
+
// We are forced into the `const Ts&...` constructor (invoking copies)
|
| 145 |
+
// because we need it to deduce the type of `{}`.
|
| 146 |
+
// std::tuple also has this behavior.
|
| 147 |
+
// Note, this test is proof that this is expected behavior, but it is not
|
| 148 |
+
// _desired_ behavior.
|
| 149 |
+
EXPECT_EQ(tracker.copies(), 1);
|
| 150 |
+
EXPECT_EQ(tracker.moves(), 0);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
TEST(CompressedTupleTest, OneCopyOnLValueConstruction) {
|
| 154 |
+
InstanceTracker tracker;
|
| 155 |
+
CopyableMovableInstance i1(1);
|
| 156 |
+
|
| 157 |
+
CompressedTuple<CopyableMovableInstance> x1(i1);
|
| 158 |
+
EXPECT_EQ(tracker.copies(), 1);
|
| 159 |
+
EXPECT_EQ(tracker.moves(), 0);
|
| 160 |
+
|
| 161 |
+
tracker.ResetCopiesMovesSwaps();
|
| 162 |
+
|
| 163 |
+
CopyableMovableInstance i2(2);
|
| 164 |
+
const CopyableMovableInstance& i2_ref = i2;
|
| 165 |
+
CompressedTuple<CopyableMovableInstance> x2(i2_ref);
|
| 166 |
+
EXPECT_EQ(tracker.copies(), 1);
|
| 167 |
+
EXPECT_EQ(tracker.moves(), 0);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
TEST(CompressedTupleTest, OneMoveOnRValueAccess) {
|
| 171 |
+
InstanceTracker tracker;
|
| 172 |
+
CopyableMovableInstance i1(1);
|
| 173 |
+
CompressedTuple<CopyableMovableInstance> x(std::move(i1));
|
| 174 |
+
tracker.ResetCopiesMovesSwaps();
|
| 175 |
+
|
| 176 |
+
CopyableMovableInstance i2 = std::move(x).get<0>();
|
| 177 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 178 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
TEST(CompressedTupleTest, OneCopyOnLValueAccess) {
|
| 182 |
+
InstanceTracker tracker;
|
| 183 |
+
|
| 184 |
+
CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
|
| 185 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 186 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 187 |
+
|
| 188 |
+
CopyableMovableInstance t = x.get<0>();
|
| 189 |
+
EXPECT_EQ(tracker.copies(), 1);
|
| 190 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
TEST(CompressedTupleTest, ZeroCopyOnRefAccess) {
|
| 194 |
+
InstanceTracker tracker;
|
| 195 |
+
|
| 196 |
+
CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
|
| 197 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 198 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 199 |
+
|
| 200 |
+
CopyableMovableInstance& t1 = x.get<0>();
|
| 201 |
+
const CopyableMovableInstance& t2 = x.get<0>();
|
| 202 |
+
EXPECT_EQ(tracker.copies(), 0);
|
| 203 |
+
EXPECT_EQ(tracker.moves(), 1);
|
| 204 |
+
EXPECT_EQ(t1.value(), 0);
|
| 205 |
+
EXPECT_EQ(t2.value(), 0);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
TEST(CompressedTupleTest, Access) {
|
| 209 |
+
struct S {
|
| 210 |
+
std::string x;
|
| 211 |
+
};
|
| 212 |
+
CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"});
|
| 213 |
+
EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>));
|
| 214 |
+
EXPECT_EQ(7, x.get<0>());
|
| 215 |
+
EXPECT_EQ("ABC", x.get<2>().x);
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
TEST(CompressedTupleTest, NonClasses) {
|
| 219 |
+
CompressedTuple<int, const char*> x(7, "ABC");
|
| 220 |
+
EXPECT_EQ(7, x.get<0>());
|
| 221 |
+
EXPECT_STREQ("ABC", x.get<1>());
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
TEST(CompressedTupleTest, MixClassAndNonClass) {
|
| 225 |
+
CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {},
|
| 226 |
+
{1.25});
|
| 227 |
+
struct Mock {
|
| 228 |
+
int v;
|
| 229 |
+
const char* p;
|
| 230 |
+
double d;
|
| 231 |
+
};
|
| 232 |
+
EXPECT_EQ(sizeof(x), sizeof(Mock));
|
| 233 |
+
EXPECT_EQ(7, x.get<0>());
|
| 234 |
+
EXPECT_STREQ("ABC", x.get<1>());
|
| 235 |
+
EXPECT_EQ(1.25, x.get<3>().value);
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
TEST(CompressedTupleTest, Nested) {
|
| 239 |
+
CompressedTuple<int, CompressedTuple<int>,
|
| 240 |
+
CompressedTuple<int, CompressedTuple<int>>>
|
| 241 |
+
x(1, CompressedTuple<int>(2),
|
| 242 |
+
CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4)));
|
| 243 |
+
EXPECT_EQ(1, x.get<0>());
|
| 244 |
+
EXPECT_EQ(2, x.get<1>().get<0>());
|
| 245 |
+
EXPECT_EQ(3, x.get<2>().get<0>());
|
| 246 |
+
EXPECT_EQ(4, x.get<2>().get<1>().get<0>());
|
| 247 |
+
|
| 248 |
+
CompressedTuple<Empty<0>, Empty<0>,
|
| 249 |
+
CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>>
|
| 250 |
+
y;
|
| 251 |
+
std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(),
|
| 252 |
+
&y.get<2>().get<1>().get<0>()};
|
| 253 |
+
#ifdef _MSC_VER
|
| 254 |
+
// MSVC has a bug where many instances of the same base class are layed out in
|
| 255 |
+
// the same address when using __declspec(empty_bases).
|
| 256 |
+
// This will be fixed in a future version of MSVC.
|
| 257 |
+
int expected = 1;
|
| 258 |
+
#else
|
| 259 |
+
int expected = 4;
|
| 260 |
+
#endif
|
| 261 |
+
EXPECT_EQ(expected, sizeof(y));
|
| 262 |
+
EXPECT_EQ(expected, empties.size());
|
| 263 |
+
EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size());
|
| 264 |
+
|
| 265 |
+
EXPECT_EQ(4 * sizeof(char),
|
| 266 |
+
sizeof(CompressedTuple<CompressedTuple<char, char>,
|
| 267 |
+
CompressedTuple<char, char>>));
|
| 268 |
+
EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value));
|
| 269 |
+
|
| 270 |
+
// Make sure everything still works when things are nested.
|
| 271 |
+
struct CT_Empty : CompressedTuple<Empty<0>> {};
|
| 272 |
+
CompressedTuple<Empty<0>, CT_Empty> nested_empty;
|
| 273 |
+
auto contained = nested_empty.get<0>();
|
| 274 |
+
auto nested = nested_empty.get<1>().get<0>();
|
| 275 |
+
EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value));
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
TEST(CompressedTupleTest, Reference) {
|
| 279 |
+
int i = 7;
|
| 280 |
+
std::string s = "Very long string that goes in the heap";
|
| 281 |
+
CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s);
|
| 282 |
+
|
| 283 |
+
// Sanity check. We should have not moved from `s`
|
| 284 |
+
EXPECT_EQ(s, "Very long string that goes in the heap");
|
| 285 |
+
|
| 286 |
+
EXPECT_EQ(x.get<0>(), x.get<1>());
|
| 287 |
+
EXPECT_NE(&x.get<0>(), &x.get<1>());
|
| 288 |
+
EXPECT_EQ(&x.get<1>(), &i);
|
| 289 |
+
|
| 290 |
+
EXPECT_EQ(x.get<2>(), x.get<3>());
|
| 291 |
+
EXPECT_NE(&x.get<2>(), &x.get<3>());
|
| 292 |
+
EXPECT_EQ(&x.get<3>(), &s);
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
TEST(CompressedTupleTest, NoElements) {
|
| 296 |
+
CompressedTuple<> x;
|
| 297 |
+
static_cast<void>(x); // Silence -Wunused-variable.
|
| 298 |
+
EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
TEST(CompressedTupleTest, MoveOnlyElements) {
|
| 302 |
+
CompressedTuple<std::unique_ptr<std::string>> str_tup(
|
| 303 |
+
absl::make_unique<std::string>("str"));
|
| 304 |
+
|
| 305 |
+
CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>,
|
| 306 |
+
std::unique_ptr<int>>
|
| 307 |
+
x(std::move(str_tup), absl::make_unique<int>(5));
|
| 308 |
+
|
| 309 |
+
EXPECT_EQ(*x.get<0>().get<0>(), "str");
|
| 310 |
+
EXPECT_EQ(*x.get<1>(), 5);
|
| 311 |
+
|
| 312 |
+
std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>();
|
| 313 |
+
std::unique_ptr<int> x1 = std::move(x).get<1>();
|
| 314 |
+
|
| 315 |
+
EXPECT_EQ(*x0, "str");
|
| 316 |
+
EXPECT_EQ(*x1, 5);
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) {
|
| 320 |
+
CompressedTuple<std::unique_ptr<std::string>> base(
|
| 321 |
+
absl::make_unique<std::string>("str"));
|
| 322 |
+
EXPECT_EQ(*base.get<0>(), "str");
|
| 323 |
+
|
| 324 |
+
CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base));
|
| 325 |
+
EXPECT_EQ(*copy.get<0>(), "str");
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
TEST(CompressedTupleTest, AnyElements) {
|
| 329 |
+
any a(std::string("str"));
|
| 330 |
+
CompressedTuple<any, any&> x(any(5), a);
|
| 331 |
+
EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5);
|
| 332 |
+
EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str");
|
| 333 |
+
|
| 334 |
+
a = 0.5f;
|
| 335 |
+
EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
TEST(CompressedTupleTest, Constexpr) {
|
| 339 |
+
struct NonTrivialStruct {
|
| 340 |
+
constexpr NonTrivialStruct() = default;
|
| 341 |
+
constexpr int value() const { return v; }
|
| 342 |
+
int v = 5;
|
| 343 |
+
};
|
| 344 |
+
struct TrivialStruct {
|
| 345 |
+
TrivialStruct() = default;
|
| 346 |
+
constexpr int value() const { return v; }
|
| 347 |
+
int v;
|
| 348 |
+
};
|
| 349 |
+
constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
|
| 350 |
+
7, 1.25, CompressedTuple<int>(5), {});
|
| 351 |
+
constexpr int x0 = x.get<0>();
|
| 352 |
+
constexpr double x1 = x.get<1>();
|
| 353 |
+
constexpr int x2 = x.get<2>().get<0>();
|
| 354 |
+
constexpr CallType x3 = x.get<3>().value();
|
| 355 |
+
|
| 356 |
+
EXPECT_EQ(x0, 7);
|
| 357 |
+
EXPECT_EQ(x1, 1.25);
|
| 358 |
+
EXPECT_EQ(x2, 5);
|
| 359 |
+
EXPECT_EQ(x3, CallType::kConstRef);
|
| 360 |
+
|
| 361 |
+
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
|
| 362 |
+
constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {};
|
| 363 |
+
constexpr CallType trivial0 = trivial.get<0>().value();
|
| 364 |
+
constexpr int trivial1 = trivial.get<1>().value();
|
| 365 |
+
constexpr int trivial2 = trivial.get<2>();
|
| 366 |
+
|
| 367 |
+
EXPECT_EQ(trivial0, CallType::kConstRef);
|
| 368 |
+
EXPECT_EQ(trivial1, 0);
|
| 369 |
+
EXPECT_EQ(trivial2, 0);
|
| 370 |
+
#endif
|
| 371 |
+
|
| 372 |
+
constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>>
|
| 373 |
+
non_trivial = {};
|
| 374 |
+
constexpr CallType non_trivial0 = non_trivial.get<0>().value();
|
| 375 |
+
constexpr int non_trivial1 = non_trivial.get<1>().value();
|
| 376 |
+
constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>();
|
| 377 |
+
|
| 378 |
+
EXPECT_EQ(non_trivial0, CallType::kConstRef);
|
| 379 |
+
EXPECT_EQ(non_trivial1, 5);
|
| 380 |
+
EXPECT_EQ(non_trivial2, absl::nullopt);
|
| 381 |
+
|
| 382 |
+
static constexpr char data[] = "DEF";
|
| 383 |
+
constexpr CompressedTuple<const char*> z(data);
|
| 384 |
+
constexpr const char* z1 = z.get<0>();
|
| 385 |
+
EXPECT_EQ(std::string(z1), std::string(data));
|
| 386 |
+
|
| 387 |
+
#if defined(__clang__)
|
| 388 |
+
// An apparent bug in earlier versions of gcc claims these are ambiguous.
|
| 389 |
+
constexpr int x2m = absl::move(x.get<2>()).get<0>();
|
| 390 |
+
constexpr CallType x3m = absl::move(x).get<3>().value();
|
| 391 |
+
EXPECT_EQ(x2m, 5);
|
| 392 |
+
EXPECT_EQ(x3m, CallType::kConstMove);
|
| 393 |
+
#endif
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
#if defined(__clang__) || defined(__GNUC__)
|
| 397 |
+
TEST(CompressedTupleTest, EmptyFinalClass) {
|
| 398 |
+
struct S final {
|
| 399 |
+
int f() const { return 5; }
|
| 400 |
+
};
|
| 401 |
+
CompressedTuple<S> x;
|
| 402 |
+
EXPECT_EQ(x.get<0>().f(), 5);
|
| 403 |
+
}
|
| 404 |
+
#endif
|
| 405 |
+
|
| 406 |
+
// TODO(b/214288561): enable this test.
|
| 407 |
+
TEST(CompressedTupleTest, DISABLED_NestedEbo) {
|
| 408 |
+
struct Empty1 {};
|
| 409 |
+
struct Empty2 {};
|
| 410 |
+
CompressedTuple<Empty1, CompressedTuple<Empty2>, int> x;
|
| 411 |
+
CompressedTuple<Empty1, Empty2, int> y;
|
| 412 |
+
// Currently fails with sizeof(x) == 8, sizeof(y) == 4.
|
| 413 |
+
EXPECT_EQ(sizeof(x), sizeof(y));
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
} // namespace
|
| 417 |
+
} // namespace container_internal
|
| 418 |
+
ABSL_NAMESPACE_END
|
| 419 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/hash_function_defaults.h
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// Define the default Hash and Eq functions for SwissTable containers.
|
| 16 |
+
//
|
| 17 |
+
// std::hash<T> and std::equal_to<T> are not appropriate hash and equal
|
| 18 |
+
// functions for SwissTable containers. There are two reasons for this.
|
| 19 |
+
//
|
| 20 |
+
// SwissTable containers are power of 2 sized containers:
|
| 21 |
+
//
|
| 22 |
+
// This means they use the lower bits of the hash value to find the slot for
|
| 23 |
+
// each entry. The typical hash function for integral types is the identity.
|
| 24 |
+
// This is a very weak hash function for SwissTable and any power of 2 sized
|
| 25 |
+
// hashtable implementation which will lead to excessive collisions. For
|
| 26 |
+
// SwissTable we use murmur3 style mixing to reduce collisions to a minimum.
|
| 27 |
+
//
|
| 28 |
+
// SwissTable containers support heterogeneous lookup:
|
| 29 |
+
//
|
| 30 |
+
// In order to make heterogeneous lookup work, hash and equal functions must be
|
| 31 |
+
// polymorphic. At the same time they have to satisfy the same requirements the
|
| 32 |
+
// C++ standard imposes on hash functions and equality operators. That is:
|
| 33 |
+
//
|
| 34 |
+
// if hash_default_eq<T>(a, b) returns true for any a and b of type T, then
|
| 35 |
+
// hash_default_hash<T>(a) must equal hash_default_hash<T>(b)
|
| 36 |
+
//
|
| 37 |
+
// For SwissTable containers this requirement is relaxed to allow a and b of
|
| 38 |
+
// any and possibly different types. Note that like the standard the hash and
|
| 39 |
+
// equal functions are still bound to T. This is important because some type U
|
| 40 |
+
// can be hashed by/tested for equality differently depending on T. A notable
|
| 41 |
+
// example is `const char*`. `const char*` is treated as a c-style string when
|
| 42 |
+
// the hash function is hash<std::string> but as a pointer when the hash
|
| 43 |
+
// function is hash<void*>.
|
| 44 |
+
//
|
| 45 |
+
#ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
|
| 46 |
+
#define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
|
| 47 |
+
|
| 48 |
+
#include <stdint.h>
|
| 49 |
+
#include <cstddef>
|
| 50 |
+
#include <memory>
|
| 51 |
+
#include <string>
|
| 52 |
+
#include <type_traits>
|
| 53 |
+
|
| 54 |
+
#include "absl/base/config.h"
|
| 55 |
+
#include "absl/hash/hash.h"
|
| 56 |
+
#include "absl/strings/cord.h"
|
| 57 |
+
#include "absl/strings/string_view.h"
|
| 58 |
+
|
| 59 |
+
#ifdef ABSL_HAVE_STD_STRING_VIEW
|
| 60 |
+
#include <string_view>
|
| 61 |
+
#endif
|
| 62 |
+
|
| 63 |
+
namespace absl {
|
| 64 |
+
ABSL_NAMESPACE_BEGIN
|
| 65 |
+
namespace container_internal {
|
| 66 |
+
|
| 67 |
+
// The hash of an object of type T is computed by using absl::Hash.
|
| 68 |
+
template <class T, class E = void>
|
| 69 |
+
struct HashEq {
|
| 70 |
+
using Hash = absl::Hash<T>;
|
| 71 |
+
using Eq = std::equal_to<T>;
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
+
struct StringHash {
|
| 75 |
+
using is_transparent = void;
|
| 76 |
+
|
| 77 |
+
size_t operator()(absl::string_view v) const {
|
| 78 |
+
return absl::Hash<absl::string_view>{}(v);
|
| 79 |
+
}
|
| 80 |
+
size_t operator()(const absl::Cord& v) const {
|
| 81 |
+
return absl::Hash<absl::Cord>{}(v);
|
| 82 |
+
}
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
struct StringEq {
|
| 86 |
+
using is_transparent = void;
|
| 87 |
+
bool operator()(absl::string_view lhs, absl::string_view rhs) const {
|
| 88 |
+
return lhs == rhs;
|
| 89 |
+
}
|
| 90 |
+
bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const {
|
| 91 |
+
return lhs == rhs;
|
| 92 |
+
}
|
| 93 |
+
bool operator()(const absl::Cord& lhs, absl::string_view rhs) const {
|
| 94 |
+
return lhs == rhs;
|
| 95 |
+
}
|
| 96 |
+
bool operator()(absl::string_view lhs, const absl::Cord& rhs) const {
|
| 97 |
+
return lhs == rhs;
|
| 98 |
+
}
|
| 99 |
+
};
|
| 100 |
+
|
| 101 |
+
// Supports heterogeneous lookup for string-like elements.
|
| 102 |
+
struct StringHashEq {
|
| 103 |
+
using Hash = StringHash;
|
| 104 |
+
using Eq = StringEq;
|
| 105 |
+
};
|
| 106 |
+
|
| 107 |
+
template <>
|
| 108 |
+
struct HashEq<std::string> : StringHashEq {};
|
| 109 |
+
template <>
|
| 110 |
+
struct HashEq<absl::string_view> : StringHashEq {};
|
| 111 |
+
template <>
|
| 112 |
+
struct HashEq<absl::Cord> : StringHashEq {};
|
| 113 |
+
|
| 114 |
+
#ifdef ABSL_HAVE_STD_STRING_VIEW
|
| 115 |
+
|
| 116 |
+
template <typename TChar>
|
| 117 |
+
struct BasicStringHash {
|
| 118 |
+
using is_transparent = void;
|
| 119 |
+
|
| 120 |
+
size_t operator()(std::basic_string_view<TChar> v) const {
|
| 121 |
+
return absl::Hash<std::basic_string_view<TChar>>{}(v);
|
| 122 |
+
}
|
| 123 |
+
};
|
| 124 |
+
|
| 125 |
+
template <typename TChar>
|
| 126 |
+
struct BasicStringEq {
|
| 127 |
+
using is_transparent = void;
|
| 128 |
+
bool operator()(std::basic_string_view<TChar> lhs,
|
| 129 |
+
std::basic_string_view<TChar> rhs) const {
|
| 130 |
+
return lhs == rhs;
|
| 131 |
+
}
|
| 132 |
+
};
|
| 133 |
+
|
| 134 |
+
// Supports heterogeneous lookup for w/u16/u32 string + string_view + char*.
|
| 135 |
+
template <typename TChar>
|
| 136 |
+
struct BasicStringHashEq {
|
| 137 |
+
using Hash = BasicStringHash<TChar>;
|
| 138 |
+
using Eq = BasicStringEq<TChar>;
|
| 139 |
+
};
|
| 140 |
+
|
| 141 |
+
template <>
|
| 142 |
+
struct HashEq<std::wstring> : BasicStringHashEq<wchar_t> {};
|
| 143 |
+
template <>
|
| 144 |
+
struct HashEq<std::wstring_view> : BasicStringHashEq<wchar_t> {};
|
| 145 |
+
template <>
|
| 146 |
+
struct HashEq<std::u16string> : BasicStringHashEq<char16_t> {};
|
| 147 |
+
template <>
|
| 148 |
+
struct HashEq<std::u16string_view> : BasicStringHashEq<char16_t> {};
|
| 149 |
+
template <>
|
| 150 |
+
struct HashEq<std::u32string> : BasicStringHashEq<char32_t> {};
|
| 151 |
+
template <>
|
| 152 |
+
struct HashEq<std::u32string_view> : BasicStringHashEq<char32_t> {};
|
| 153 |
+
|
| 154 |
+
#endif // ABSL_HAVE_STD_STRING_VIEW
|
| 155 |
+
|
| 156 |
+
// Supports heterogeneous lookup for pointers and smart pointers.
|
| 157 |
+
template <class T>
|
| 158 |
+
struct HashEq<T*> {
|
| 159 |
+
struct Hash {
|
| 160 |
+
using is_transparent = void;
|
| 161 |
+
template <class U>
|
| 162 |
+
size_t operator()(const U& ptr) const {
|
| 163 |
+
return absl::Hash<const T*>{}(HashEq::ToPtr(ptr));
|
| 164 |
+
}
|
| 165 |
+
};
|
| 166 |
+
struct Eq {
|
| 167 |
+
using is_transparent = void;
|
| 168 |
+
template <class A, class B>
|
| 169 |
+
bool operator()(const A& a, const B& b) const {
|
| 170 |
+
return HashEq::ToPtr(a) == HashEq::ToPtr(b);
|
| 171 |
+
}
|
| 172 |
+
};
|
| 173 |
+
|
| 174 |
+
private:
|
| 175 |
+
static const T* ToPtr(const T* ptr) { return ptr; }
|
| 176 |
+
template <class U, class D>
|
| 177 |
+
static const T* ToPtr(const std::unique_ptr<U, D>& ptr) {
|
| 178 |
+
return ptr.get();
|
| 179 |
+
}
|
| 180 |
+
template <class U>
|
| 181 |
+
static const T* ToPtr(const std::shared_ptr<U>& ptr) {
|
| 182 |
+
return ptr.get();
|
| 183 |
+
}
|
| 184 |
+
};
|
| 185 |
+
|
| 186 |
+
template <class T, class D>
|
| 187 |
+
struct HashEq<std::unique_ptr<T, D>> : HashEq<T*> {};
|
| 188 |
+
template <class T>
|
| 189 |
+
struct HashEq<std::shared_ptr<T>> : HashEq<T*> {};
|
| 190 |
+
|
| 191 |
+
// This header's visibility is restricted. If you need to access the default
|
| 192 |
+
// hasher please use the container's ::hasher alias instead.
|
| 193 |
+
//
|
| 194 |
+
// Example: typename Hash = typename absl::flat_hash_map<K, V>::hasher
|
| 195 |
+
template <class T>
|
| 196 |
+
using hash_default_hash = typename container_internal::HashEq<T>::Hash;
|
| 197 |
+
|
| 198 |
+
// This header's visibility is restricted. If you need to access the default
|
| 199 |
+
// key equal please use the container's ::key_equal alias instead.
|
| 200 |
+
//
|
| 201 |
+
// Example: typename Eq = typename absl::flat_hash_map<K, V, Hash>::key_equal
|
| 202 |
+
template <class T>
|
| 203 |
+
using hash_default_eq = typename container_internal::HashEq<T>::Eq;
|
| 204 |
+
|
| 205 |
+
} // namespace container_internal
|
| 206 |
+
ABSL_NAMESPACE_END
|
| 207 |
+
} // namespace absl
|
| 208 |
+
|
| 209 |
+
#endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_
|
weight/_dep/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/hash_function_defaults.h"
|
| 16 |
+
|
| 17 |
+
#include <functional>
|
| 18 |
+
#include <type_traits>
|
| 19 |
+
#include <utility>
|
| 20 |
+
|
| 21 |
+
#include "gtest/gtest.h"
|
| 22 |
+
#include "absl/random/random.h"
|
| 23 |
+
#include "absl/strings/cord.h"
|
| 24 |
+
#include "absl/strings/cord_test_helpers.h"
|
| 25 |
+
#include "absl/strings/string_view.h"
|
| 26 |
+
|
| 27 |
+
#ifdef ABSL_HAVE_STD_STRING_VIEW
|
| 28 |
+
#include <string_view>
|
| 29 |
+
#endif
|
| 30 |
+
|
| 31 |
+
namespace absl {
|
| 32 |
+
ABSL_NAMESPACE_BEGIN
|
| 33 |
+
namespace container_internal {
|
| 34 |
+
namespace {
|
| 35 |
+
|
| 36 |
+
using ::testing::Types;
|
| 37 |
+
|
| 38 |
+
TEST(Eq, Int32) {
|
| 39 |
+
hash_default_eq<int32_t> eq;
|
| 40 |
+
EXPECT_TRUE(eq(1, 1u));
|
| 41 |
+
EXPECT_TRUE(eq(1, char{1}));
|
| 42 |
+
EXPECT_TRUE(eq(1, true));
|
| 43 |
+
EXPECT_TRUE(eq(1, double{1.1}));
|
| 44 |
+
EXPECT_FALSE(eq(1, char{2}));
|
| 45 |
+
EXPECT_FALSE(eq(1, 2u));
|
| 46 |
+
EXPECT_FALSE(eq(1, false));
|
| 47 |
+
EXPECT_FALSE(eq(1, 2.));
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
TEST(Hash, Int32) {
|
| 51 |
+
hash_default_hash<int32_t> hash;
|
| 52 |
+
auto h = hash(1);
|
| 53 |
+
EXPECT_EQ(h, hash(1u));
|
| 54 |
+
EXPECT_EQ(h, hash(char{1}));
|
| 55 |
+
EXPECT_EQ(h, hash(true));
|
| 56 |
+
EXPECT_EQ(h, hash(double{1.1}));
|
| 57 |
+
EXPECT_NE(h, hash(2u));
|
| 58 |
+
EXPECT_NE(h, hash(char{2}));
|
| 59 |
+
EXPECT_NE(h, hash(false));
|
| 60 |
+
EXPECT_NE(h, hash(2.));
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
enum class MyEnum { A, B, C, D };
|
| 64 |
+
|
| 65 |
+
TEST(Eq, Enum) {
|
| 66 |
+
hash_default_eq<MyEnum> eq;
|
| 67 |
+
EXPECT_TRUE(eq(MyEnum::A, MyEnum::A));
|
| 68 |
+
EXPECT_FALSE(eq(MyEnum::A, MyEnum::B));
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
TEST(Hash, Enum) {
|
| 72 |
+
hash_default_hash<MyEnum> hash;
|
| 73 |
+
|
| 74 |
+
for (MyEnum e : {MyEnum::A, MyEnum::B, MyEnum::C}) {
|
| 75 |
+
auto h = hash(e);
|
| 76 |
+
EXPECT_EQ(h, hash_default_hash<int>{}(static_cast<int>(e)));
|
| 77 |
+
EXPECT_NE(h, hash(MyEnum::D));
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
using StringTypes = ::testing::Types<std::string, absl::string_view>;
|
| 82 |
+
|
| 83 |
+
template <class T>
|
| 84 |
+
struct EqString : ::testing::Test {
|
| 85 |
+
hash_default_eq<T> key_eq;
|
| 86 |
+
};
|
| 87 |
+
|
| 88 |
+
TYPED_TEST_SUITE(EqString, StringTypes);
|
| 89 |
+
|
| 90 |
+
template <class T>
|
| 91 |
+
struct HashString : ::testing::Test {
|
| 92 |
+
hash_default_hash<T> hasher;
|
| 93 |
+
};
|
| 94 |
+
|
| 95 |
+
TYPED_TEST_SUITE(HashString, StringTypes);
|
| 96 |
+
|
| 97 |
+
TYPED_TEST(EqString, Works) {
|
| 98 |
+
auto eq = this->key_eq;
|
| 99 |
+
EXPECT_TRUE(eq("a", "a"));
|
| 100 |
+
EXPECT_TRUE(eq("a", absl::string_view("a")));
|
| 101 |
+
EXPECT_TRUE(eq("a", std::string("a")));
|
| 102 |
+
EXPECT_FALSE(eq("a", "b"));
|
| 103 |
+
EXPECT_FALSE(eq("a", absl::string_view("b")));
|
| 104 |
+
EXPECT_FALSE(eq("a", std::string("b")));
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
TYPED_TEST(HashString, Works) {
|
| 108 |
+
auto hash = this->hasher;
|
| 109 |
+
auto h = hash("a");
|
| 110 |
+
EXPECT_EQ(h, hash(absl::string_view("a")));
|
| 111 |
+
EXPECT_EQ(h, hash(std::string("a")));
|
| 112 |
+
EXPECT_NE(h, hash(absl::string_view("b")));
|
| 113 |
+
EXPECT_NE(h, hash(std::string("b")));
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
TEST(BasicStringViewTest, WStringEqWorks) {
|
| 117 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 118 |
+
GTEST_SKIP();
|
| 119 |
+
#else
|
| 120 |
+
hash_default_eq<std::wstring> eq;
|
| 121 |
+
EXPECT_TRUE(eq(L"a", L"a"));
|
| 122 |
+
EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
|
| 123 |
+
EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
|
| 124 |
+
EXPECT_FALSE(eq(L"a", L"b"));
|
| 125 |
+
EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
|
| 126 |
+
EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
|
| 127 |
+
#endif
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
TEST(BasicStringViewTest, WStringViewEqWorks) {
|
| 131 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 132 |
+
GTEST_SKIP();
|
| 133 |
+
#else
|
| 134 |
+
hash_default_eq<std::wstring_view> eq;
|
| 135 |
+
EXPECT_TRUE(eq(L"a", L"a"));
|
| 136 |
+
EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
|
| 137 |
+
EXPECT_TRUE(eq(L"a", std::wstring(L"a")));
|
| 138 |
+
EXPECT_FALSE(eq(L"a", L"b"));
|
| 139 |
+
EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
|
| 140 |
+
EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
|
| 141 |
+
#endif
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
TEST(BasicStringViewTest, U16StringEqWorks) {
|
| 145 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 146 |
+
GTEST_SKIP();
|
| 147 |
+
#else
|
| 148 |
+
hash_default_eq<std::u16string> eq;
|
| 149 |
+
EXPECT_TRUE(eq(u"a", u"a"));
|
| 150 |
+
EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
|
| 151 |
+
EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
|
| 152 |
+
EXPECT_FALSE(eq(u"a", u"b"));
|
| 153 |
+
EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
|
| 154 |
+
EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
|
| 155 |
+
#endif
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
TEST(BasicStringViewTest, U16StringViewEqWorks) {
|
| 159 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 160 |
+
GTEST_SKIP();
|
| 161 |
+
#else
|
| 162 |
+
hash_default_eq<std::u16string_view> eq;
|
| 163 |
+
EXPECT_TRUE(eq(u"a", u"a"));
|
| 164 |
+
EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
|
| 165 |
+
EXPECT_TRUE(eq(u"a", std::u16string(u"a")));
|
| 166 |
+
EXPECT_FALSE(eq(u"a", u"b"));
|
| 167 |
+
EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
|
| 168 |
+
EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
|
| 169 |
+
#endif
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
TEST(BasicStringViewTest, U32StringEqWorks) {
|
| 173 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 174 |
+
GTEST_SKIP();
|
| 175 |
+
#else
|
| 176 |
+
hash_default_eq<std::u32string> eq;
|
| 177 |
+
EXPECT_TRUE(eq(U"a", U"a"));
|
| 178 |
+
EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
|
| 179 |
+
EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
|
| 180 |
+
EXPECT_FALSE(eq(U"a", U"b"));
|
| 181 |
+
EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
|
| 182 |
+
EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
|
| 183 |
+
#endif
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
TEST(BasicStringViewTest, U32StringViewEqWorks) {
|
| 187 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 188 |
+
GTEST_SKIP();
|
| 189 |
+
#else
|
| 190 |
+
hash_default_eq<std::u32string_view> eq;
|
| 191 |
+
EXPECT_TRUE(eq(U"a", U"a"));
|
| 192 |
+
EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
|
| 193 |
+
EXPECT_TRUE(eq(U"a", std::u32string(U"a")));
|
| 194 |
+
EXPECT_FALSE(eq(U"a", U"b"));
|
| 195 |
+
EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
|
| 196 |
+
EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
|
| 197 |
+
#endif
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
TEST(BasicStringViewTest, WStringHashWorks) {
|
| 201 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 202 |
+
GTEST_SKIP();
|
| 203 |
+
#else
|
| 204 |
+
hash_default_hash<std::wstring> hash;
|
| 205 |
+
auto h = hash(L"a");
|
| 206 |
+
EXPECT_EQ(h, hash(std::wstring_view(L"a")));
|
| 207 |
+
EXPECT_EQ(h, hash(std::wstring(L"a")));
|
| 208 |
+
EXPECT_NE(h, hash(std::wstring_view(L"b")));
|
| 209 |
+
EXPECT_NE(h, hash(std::wstring(L"b")));
|
| 210 |
+
#endif
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
TEST(BasicStringViewTest, WStringViewHashWorks) {
|
| 214 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 215 |
+
GTEST_SKIP();
|
| 216 |
+
#else
|
| 217 |
+
hash_default_hash<std::wstring_view> hash;
|
| 218 |
+
auto h = hash(L"a");
|
| 219 |
+
EXPECT_EQ(h, hash(std::wstring_view(L"a")));
|
| 220 |
+
EXPECT_EQ(h, hash(std::wstring(L"a")));
|
| 221 |
+
EXPECT_NE(h, hash(std::wstring_view(L"b")));
|
| 222 |
+
EXPECT_NE(h, hash(std::wstring(L"b")));
|
| 223 |
+
#endif
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
TEST(BasicStringViewTest, U16StringHashWorks) {
|
| 227 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 228 |
+
GTEST_SKIP();
|
| 229 |
+
#else
|
| 230 |
+
hash_default_hash<std::u16string> hash;
|
| 231 |
+
auto h = hash(u"a");
|
| 232 |
+
EXPECT_EQ(h, hash(std::u16string_view(u"a")));
|
| 233 |
+
EXPECT_EQ(h, hash(std::u16string(u"a")));
|
| 234 |
+
EXPECT_NE(h, hash(std::u16string_view(u"b")));
|
| 235 |
+
EXPECT_NE(h, hash(std::u16string(u"b")));
|
| 236 |
+
#endif
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
TEST(BasicStringViewTest, U16StringViewHashWorks) {
|
| 240 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 241 |
+
GTEST_SKIP();
|
| 242 |
+
#else
|
| 243 |
+
hash_default_hash<std::u16string_view> hash;
|
| 244 |
+
auto h = hash(u"a");
|
| 245 |
+
EXPECT_EQ(h, hash(std::u16string_view(u"a")));
|
| 246 |
+
EXPECT_EQ(h, hash(std::u16string(u"a")));
|
| 247 |
+
EXPECT_NE(h, hash(std::u16string_view(u"b")));
|
| 248 |
+
EXPECT_NE(h, hash(std::u16string(u"b")));
|
| 249 |
+
#endif
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
TEST(BasicStringViewTest, U32StringHashWorks) {
|
| 253 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 254 |
+
GTEST_SKIP();
|
| 255 |
+
#else
|
| 256 |
+
hash_default_hash<std::u32string> hash;
|
| 257 |
+
auto h = hash(U"a");
|
| 258 |
+
EXPECT_EQ(h, hash(std::u32string_view(U"a")));
|
| 259 |
+
EXPECT_EQ(h, hash(std::u32string(U"a")));
|
| 260 |
+
EXPECT_NE(h, hash(std::u32string_view(U"b")));
|
| 261 |
+
EXPECT_NE(h, hash(std::u32string(U"b")));
|
| 262 |
+
#endif
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
TEST(BasicStringViewTest, U32StringViewHashWorks) {
|
| 266 |
+
#ifndef ABSL_HAVE_STD_STRING_VIEW
|
| 267 |
+
GTEST_SKIP();
|
| 268 |
+
#else
|
| 269 |
+
hash_default_hash<std::u32string_view> hash;
|
| 270 |
+
auto h = hash(U"a");
|
| 271 |
+
EXPECT_EQ(h, hash(std::u32string_view(U"a")));
|
| 272 |
+
EXPECT_EQ(h, hash(std::u32string(U"a")));
|
| 273 |
+
EXPECT_NE(h, hash(std::u32string_view(U"b")));
|
| 274 |
+
EXPECT_NE(h, hash(std::u32string(U"b")));
|
| 275 |
+
#endif
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
struct NoDeleter {
|
| 279 |
+
template <class T>
|
| 280 |
+
void operator()(const T* ptr) const {}
|
| 281 |
+
};
|
| 282 |
+
|
| 283 |
+
using PointerTypes =
|
| 284 |
+
::testing::Types<const int*, int*, std::unique_ptr<const int>,
|
| 285 |
+
std::unique_ptr<const int, NoDeleter>,
|
| 286 |
+
std::unique_ptr<int>, std::unique_ptr<int, NoDeleter>,
|
| 287 |
+
std::shared_ptr<const int>, std::shared_ptr<int>>;
|
| 288 |
+
|
| 289 |
+
template <class T>
|
| 290 |
+
struct EqPointer : ::testing::Test {
|
| 291 |
+
hash_default_eq<T> key_eq;
|
| 292 |
+
};
|
| 293 |
+
|
| 294 |
+
TYPED_TEST_SUITE(EqPointer, PointerTypes);
|
| 295 |
+
|
| 296 |
+
template <class T>
|
| 297 |
+
struct HashPointer : ::testing::Test {
|
| 298 |
+
hash_default_hash<T> hasher;
|
| 299 |
+
};
|
| 300 |
+
|
| 301 |
+
TYPED_TEST_SUITE(HashPointer, PointerTypes);
|
| 302 |
+
|
| 303 |
+
TYPED_TEST(EqPointer, Works) {
|
| 304 |
+
int dummy;
|
| 305 |
+
auto eq = this->key_eq;
|
| 306 |
+
auto sptr = std::make_shared<int>();
|
| 307 |
+
std::shared_ptr<const int> csptr = sptr;
|
| 308 |
+
int* ptr = sptr.get();
|
| 309 |
+
const int* cptr = ptr;
|
| 310 |
+
std::unique_ptr<int, NoDeleter> uptr(ptr);
|
| 311 |
+
std::unique_ptr<const int, NoDeleter> cuptr(ptr);
|
| 312 |
+
|
| 313 |
+
EXPECT_TRUE(eq(ptr, cptr));
|
| 314 |
+
EXPECT_TRUE(eq(ptr, sptr));
|
| 315 |
+
EXPECT_TRUE(eq(ptr, uptr));
|
| 316 |
+
EXPECT_TRUE(eq(ptr, csptr));
|
| 317 |
+
EXPECT_TRUE(eq(ptr, cuptr));
|
| 318 |
+
EXPECT_FALSE(eq(&dummy, cptr));
|
| 319 |
+
EXPECT_FALSE(eq(&dummy, sptr));
|
| 320 |
+
EXPECT_FALSE(eq(&dummy, uptr));
|
| 321 |
+
EXPECT_FALSE(eq(&dummy, csptr));
|
| 322 |
+
EXPECT_FALSE(eq(&dummy, cuptr));
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
TEST(Hash, DerivedAndBase) {
|
| 326 |
+
struct Base {};
|
| 327 |
+
struct Derived : Base {};
|
| 328 |
+
|
| 329 |
+
hash_default_hash<Base*> hasher;
|
| 330 |
+
|
| 331 |
+
Base base;
|
| 332 |
+
Derived derived;
|
| 333 |
+
EXPECT_NE(hasher(&base), hasher(&derived));
|
| 334 |
+
EXPECT_EQ(hasher(static_cast<Base*>(&derived)), hasher(&derived));
|
| 335 |
+
|
| 336 |
+
auto dp = std::make_shared<Derived>();
|
| 337 |
+
EXPECT_EQ(hasher(static_cast<Base*>(dp.get())), hasher(dp));
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
TEST(Hash, FunctionPointer) {
|
| 341 |
+
using Func = int (*)();
|
| 342 |
+
hash_default_hash<Func> hasher;
|
| 343 |
+
hash_default_eq<Func> eq;
|
| 344 |
+
|
| 345 |
+
Func p1 = [] { return 1; }, p2 = [] { return 2; };
|
| 346 |
+
EXPECT_EQ(hasher(p1), hasher(p1));
|
| 347 |
+
EXPECT_TRUE(eq(p1, p1));
|
| 348 |
+
|
| 349 |
+
EXPECT_NE(hasher(p1), hasher(p2));
|
| 350 |
+
EXPECT_FALSE(eq(p1, p2));
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
TYPED_TEST(HashPointer, Works) {
|
| 354 |
+
int dummy;
|
| 355 |
+
auto hash = this->hasher;
|
| 356 |
+
auto sptr = std::make_shared<int>();
|
| 357 |
+
std::shared_ptr<const int> csptr = sptr;
|
| 358 |
+
int* ptr = sptr.get();
|
| 359 |
+
const int* cptr = ptr;
|
| 360 |
+
std::unique_ptr<int, NoDeleter> uptr(ptr);
|
| 361 |
+
std::unique_ptr<const int, NoDeleter> cuptr(ptr);
|
| 362 |
+
|
| 363 |
+
EXPECT_EQ(hash(ptr), hash(cptr));
|
| 364 |
+
EXPECT_EQ(hash(ptr), hash(sptr));
|
| 365 |
+
EXPECT_EQ(hash(ptr), hash(uptr));
|
| 366 |
+
EXPECT_EQ(hash(ptr), hash(csptr));
|
| 367 |
+
EXPECT_EQ(hash(ptr), hash(cuptr));
|
| 368 |
+
EXPECT_NE(hash(&dummy), hash(cptr));
|
| 369 |
+
EXPECT_NE(hash(&dummy), hash(sptr));
|
| 370 |
+
EXPECT_NE(hash(&dummy), hash(uptr));
|
| 371 |
+
EXPECT_NE(hash(&dummy), hash(csptr));
|
| 372 |
+
EXPECT_NE(hash(&dummy), hash(cuptr));
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
TEST(EqCord, Works) {
|
| 376 |
+
hash_default_eq<absl::Cord> eq;
|
| 377 |
+
const absl::string_view a_string_view = "a";
|
| 378 |
+
const absl::Cord a_cord(a_string_view);
|
| 379 |
+
const absl::string_view b_string_view = "b";
|
| 380 |
+
const absl::Cord b_cord(b_string_view);
|
| 381 |
+
|
| 382 |
+
EXPECT_TRUE(eq(a_cord, a_cord));
|
| 383 |
+
EXPECT_TRUE(eq(a_cord, a_string_view));
|
| 384 |
+
EXPECT_TRUE(eq(a_string_view, a_cord));
|
| 385 |
+
EXPECT_FALSE(eq(a_cord, b_cord));
|
| 386 |
+
EXPECT_FALSE(eq(a_cord, b_string_view));
|
| 387 |
+
EXPECT_FALSE(eq(b_string_view, a_cord));
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
TEST(HashCord, Works) {
|
| 391 |
+
hash_default_hash<absl::Cord> hash;
|
| 392 |
+
const absl::string_view a_string_view = "a";
|
| 393 |
+
const absl::Cord a_cord(a_string_view);
|
| 394 |
+
const absl::string_view b_string_view = "b";
|
| 395 |
+
const absl::Cord b_cord(b_string_view);
|
| 396 |
+
|
| 397 |
+
EXPECT_EQ(hash(a_cord), hash(a_cord));
|
| 398 |
+
EXPECT_EQ(hash(b_cord), hash(b_cord));
|
| 399 |
+
EXPECT_EQ(hash(a_string_view), hash(a_cord));
|
| 400 |
+
EXPECT_EQ(hash(b_string_view), hash(b_cord));
|
| 401 |
+
EXPECT_EQ(hash(absl::Cord("")), hash(""));
|
| 402 |
+
EXPECT_EQ(hash(absl::Cord()), hash(absl::string_view()));
|
| 403 |
+
|
| 404 |
+
EXPECT_NE(hash(a_cord), hash(b_cord));
|
| 405 |
+
EXPECT_NE(hash(a_cord), hash(b_string_view));
|
| 406 |
+
EXPECT_NE(hash(a_string_view), hash(b_cord));
|
| 407 |
+
EXPECT_NE(hash(a_string_view), hash(b_string_view));
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
void NoOpReleaser(absl::string_view data, void* arg) {}
|
| 411 |
+
|
| 412 |
+
TEST(HashCord, FragmentedCordWorks) {
|
| 413 |
+
hash_default_hash<absl::Cord> hash;
|
| 414 |
+
absl::Cord c = absl::MakeFragmentedCord({"a", "b", "c"});
|
| 415 |
+
EXPECT_FALSE(c.TryFlat().has_value());
|
| 416 |
+
EXPECT_EQ(hash(c), hash("abc"));
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
TEST(HashCord, FragmentedLongCordWorks) {
|
| 420 |
+
hash_default_hash<absl::Cord> hash;
|
| 421 |
+
// Crete some large strings which do not fit on the stack.
|
| 422 |
+
std::string a(65536, 'a');
|
| 423 |
+
std::string b(65536, 'b');
|
| 424 |
+
absl::Cord c = absl::MakeFragmentedCord({a, b});
|
| 425 |
+
EXPECT_FALSE(c.TryFlat().has_value());
|
| 426 |
+
EXPECT_EQ(hash(c), hash(a + b));
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
TEST(HashCord, RandomCord) {
|
| 430 |
+
hash_default_hash<absl::Cord> hash;
|
| 431 |
+
auto bitgen = absl::BitGen();
|
| 432 |
+
for (int i = 0; i < 1000; ++i) {
|
| 433 |
+
const int number_of_segments = absl::Uniform(bitgen, 0, 10);
|
| 434 |
+
std::vector<std::string> pieces;
|
| 435 |
+
for (size_t s = 0; s < number_of_segments; ++s) {
|
| 436 |
+
std::string str;
|
| 437 |
+
str.resize(absl::Uniform(bitgen, 0, 4096));
|
| 438 |
+
// MSVC needed the explicit return type in the lambda.
|
| 439 |
+
std::generate(str.begin(), str.end(), [&]() -> char {
|
| 440 |
+
return static_cast<char>(absl::Uniform<unsigned char>(bitgen));
|
| 441 |
+
});
|
| 442 |
+
pieces.push_back(str);
|
| 443 |
+
}
|
| 444 |
+
absl::Cord c = absl::MakeFragmentedCord(pieces);
|
| 445 |
+
EXPECT_EQ(hash(c), hash(std::string(c)));
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
// Cartesian product of (std::string, absl::string_view)
|
| 450 |
+
// with (std::string, absl::string_view, const char*, absl::Cord).
|
| 451 |
+
using StringTypesCartesianProduct = Types<
|
| 452 |
+
// clang-format off
|
| 453 |
+
std::pair<absl::Cord, std::string>,
|
| 454 |
+
std::pair<absl::Cord, absl::string_view>,
|
| 455 |
+
std::pair<absl::Cord, absl::Cord>,
|
| 456 |
+
std::pair<absl::Cord, const char*>,
|
| 457 |
+
|
| 458 |
+
std::pair<std::string, absl::Cord>,
|
| 459 |
+
std::pair<absl::string_view, absl::Cord>,
|
| 460 |
+
|
| 461 |
+
std::pair<absl::string_view, std::string>,
|
| 462 |
+
std::pair<absl::string_view, absl::string_view>,
|
| 463 |
+
std::pair<absl::string_view, const char*>>;
|
| 464 |
+
// clang-format on
|
| 465 |
+
|
| 466 |
+
constexpr char kFirstString[] = "abc123";
|
| 467 |
+
constexpr char kSecondString[] = "ijk456";
|
| 468 |
+
|
| 469 |
+
template <typename T>
|
| 470 |
+
struct StringLikeTest : public ::testing::Test {
|
| 471 |
+
typename T::first_type a1{kFirstString};
|
| 472 |
+
typename T::second_type b1{kFirstString};
|
| 473 |
+
typename T::first_type a2{kSecondString};
|
| 474 |
+
typename T::second_type b2{kSecondString};
|
| 475 |
+
hash_default_eq<typename T::first_type> eq;
|
| 476 |
+
hash_default_hash<typename T::first_type> hash;
|
| 477 |
+
};
|
| 478 |
+
|
| 479 |
+
TYPED_TEST_SUITE_P(StringLikeTest);
|
| 480 |
+
|
| 481 |
+
TYPED_TEST_P(StringLikeTest, Eq) {
|
| 482 |
+
EXPECT_TRUE(this->eq(this->a1, this->b1));
|
| 483 |
+
EXPECT_TRUE(this->eq(this->b1, this->a1));
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
TYPED_TEST_P(StringLikeTest, NotEq) {
|
| 487 |
+
EXPECT_FALSE(this->eq(this->a1, this->b2));
|
| 488 |
+
EXPECT_FALSE(this->eq(this->b2, this->a1));
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
TYPED_TEST_P(StringLikeTest, HashEq) {
|
| 492 |
+
EXPECT_EQ(this->hash(this->a1), this->hash(this->b1));
|
| 493 |
+
EXPECT_EQ(this->hash(this->a2), this->hash(this->b2));
|
| 494 |
+
// It would be a poor hash function which collides on these strings.
|
| 495 |
+
EXPECT_NE(this->hash(this->a1), this->hash(this->b2));
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
TYPED_TEST_SUITE(StringLikeTest, StringTypesCartesianProduct);
|
| 499 |
+
|
| 500 |
+
} // namespace
|
| 501 |
+
} // namespace container_internal
|
| 502 |
+
ABSL_NAMESPACE_END
|
| 503 |
+
} // namespace absl
|
| 504 |
+
|
| 505 |
+
enum Hash : size_t {
|
| 506 |
+
kStd = 0x1, // std::hash
|
| 507 |
+
#ifdef _MSC_VER
|
| 508 |
+
kExtension = kStd, // In MSVC, std::hash == ::hash
|
| 509 |
+
#else // _MSC_VER
|
| 510 |
+
kExtension = 0x2, // ::hash (GCC extension)
|
| 511 |
+
#endif // _MSC_VER
|
| 512 |
+
};
|
| 513 |
+
|
| 514 |
+
// H is a bitmask of Hash enumerations.
|
| 515 |
+
// Hashable<H> is hashable via all means specified in H.
|
| 516 |
+
template <int H>
|
| 517 |
+
struct Hashable {
|
| 518 |
+
static constexpr bool HashableBy(Hash h) { return h & H; }
|
| 519 |
+
};
|
| 520 |
+
|
| 521 |
+
namespace std {
|
| 522 |
+
template <int H>
|
| 523 |
+
struct hash<Hashable<H>> {
|
| 524 |
+
template <class E = Hashable<H>,
|
| 525 |
+
class = typename std::enable_if<E::HashableBy(kStd)>::type>
|
| 526 |
+
size_t operator()(E) const {
|
| 527 |
+
return kStd;
|
| 528 |
+
}
|
| 529 |
+
};
|
| 530 |
+
} // namespace std
|
| 531 |
+
|
| 532 |
+
namespace absl {
|
| 533 |
+
ABSL_NAMESPACE_BEGIN
|
| 534 |
+
namespace container_internal {
|
| 535 |
+
namespace {
|
| 536 |
+
|
| 537 |
+
template <class T>
|
| 538 |
+
size_t Hash(const T& v) {
|
| 539 |
+
return hash_default_hash<T>()(v);
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
TEST(Delegate, HashDispatch) {
|
| 543 |
+
EXPECT_EQ(Hash(kStd), Hash(Hashable<kStd>()));
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
} // namespace
|
| 547 |
+
} // namespace container_internal
|
| 548 |
+
ABSL_NAMESPACE_END
|
| 549 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/hash_generator_testing.cc
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/hash_generator_testing.h"
|
| 16 |
+
|
| 17 |
+
#include <deque>
|
| 18 |
+
|
| 19 |
+
#include "absl/base/no_destructor.h"
|
| 20 |
+
|
| 21 |
+
namespace absl {
|
| 22 |
+
ABSL_NAMESPACE_BEGIN
|
| 23 |
+
namespace container_internal {
|
| 24 |
+
namespace hash_internal {
|
| 25 |
+
namespace {
|
| 26 |
+
|
| 27 |
+
class RandomDeviceSeedSeq {
|
| 28 |
+
public:
|
| 29 |
+
using result_type = typename std::random_device::result_type;
|
| 30 |
+
|
| 31 |
+
template <class Iterator>
|
| 32 |
+
void generate(Iterator start, Iterator end) {
|
| 33 |
+
while (start != end) {
|
| 34 |
+
*start = gen_();
|
| 35 |
+
++start;
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
private:
|
| 40 |
+
std::random_device gen_;
|
| 41 |
+
};
|
| 42 |
+
|
| 43 |
+
} // namespace
|
| 44 |
+
|
| 45 |
+
std::mt19937_64* GetSharedRng() {
|
| 46 |
+
static absl::NoDestructor<std::mt19937_64> rng([] {
|
| 47 |
+
RandomDeviceSeedSeq seed_seq;
|
| 48 |
+
return std::mt19937_64(seed_seq);
|
| 49 |
+
}());
|
| 50 |
+
return rng.get();
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
std::string Generator<std::string>::operator()() const {
|
| 54 |
+
// NOLINTNEXTLINE(runtime/int)
|
| 55 |
+
std::uniform_int_distribution<short> chars(0x20, 0x7E);
|
| 56 |
+
std::string res;
|
| 57 |
+
res.resize(32);
|
| 58 |
+
std::generate(res.begin(), res.end(),
|
| 59 |
+
[&]() { return chars(*GetSharedRng()); });
|
| 60 |
+
return res;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
absl::string_view Generator<absl::string_view>::operator()() const {
|
| 64 |
+
static absl::NoDestructor<std::deque<std::string>> arena;
|
| 65 |
+
// NOLINTNEXTLINE(runtime/int)
|
| 66 |
+
std::uniform_int_distribution<short> chars(0x20, 0x7E);
|
| 67 |
+
arena->emplace_back();
|
| 68 |
+
auto& res = arena->back();
|
| 69 |
+
res.resize(32);
|
| 70 |
+
std::generate(res.begin(), res.end(),
|
| 71 |
+
[&]() { return chars(*GetSharedRng()); });
|
| 72 |
+
return res;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
} // namespace hash_internal
|
| 76 |
+
} // namespace container_internal
|
| 77 |
+
ABSL_NAMESPACE_END
|
| 78 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/hash_generator_testing.h
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// Generates random values for testing. Specialized only for the few types we
|
| 16 |
+
// care about.
|
| 17 |
+
|
| 18 |
+
#ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
|
| 19 |
+
#define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
|
| 20 |
+
|
| 21 |
+
#include <stdint.h>
|
| 22 |
+
|
| 23 |
+
#include <algorithm>
|
| 24 |
+
#include <cassert>
|
| 25 |
+
#include <iosfwd>
|
| 26 |
+
#include <random>
|
| 27 |
+
#include <tuple>
|
| 28 |
+
#include <type_traits>
|
| 29 |
+
#include <utility>
|
| 30 |
+
#include <vector>
|
| 31 |
+
|
| 32 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 33 |
+
#include "absl/memory/memory.h"
|
| 34 |
+
#include "absl/meta/type_traits.h"
|
| 35 |
+
#include "absl/strings/string_view.h"
|
| 36 |
+
|
| 37 |
+
namespace absl {
|
| 38 |
+
ABSL_NAMESPACE_BEGIN
|
| 39 |
+
namespace container_internal {
|
| 40 |
+
namespace hash_internal {
|
| 41 |
+
namespace generator_internal {
|
| 42 |
+
|
| 43 |
+
template <class Container, class = void>
|
| 44 |
+
struct IsMap : std::false_type {};
|
| 45 |
+
|
| 46 |
+
template <class Map>
|
| 47 |
+
struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type {};
|
| 48 |
+
|
| 49 |
+
} // namespace generator_internal
|
| 50 |
+
|
| 51 |
+
std::mt19937_64* GetSharedRng();
|
| 52 |
+
|
| 53 |
+
enum Enum {
|
| 54 |
+
kEnumEmpty,
|
| 55 |
+
kEnumDeleted,
|
| 56 |
+
};
|
| 57 |
+
|
| 58 |
+
enum class EnumClass : uint64_t {
|
| 59 |
+
kEmpty,
|
| 60 |
+
kDeleted,
|
| 61 |
+
};
|
| 62 |
+
|
| 63 |
+
inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) {
|
| 64 |
+
return o << static_cast<uint64_t>(ec);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
template <class T, class E = void>
|
| 68 |
+
struct Generator;
|
| 69 |
+
|
| 70 |
+
template <class T>
|
| 71 |
+
struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> {
|
| 72 |
+
T operator()() const {
|
| 73 |
+
std::uniform_int_distribution<T> dist;
|
| 74 |
+
return dist(*GetSharedRng());
|
| 75 |
+
}
|
| 76 |
+
};
|
| 77 |
+
|
| 78 |
+
template <>
|
| 79 |
+
struct Generator<Enum> {
|
| 80 |
+
Enum operator()() const {
|
| 81 |
+
std::uniform_int_distribution<typename std::underlying_type<Enum>::type>
|
| 82 |
+
dist;
|
| 83 |
+
while (true) {
|
| 84 |
+
auto variate = dist(*GetSharedRng());
|
| 85 |
+
if (variate != kEnumEmpty && variate != kEnumDeleted)
|
| 86 |
+
return static_cast<Enum>(variate);
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
};
|
| 90 |
+
|
| 91 |
+
template <>
|
| 92 |
+
struct Generator<EnumClass> {
|
| 93 |
+
EnumClass operator()() const {
|
| 94 |
+
std::uniform_int_distribution<
|
| 95 |
+
typename std::underlying_type<EnumClass>::type>
|
| 96 |
+
dist;
|
| 97 |
+
while (true) {
|
| 98 |
+
EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng()));
|
| 99 |
+
if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted)
|
| 100 |
+
return static_cast<EnumClass>(variate);
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
};
|
| 104 |
+
|
| 105 |
+
template <>
|
| 106 |
+
struct Generator<std::string> {
|
| 107 |
+
std::string operator()() const;
|
| 108 |
+
};
|
| 109 |
+
|
| 110 |
+
template <>
|
| 111 |
+
struct Generator<absl::string_view> {
|
| 112 |
+
absl::string_view operator()() const;
|
| 113 |
+
};
|
| 114 |
+
|
| 115 |
+
template <>
|
| 116 |
+
struct Generator<NonStandardLayout> {
|
| 117 |
+
NonStandardLayout operator()() const {
|
| 118 |
+
return NonStandardLayout(Generator<std::string>()());
|
| 119 |
+
}
|
| 120 |
+
};
|
| 121 |
+
|
| 122 |
+
template <class K, class V>
|
| 123 |
+
struct Generator<std::pair<K, V>> {
|
| 124 |
+
std::pair<K, V> operator()() const {
|
| 125 |
+
return std::pair<K, V>(Generator<typename std::decay<K>::type>()(),
|
| 126 |
+
Generator<typename std::decay<V>::type>()());
|
| 127 |
+
}
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
template <class... Ts>
|
| 131 |
+
struct Generator<std::tuple<Ts...>> {
|
| 132 |
+
std::tuple<Ts...> operator()() const {
|
| 133 |
+
return std::tuple<Ts...>(Generator<typename std::decay<Ts>::type>()()...);
|
| 134 |
+
}
|
| 135 |
+
};
|
| 136 |
+
|
| 137 |
+
template <class T>
|
| 138 |
+
struct Generator<std::unique_ptr<T>> {
|
| 139 |
+
std::unique_ptr<T> operator()() const {
|
| 140 |
+
return absl::make_unique<T>(Generator<T>()());
|
| 141 |
+
}
|
| 142 |
+
};
|
| 143 |
+
|
| 144 |
+
template <class U>
|
| 145 |
+
struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()),
|
| 146 |
+
decltype(std::declval<U&>().value())>>
|
| 147 |
+
: Generator<std::pair<
|
| 148 |
+
typename std::decay<decltype(std::declval<U&>().key())>::type,
|
| 149 |
+
typename std::decay<decltype(std::declval<U&>().value())>::type>> {};
|
| 150 |
+
|
| 151 |
+
template <class Container>
|
| 152 |
+
using GeneratedType = decltype(
|
| 153 |
+
std::declval<const Generator<
|
| 154 |
+
typename std::conditional<generator_internal::IsMap<Container>::value,
|
| 155 |
+
typename Container::value_type,
|
| 156 |
+
typename Container::key_type>::type>&>()());
|
| 157 |
+
|
| 158 |
+
// Naive wrapper that performs a linear search of previous values.
|
| 159 |
+
// Beware this is O(SQR), which is reasonable for smaller kMaxValues.
|
| 160 |
+
template <class T, size_t kMaxValues = 64, class E = void>
|
| 161 |
+
struct UniqueGenerator {
|
| 162 |
+
Generator<T, E> gen;
|
| 163 |
+
std::vector<T> values;
|
| 164 |
+
|
| 165 |
+
T operator()() {
|
| 166 |
+
assert(values.size() < kMaxValues);
|
| 167 |
+
for (;;) {
|
| 168 |
+
T value = gen();
|
| 169 |
+
if (std::find(values.begin(), values.end(), value) == values.end()) {
|
| 170 |
+
values.push_back(value);
|
| 171 |
+
return value;
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
};
|
| 176 |
+
|
| 177 |
+
} // namespace hash_internal
|
| 178 |
+
} // namespace container_internal
|
| 179 |
+
ABSL_NAMESPACE_END
|
| 180 |
+
} // namespace absl
|
| 181 |
+
|
| 182 |
+
#endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_
|
weight/_dep/abseil-cpp/absl/container/internal/hash_policy_testing.h
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// Utilities to help tests verify that hash tables properly handle stateful
|
| 16 |
+
// allocators and hash functions.
|
| 17 |
+
|
| 18 |
+
#ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
|
| 19 |
+
#define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
|
| 20 |
+
|
| 21 |
+
#include <cstdlib>
|
| 22 |
+
#include <limits>
|
| 23 |
+
#include <memory>
|
| 24 |
+
#include <ostream>
|
| 25 |
+
#include <type_traits>
|
| 26 |
+
#include <utility>
|
| 27 |
+
#include <vector>
|
| 28 |
+
|
| 29 |
+
#include "absl/hash/hash.h"
|
| 30 |
+
#include "absl/strings/string_view.h"
|
| 31 |
+
|
| 32 |
+
namespace absl {
|
| 33 |
+
ABSL_NAMESPACE_BEGIN
|
| 34 |
+
namespace container_internal {
|
| 35 |
+
namespace hash_testing_internal {
|
| 36 |
+
|
| 37 |
+
template <class Derived>
|
| 38 |
+
struct WithId {
|
| 39 |
+
WithId() : id_(next_id<Derived>()) {}
|
| 40 |
+
WithId(const WithId& that) : id_(that.id_) {}
|
| 41 |
+
WithId(WithId&& that) : id_(that.id_) { that.id_ = 0; }
|
| 42 |
+
WithId& operator=(const WithId& that) {
|
| 43 |
+
id_ = that.id_;
|
| 44 |
+
return *this;
|
| 45 |
+
}
|
| 46 |
+
WithId& operator=(WithId&& that) {
|
| 47 |
+
id_ = that.id_;
|
| 48 |
+
that.id_ = 0;
|
| 49 |
+
return *this;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
size_t id() const { return id_; }
|
| 53 |
+
|
| 54 |
+
friend bool operator==(const WithId& a, const WithId& b) {
|
| 55 |
+
return a.id_ == b.id_;
|
| 56 |
+
}
|
| 57 |
+
friend bool operator!=(const WithId& a, const WithId& b) { return !(a == b); }
|
| 58 |
+
|
| 59 |
+
protected:
|
| 60 |
+
explicit WithId(size_t id) : id_(id) {}
|
| 61 |
+
|
| 62 |
+
private:
|
| 63 |
+
size_t id_;
|
| 64 |
+
|
| 65 |
+
template <class T>
|
| 66 |
+
static size_t next_id() {
|
| 67 |
+
// 0 is reserved for moved from state.
|
| 68 |
+
static size_t gId = 1;
|
| 69 |
+
return gId++;
|
| 70 |
+
}
|
| 71 |
+
};
|
| 72 |
+
|
| 73 |
+
} // namespace hash_testing_internal
|
| 74 |
+
|
| 75 |
+
struct NonStandardLayout {
|
| 76 |
+
NonStandardLayout() {}
|
| 77 |
+
explicit NonStandardLayout(std::string s) : value(std::move(s)) {}
|
| 78 |
+
virtual ~NonStandardLayout() {}
|
| 79 |
+
|
| 80 |
+
friend bool operator==(const NonStandardLayout& a,
|
| 81 |
+
const NonStandardLayout& b) {
|
| 82 |
+
return a.value == b.value;
|
| 83 |
+
}
|
| 84 |
+
friend bool operator!=(const NonStandardLayout& a,
|
| 85 |
+
const NonStandardLayout& b) {
|
| 86 |
+
return a.value != b.value;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
template <typename H>
|
| 90 |
+
friend H AbslHashValue(H h, const NonStandardLayout& v) {
|
| 91 |
+
return H::combine(std::move(h), v.value);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
std::string value;
|
| 95 |
+
};
|
| 96 |
+
|
| 97 |
+
struct StatefulTestingHash
|
| 98 |
+
: absl::container_internal::hash_testing_internal::WithId<
|
| 99 |
+
StatefulTestingHash> {
|
| 100 |
+
template <class T>
|
| 101 |
+
size_t operator()(const T& t) const {
|
| 102 |
+
return absl::Hash<T>{}(t);
|
| 103 |
+
}
|
| 104 |
+
};
|
| 105 |
+
|
| 106 |
+
struct StatefulTestingEqual
|
| 107 |
+
: absl::container_internal::hash_testing_internal::WithId<
|
| 108 |
+
StatefulTestingEqual> {
|
| 109 |
+
template <class T, class U>
|
| 110 |
+
bool operator()(const T& t, const U& u) const {
|
| 111 |
+
return t == u;
|
| 112 |
+
}
|
| 113 |
+
};
|
| 114 |
+
|
| 115 |
+
// It is expected that Alloc() == Alloc() for all allocators so we cannot use
|
| 116 |
+
// WithId base. We need to explicitly assign ids.
|
| 117 |
+
template <class T = int>
|
| 118 |
+
struct Alloc : std::allocator<T> {
|
| 119 |
+
using propagate_on_container_swap = std::true_type;
|
| 120 |
+
|
| 121 |
+
// Using old paradigm for this to ensure compatibility.
|
| 122 |
+
explicit Alloc(size_t id = 0) : id_(id) {}
|
| 123 |
+
|
| 124 |
+
Alloc(const Alloc&) = default;
|
| 125 |
+
Alloc& operator=(const Alloc&) = default;
|
| 126 |
+
|
| 127 |
+
template <class U>
|
| 128 |
+
Alloc(const Alloc<U>& that) : std::allocator<T>(that), id_(that.id()) {}
|
| 129 |
+
|
| 130 |
+
template <class U>
|
| 131 |
+
struct rebind {
|
| 132 |
+
using other = Alloc<U>;
|
| 133 |
+
};
|
| 134 |
+
|
| 135 |
+
size_t id() const { return id_; }
|
| 136 |
+
|
| 137 |
+
friend bool operator==(const Alloc& a, const Alloc& b) {
|
| 138 |
+
return a.id_ == b.id_;
|
| 139 |
+
}
|
| 140 |
+
friend bool operator!=(const Alloc& a, const Alloc& b) { return !(a == b); }
|
| 141 |
+
|
| 142 |
+
private:
|
| 143 |
+
size_t id_ = (std::numeric_limits<size_t>::max)();
|
| 144 |
+
};
|
| 145 |
+
|
| 146 |
+
template <class Map>
|
| 147 |
+
auto items(const Map& m) -> std::vector<
|
| 148 |
+
std::pair<typename Map::key_type, typename Map::mapped_type>> {
|
| 149 |
+
using std::get;
|
| 150 |
+
std::vector<std::pair<typename Map::key_type, typename Map::mapped_type>> res;
|
| 151 |
+
res.reserve(m.size());
|
| 152 |
+
for (const auto& v : m) res.emplace_back(get<0>(v), get<1>(v));
|
| 153 |
+
return res;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
template <class Set>
|
| 157 |
+
auto keys(const Set& s)
|
| 158 |
+
-> std::vector<typename std::decay<typename Set::key_type>::type> {
|
| 159 |
+
std::vector<typename std::decay<typename Set::key_type>::type> res;
|
| 160 |
+
res.reserve(s.size());
|
| 161 |
+
for (const auto& v : s) res.emplace_back(v);
|
| 162 |
+
return res;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
} // namespace container_internal
|
| 166 |
+
ABSL_NAMESPACE_END
|
| 167 |
+
} // namespace absl
|
| 168 |
+
|
| 169 |
+
// ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
|
| 170 |
+
// where the unordered containers are missing certain constructors that
|
| 171 |
+
// take allocator arguments. This test is defined ad-hoc for the platforms
|
| 172 |
+
// we care about (notably Crosstool 17) because libstdcxx's useless
|
| 173 |
+
// versioning scheme precludes a more principled solution.
|
| 174 |
+
// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
|
| 175 |
+
// "the unordered associative containers in <unordered_map> and <unordered_set>
|
| 176 |
+
// meet the allocator-aware container requirements;"
|
| 177 |
+
#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \
|
| 178 |
+
( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 ))
|
| 179 |
+
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0
|
| 180 |
+
#else
|
| 181 |
+
#define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1
|
| 182 |
+
#endif
|
| 183 |
+
|
| 184 |
+
#endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_
|
weight/_dep/abseil-cpp/absl/container/internal/hash_policy_traits_test.cc
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/hash_policy_traits.h"
|
| 16 |
+
|
| 17 |
+
#include <functional>
|
| 18 |
+
#include <memory>
|
| 19 |
+
#include <new>
|
| 20 |
+
|
| 21 |
+
#include "gmock/gmock.h"
|
| 22 |
+
#include "gtest/gtest.h"
|
| 23 |
+
|
| 24 |
+
namespace absl {
|
| 25 |
+
ABSL_NAMESPACE_BEGIN
|
| 26 |
+
namespace container_internal {
|
| 27 |
+
namespace {
|
| 28 |
+
|
| 29 |
+
using ::testing::MockFunction;
|
| 30 |
+
using ::testing::Return;
|
| 31 |
+
using ::testing::ReturnRef;
|
| 32 |
+
|
| 33 |
+
using Alloc = std::allocator<int>;
|
| 34 |
+
using Slot = int;
|
| 35 |
+
|
| 36 |
+
struct PolicyWithoutOptionalOps {
|
| 37 |
+
using slot_type = Slot;
|
| 38 |
+
using key_type = Slot;
|
| 39 |
+
using init_type = Slot;
|
| 40 |
+
|
| 41 |
+
static std::function<Slot&(Slot*)> element;
|
| 42 |
+
static int apply(int v) { return apply_impl(v); }
|
| 43 |
+
static std::function<int(int)> apply_impl;
|
| 44 |
+
static std::function<Slot&(Slot*)> value;
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
std::function<int(int)> PolicyWithoutOptionalOps::apply_impl;
|
| 48 |
+
std::function<Slot&(Slot*)> PolicyWithoutOptionalOps::value;
|
| 49 |
+
|
| 50 |
+
struct Test : ::testing::Test {
|
| 51 |
+
Test() {
|
| 52 |
+
PolicyWithoutOptionalOps::apply_impl = [&](int a1) -> int {
|
| 53 |
+
return apply.Call(a1);
|
| 54 |
+
};
|
| 55 |
+
PolicyWithoutOptionalOps::value = [&](Slot* a1) -> Slot& {
|
| 56 |
+
return value.Call(a1);
|
| 57 |
+
};
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
std::allocator<int> alloc;
|
| 61 |
+
int a = 53;
|
| 62 |
+
MockFunction<int(int)> apply;
|
| 63 |
+
MockFunction<Slot&(Slot*)> value;
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
TEST_F(Test, apply) {
|
| 67 |
+
EXPECT_CALL(apply, Call(42)).WillOnce(Return(1337));
|
| 68 |
+
EXPECT_EQ(1337, (hash_policy_traits<PolicyWithoutOptionalOps>::apply(42)));
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
TEST_F(Test, value) {
|
| 72 |
+
int b = 0;
|
| 73 |
+
EXPECT_CALL(value, Call(&a)).WillOnce(ReturnRef(b));
|
| 74 |
+
EXPECT_EQ(&b, &hash_policy_traits<PolicyWithoutOptionalOps>::value(&a));
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
} // namespace
|
| 78 |
+
} // namespace container_internal
|
| 79 |
+
ABSL_NAMESPACE_END
|
| 80 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler.cc
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/hashtablez_sampler.h"
|
| 16 |
+
|
| 17 |
+
#include <algorithm>
|
| 18 |
+
#include <atomic>
|
| 19 |
+
#include <cassert>
|
| 20 |
+
#include <cmath>
|
| 21 |
+
#include <functional>
|
| 22 |
+
#include <limits>
|
| 23 |
+
|
| 24 |
+
#include "absl/base/attributes.h"
|
| 25 |
+
#include "absl/base/config.h"
|
| 26 |
+
#include "absl/base/internal/raw_logging.h"
|
| 27 |
+
#include "absl/debugging/stacktrace.h"
|
| 28 |
+
#include "absl/memory/memory.h"
|
| 29 |
+
#include "absl/profiling/internal/exponential_biased.h"
|
| 30 |
+
#include "absl/profiling/internal/sample_recorder.h"
|
| 31 |
+
#include "absl/synchronization/mutex.h"
|
| 32 |
+
#include "absl/time/clock.h"
|
| 33 |
+
#include "absl/utility/utility.h"
|
| 34 |
+
|
| 35 |
+
namespace absl {
|
| 36 |
+
ABSL_NAMESPACE_BEGIN
|
| 37 |
+
namespace container_internal {
|
| 38 |
+
|
| 39 |
+
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
| 40 |
+
constexpr int HashtablezInfo::kMaxStackDepth;
|
| 41 |
+
#endif
|
| 42 |
+
|
| 43 |
+
namespace {
|
| 44 |
+
ABSL_CONST_INIT std::atomic<bool> g_hashtablez_enabled{
|
| 45 |
+
false
|
| 46 |
+
};
|
| 47 |
+
ABSL_CONST_INIT std::atomic<int32_t> g_hashtablez_sample_parameter{1 << 10};
|
| 48 |
+
std::atomic<HashtablezConfigListener> g_hashtablez_config_listener{nullptr};
|
| 49 |
+
|
| 50 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 51 |
+
ABSL_PER_THREAD_TLS_KEYWORD absl::profiling_internal::ExponentialBiased
|
| 52 |
+
g_exponential_biased_generator;
|
| 53 |
+
#endif
|
| 54 |
+
|
| 55 |
+
void TriggerHashtablezConfigListener() {
|
| 56 |
+
auto* listener = g_hashtablez_config_listener.load(std::memory_order_acquire);
|
| 57 |
+
if (listener != nullptr) listener();
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
} // namespace
|
| 61 |
+
|
| 62 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 63 |
+
ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample = {0, 0};
|
| 64 |
+
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 65 |
+
|
| 66 |
+
HashtablezSampler& GlobalHashtablezSampler() {
|
| 67 |
+
static auto* sampler = new HashtablezSampler();
|
| 68 |
+
return *sampler;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
HashtablezInfo::HashtablezInfo() = default;
|
| 72 |
+
HashtablezInfo::~HashtablezInfo() = default;
|
| 73 |
+
|
| 74 |
+
void HashtablezInfo::PrepareForSampling(int64_t stride,
|
| 75 |
+
size_t inline_element_size_value) {
|
| 76 |
+
capacity.store(0, std::memory_order_relaxed);
|
| 77 |
+
size.store(0, std::memory_order_relaxed);
|
| 78 |
+
num_erases.store(0, std::memory_order_relaxed);
|
| 79 |
+
num_rehashes.store(0, std::memory_order_relaxed);
|
| 80 |
+
max_probe_length.store(0, std::memory_order_relaxed);
|
| 81 |
+
total_probe_length.store(0, std::memory_order_relaxed);
|
| 82 |
+
hashes_bitwise_or.store(0, std::memory_order_relaxed);
|
| 83 |
+
hashes_bitwise_and.store(~size_t{}, std::memory_order_relaxed);
|
| 84 |
+
hashes_bitwise_xor.store(0, std::memory_order_relaxed);
|
| 85 |
+
max_reserve.store(0, std::memory_order_relaxed);
|
| 86 |
+
|
| 87 |
+
create_time = absl::Now();
|
| 88 |
+
weight = stride;
|
| 89 |
+
// The inliner makes hardcoded skip_count difficult (especially when combined
|
| 90 |
+
// with LTO). We use the ability to exclude stacks by regex when encoding
|
| 91 |
+
// instead.
|
| 92 |
+
depth = absl::GetStackTrace(stack, HashtablezInfo::kMaxStackDepth,
|
| 93 |
+
/* skip_count= */ 0);
|
| 94 |
+
inline_element_size = inline_element_size_value;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
static bool ShouldForceSampling() {
|
| 98 |
+
enum ForceState {
|
| 99 |
+
kDontForce,
|
| 100 |
+
kForce,
|
| 101 |
+
kUninitialized
|
| 102 |
+
};
|
| 103 |
+
ABSL_CONST_INIT static std::atomic<ForceState> global_state{
|
| 104 |
+
kUninitialized};
|
| 105 |
+
ForceState state = global_state.load(std::memory_order_relaxed);
|
| 106 |
+
if (ABSL_PREDICT_TRUE(state == kDontForce)) return false;
|
| 107 |
+
|
| 108 |
+
if (state == kUninitialized) {
|
| 109 |
+
state = ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)()
|
| 110 |
+
? kForce
|
| 111 |
+
: kDontForce;
|
| 112 |
+
global_state.store(state, std::memory_order_relaxed);
|
| 113 |
+
}
|
| 114 |
+
return state == kForce;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
HashtablezInfo* SampleSlow(SamplingState& next_sample,
|
| 118 |
+
size_t inline_element_size) {
|
| 119 |
+
if (ABSL_PREDICT_FALSE(ShouldForceSampling())) {
|
| 120 |
+
next_sample.next_sample = 1;
|
| 121 |
+
const int64_t old_stride = exchange(next_sample.sample_stride, 1);
|
| 122 |
+
HashtablezInfo* result =
|
| 123 |
+
GlobalHashtablezSampler().Register(old_stride, inline_element_size);
|
| 124 |
+
return result;
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
#if !defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 128 |
+
next_sample = {
|
| 129 |
+
std::numeric_limits<int64_t>::max(),
|
| 130 |
+
std::numeric_limits<int64_t>::max(),
|
| 131 |
+
};
|
| 132 |
+
return nullptr;
|
| 133 |
+
#else
|
| 134 |
+
bool first = next_sample.next_sample < 0;
|
| 135 |
+
|
| 136 |
+
const int64_t next_stride = g_exponential_biased_generator.GetStride(
|
| 137 |
+
g_hashtablez_sample_parameter.load(std::memory_order_relaxed));
|
| 138 |
+
|
| 139 |
+
next_sample.next_sample = next_stride;
|
| 140 |
+
const int64_t old_stride = exchange(next_sample.sample_stride, next_stride);
|
| 141 |
+
// Small values of interval are equivalent to just sampling next time.
|
| 142 |
+
ABSL_ASSERT(next_stride >= 1);
|
| 143 |
+
|
| 144 |
+
// g_hashtablez_enabled can be dynamically flipped, we need to set a threshold
|
| 145 |
+
// low enough that we will start sampling in a reasonable time, so we just use
|
| 146 |
+
// the default sampling rate.
|
| 147 |
+
if (!g_hashtablez_enabled.load(std::memory_order_relaxed)) return nullptr;
|
| 148 |
+
|
| 149 |
+
// We will only be negative on our first count, so we should just retry in
|
| 150 |
+
// that case.
|
| 151 |
+
if (first) {
|
| 152 |
+
if (ABSL_PREDICT_TRUE(--next_sample.next_sample > 0)) return nullptr;
|
| 153 |
+
return SampleSlow(next_sample, inline_element_size);
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
return GlobalHashtablezSampler().Register(old_stride, inline_element_size);
|
| 157 |
+
#endif
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
void UnsampleSlow(HashtablezInfo* info) {
|
| 161 |
+
GlobalHashtablezSampler().Unregister(info);
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) {
|
| 165 |
+
#ifdef ABSL_INTERNAL_HAVE_SSE2
|
| 166 |
+
total_probe_length /= 16;
|
| 167 |
+
#else
|
| 168 |
+
total_probe_length /= 8;
|
| 169 |
+
#endif
|
| 170 |
+
info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
|
| 171 |
+
info->num_erases.store(0, std::memory_order_relaxed);
|
| 172 |
+
// There is only one concurrent writer, so `load` then `store` is sufficient
|
| 173 |
+
// instead of using `fetch_add`.
|
| 174 |
+
info->num_rehashes.store(
|
| 175 |
+
1 + info->num_rehashes.load(std::memory_order_relaxed),
|
| 176 |
+
std::memory_order_relaxed);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity) {
|
| 180 |
+
info->max_reserve.store(
|
| 181 |
+
(std::max)(info->max_reserve.load(std::memory_order_relaxed),
|
| 182 |
+
target_capacity),
|
| 183 |
+
std::memory_order_relaxed);
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
void RecordClearedReservationSlow(HashtablezInfo* info) {
|
| 187 |
+
info->max_reserve.store(0, std::memory_order_relaxed);
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
|
| 191 |
+
size_t capacity) {
|
| 192 |
+
info->size.store(size, std::memory_order_relaxed);
|
| 193 |
+
info->capacity.store(capacity, std::memory_order_relaxed);
|
| 194 |
+
if (size == 0) {
|
| 195 |
+
// This is a clear, reset the total/num_erases too.
|
| 196 |
+
info->total_probe_length.store(0, std::memory_order_relaxed);
|
| 197 |
+
info->num_erases.store(0, std::memory_order_relaxed);
|
| 198 |
+
}
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
void RecordInsertSlow(HashtablezInfo* info, size_t hash,
|
| 202 |
+
size_t distance_from_desired) {
|
| 203 |
+
// SwissTables probe in groups of 16, so scale this to count items probes and
|
| 204 |
+
// not offset from desired.
|
| 205 |
+
size_t probe_length = distance_from_desired;
|
| 206 |
+
#ifdef ABSL_INTERNAL_HAVE_SSE2
|
| 207 |
+
probe_length /= 16;
|
| 208 |
+
#else
|
| 209 |
+
probe_length /= 8;
|
| 210 |
+
#endif
|
| 211 |
+
|
| 212 |
+
info->hashes_bitwise_and.fetch_and(hash, std::memory_order_relaxed);
|
| 213 |
+
info->hashes_bitwise_or.fetch_or(hash, std::memory_order_relaxed);
|
| 214 |
+
info->hashes_bitwise_xor.fetch_xor(hash, std::memory_order_relaxed);
|
| 215 |
+
info->max_probe_length.store(
|
| 216 |
+
std::max(info->max_probe_length.load(std::memory_order_relaxed),
|
| 217 |
+
probe_length),
|
| 218 |
+
std::memory_order_relaxed);
|
| 219 |
+
info->total_probe_length.fetch_add(probe_length, std::memory_order_relaxed);
|
| 220 |
+
info->size.fetch_add(1, std::memory_order_relaxed);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
void RecordEraseSlow(HashtablezInfo* info) {
|
| 224 |
+
info->size.fetch_sub(1, std::memory_order_relaxed);
|
| 225 |
+
// There is only one concurrent writer, so `load` then `store` is sufficient
|
| 226 |
+
// instead of using `fetch_add`.
|
| 227 |
+
info->num_erases.store(1 + info->num_erases.load(std::memory_order_relaxed),
|
| 228 |
+
std::memory_order_relaxed);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
void SetHashtablezConfigListener(HashtablezConfigListener l) {
|
| 232 |
+
g_hashtablez_config_listener.store(l, std::memory_order_release);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
bool IsHashtablezEnabled() {
|
| 236 |
+
return g_hashtablez_enabled.load(std::memory_order_acquire);
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
void SetHashtablezEnabled(bool enabled) {
|
| 240 |
+
SetHashtablezEnabledInternal(enabled);
|
| 241 |
+
TriggerHashtablezConfigListener();
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
void SetHashtablezEnabledInternal(bool enabled) {
|
| 245 |
+
g_hashtablez_enabled.store(enabled, std::memory_order_release);
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
int32_t GetHashtablezSampleParameter() {
|
| 249 |
+
return g_hashtablez_sample_parameter.load(std::memory_order_acquire);
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
void SetHashtablezSampleParameter(int32_t rate) {
|
| 253 |
+
SetHashtablezSampleParameterInternal(rate);
|
| 254 |
+
TriggerHashtablezConfigListener();
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
void SetHashtablezSampleParameterInternal(int32_t rate) {
|
| 258 |
+
if (rate > 0) {
|
| 259 |
+
g_hashtablez_sample_parameter.store(rate, std::memory_order_release);
|
| 260 |
+
} else {
|
| 261 |
+
ABSL_RAW_LOG(ERROR, "Invalid hashtablez sample rate: %lld",
|
| 262 |
+
static_cast<long long>(rate)); // NOLINT(runtime/int)
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
size_t GetHashtablezMaxSamples() {
|
| 267 |
+
return GlobalHashtablezSampler().GetMaxSamples();
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
void SetHashtablezMaxSamples(size_t max) {
|
| 271 |
+
SetHashtablezMaxSamplesInternal(max);
|
| 272 |
+
TriggerHashtablezConfigListener();
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
void SetHashtablezMaxSamplesInternal(size_t max) {
|
| 276 |
+
if (max > 0) {
|
| 277 |
+
GlobalHashtablezSampler().SetMaxSamples(max);
|
| 278 |
+
} else {
|
| 279 |
+
ABSL_RAW_LOG(ERROR, "Invalid hashtablez max samples: 0");
|
| 280 |
+
}
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
} // namespace container_internal
|
| 284 |
+
ABSL_NAMESPACE_END
|
| 285 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler.h
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// -----------------------------------------------------------------------------
|
| 16 |
+
// File: hashtablez_sampler.h
|
| 17 |
+
// -----------------------------------------------------------------------------
|
| 18 |
+
//
|
| 19 |
+
// This header file defines the API for a low level library to sample hashtables
|
| 20 |
+
// and collect runtime statistics about them.
|
| 21 |
+
//
|
| 22 |
+
// `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which
|
| 23 |
+
// store information about a single sample.
|
| 24 |
+
//
|
| 25 |
+
// `Record*` methods store information into samples.
|
| 26 |
+
// `Sample()` and `Unsample()` make use of a single global sampler with
|
| 27 |
+
// properties controlled by the flags hashtablez_enabled,
|
| 28 |
+
// hashtablez_sample_rate, and hashtablez_max_samples.
|
| 29 |
+
//
|
| 30 |
+
// WARNING
|
| 31 |
+
//
|
| 32 |
+
// Using this sampling API may cause sampled Swiss tables to use the global
|
| 33 |
+
// allocator (operator `new`) in addition to any custom allocator. If you
|
| 34 |
+
// are using a table in an unusual circumstance where allocation or calling a
|
| 35 |
+
// linux syscall is unacceptable, this could interfere.
|
| 36 |
+
//
|
| 37 |
+
// This utility is internal-only. Use at your own risk.
|
| 38 |
+
|
| 39 |
+
#ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
|
| 40 |
+
#define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
|
| 41 |
+
|
| 42 |
+
#include <atomic>
|
| 43 |
+
#include <functional>
|
| 44 |
+
#include <memory>
|
| 45 |
+
#include <vector>
|
| 46 |
+
|
| 47 |
+
#include "absl/base/config.h"
|
| 48 |
+
#include "absl/base/internal/per_thread_tls.h"
|
| 49 |
+
#include "absl/base/optimization.h"
|
| 50 |
+
#include "absl/profiling/internal/sample_recorder.h"
|
| 51 |
+
#include "absl/synchronization/mutex.h"
|
| 52 |
+
#include "absl/utility/utility.h"
|
| 53 |
+
|
| 54 |
+
namespace absl {
|
| 55 |
+
ABSL_NAMESPACE_BEGIN
|
| 56 |
+
namespace container_internal {
|
| 57 |
+
|
| 58 |
+
// Stores information about a sampled hashtable. All mutations to this *must*
|
| 59 |
+
// be made through `Record*` functions below. All reads from this *must* only
|
| 60 |
+
// occur in the callback to `HashtablezSampler::Iterate`.
|
| 61 |
+
struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> {
|
| 62 |
+
// Constructs the object but does not fill in any fields.
|
| 63 |
+
HashtablezInfo();
|
| 64 |
+
~HashtablezInfo();
|
| 65 |
+
HashtablezInfo(const HashtablezInfo&) = delete;
|
| 66 |
+
HashtablezInfo& operator=(const HashtablezInfo&) = delete;
|
| 67 |
+
|
| 68 |
+
// Puts the object into a clean state, fills in the logically `const` members,
|
| 69 |
+
// blocking for any readers that are currently sampling the object.
|
| 70 |
+
void PrepareForSampling(int64_t stride, size_t inline_element_size_value)
|
| 71 |
+
ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
|
| 72 |
+
|
| 73 |
+
// These fields are mutated by the various Record* APIs and need to be
|
| 74 |
+
// thread-safe.
|
| 75 |
+
std::atomic<size_t> capacity;
|
| 76 |
+
std::atomic<size_t> size;
|
| 77 |
+
std::atomic<size_t> num_erases;
|
| 78 |
+
std::atomic<size_t> num_rehashes;
|
| 79 |
+
std::atomic<size_t> max_probe_length;
|
| 80 |
+
std::atomic<size_t> total_probe_length;
|
| 81 |
+
std::atomic<size_t> hashes_bitwise_or;
|
| 82 |
+
std::atomic<size_t> hashes_bitwise_and;
|
| 83 |
+
std::atomic<size_t> hashes_bitwise_xor;
|
| 84 |
+
std::atomic<size_t> max_reserve;
|
| 85 |
+
|
| 86 |
+
// All of the fields below are set by `PrepareForSampling`, they must not be
|
| 87 |
+
// mutated in `Record*` functions. They are logically `const` in that sense.
|
| 88 |
+
// These are guarded by init_mu, but that is not externalized to clients,
|
| 89 |
+
// which can read them only during `SampleRecorder::Iterate` which will hold
|
| 90 |
+
// the lock.
|
| 91 |
+
static constexpr int kMaxStackDepth = 64;
|
| 92 |
+
absl::Time create_time;
|
| 93 |
+
int32_t depth;
|
| 94 |
+
void* stack[kMaxStackDepth];
|
| 95 |
+
size_t inline_element_size; // How big is the slot?
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length);
|
| 99 |
+
|
| 100 |
+
void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity);
|
| 101 |
+
|
| 102 |
+
void RecordClearedReservationSlow(HashtablezInfo* info);
|
| 103 |
+
|
| 104 |
+
void RecordStorageChangedSlow(HashtablezInfo* info, size_t size,
|
| 105 |
+
size_t capacity);
|
| 106 |
+
|
| 107 |
+
void RecordInsertSlow(HashtablezInfo* info, size_t hash,
|
| 108 |
+
size_t distance_from_desired);
|
| 109 |
+
|
| 110 |
+
void RecordEraseSlow(HashtablezInfo* info);
|
| 111 |
+
|
| 112 |
+
struct SamplingState {
|
| 113 |
+
int64_t next_sample;
|
| 114 |
+
// When we make a sampling decision, we record that distance so we can weight
|
| 115 |
+
// each sample.
|
| 116 |
+
int64_t sample_stride;
|
| 117 |
+
};
|
| 118 |
+
|
| 119 |
+
HashtablezInfo* SampleSlow(SamplingState& next_sample,
|
| 120 |
+
size_t inline_element_size);
|
| 121 |
+
void UnsampleSlow(HashtablezInfo* info);
|
| 122 |
+
|
| 123 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 124 |
+
#error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set
|
| 125 |
+
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 126 |
+
|
| 127 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 128 |
+
class HashtablezInfoHandle {
|
| 129 |
+
public:
|
| 130 |
+
explicit HashtablezInfoHandle() : info_(nullptr) {}
|
| 131 |
+
explicit HashtablezInfoHandle(HashtablezInfo* info) : info_(info) {}
|
| 132 |
+
|
| 133 |
+
// We do not have a destructor. Caller is responsible for calling Unregister
|
| 134 |
+
// before destroying the handle.
|
| 135 |
+
void Unregister() {
|
| 136 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 137 |
+
UnsampleSlow(info_);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
inline bool IsSampled() const { return ABSL_PREDICT_FALSE(info_ != nullptr); }
|
| 141 |
+
|
| 142 |
+
inline void RecordStorageChanged(size_t size, size_t capacity) {
|
| 143 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 144 |
+
RecordStorageChangedSlow(info_, size, capacity);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
inline void RecordRehash(size_t total_probe_length) {
|
| 148 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 149 |
+
RecordRehashSlow(info_, total_probe_length);
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
inline void RecordReservation(size_t target_capacity) {
|
| 153 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 154 |
+
RecordReservationSlow(info_, target_capacity);
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
inline void RecordClearedReservation() {
|
| 158 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 159 |
+
RecordClearedReservationSlow(info_);
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
inline void RecordInsert(size_t hash, size_t distance_from_desired) {
|
| 163 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 164 |
+
RecordInsertSlow(info_, hash, distance_from_desired);
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
inline void RecordErase() {
|
| 168 |
+
if (ABSL_PREDICT_TRUE(info_ == nullptr)) return;
|
| 169 |
+
RecordEraseSlow(info_);
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
friend inline void swap(HashtablezInfoHandle& lhs,
|
| 173 |
+
HashtablezInfoHandle& rhs) {
|
| 174 |
+
std::swap(lhs.info_, rhs.info_);
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
private:
|
| 178 |
+
friend class HashtablezInfoHandlePeer;
|
| 179 |
+
HashtablezInfo* info_;
|
| 180 |
+
};
|
| 181 |
+
#else
|
| 182 |
+
// Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can
|
| 183 |
+
// be removed by the linker, in order to reduce the binary size.
|
| 184 |
+
class HashtablezInfoHandle {
|
| 185 |
+
public:
|
| 186 |
+
explicit HashtablezInfoHandle() = default;
|
| 187 |
+
explicit HashtablezInfoHandle(std::nullptr_t) {}
|
| 188 |
+
|
| 189 |
+
inline void Unregister() {}
|
| 190 |
+
inline bool IsSampled() const { return false; }
|
| 191 |
+
inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) {}
|
| 192 |
+
inline void RecordRehash(size_t /*total_probe_length*/) {}
|
| 193 |
+
inline void RecordReservation(size_t /*target_capacity*/) {}
|
| 194 |
+
inline void RecordClearedReservation() {}
|
| 195 |
+
inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) {}
|
| 196 |
+
inline void RecordErase() {}
|
| 197 |
+
|
| 198 |
+
friend inline void swap(HashtablezInfoHandle& /*lhs*/,
|
| 199 |
+
HashtablezInfoHandle& /*rhs*/) {}
|
| 200 |
+
};
|
| 201 |
+
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 202 |
+
|
| 203 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 204 |
+
extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample;
|
| 205 |
+
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 206 |
+
|
| 207 |
+
// Returns an RAII sampling handle that manages registration and unregistation
|
| 208 |
+
// with the global sampler.
|
| 209 |
+
inline HashtablezInfoHandle Sample(
|
| 210 |
+
size_t inline_element_size ABSL_ATTRIBUTE_UNUSED) {
|
| 211 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 212 |
+
if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) {
|
| 213 |
+
return HashtablezInfoHandle(nullptr);
|
| 214 |
+
}
|
| 215 |
+
return HashtablezInfoHandle(
|
| 216 |
+
SampleSlow(global_next_sample, inline_element_size));
|
| 217 |
+
#else
|
| 218 |
+
return HashtablezInfoHandle(nullptr);
|
| 219 |
+
#endif // !ABSL_PER_THREAD_TLS
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
using HashtablezSampler =
|
| 223 |
+
::absl::profiling_internal::SampleRecorder<HashtablezInfo>;
|
| 224 |
+
|
| 225 |
+
// Returns a global Sampler.
|
| 226 |
+
HashtablezSampler& GlobalHashtablezSampler();
|
| 227 |
+
|
| 228 |
+
using HashtablezConfigListener = void (*)();
|
| 229 |
+
void SetHashtablezConfigListener(HashtablezConfigListener l);
|
| 230 |
+
|
| 231 |
+
// Enables or disables sampling for Swiss tables.
|
| 232 |
+
bool IsHashtablezEnabled();
|
| 233 |
+
void SetHashtablezEnabled(bool enabled);
|
| 234 |
+
void SetHashtablezEnabledInternal(bool enabled);
|
| 235 |
+
|
| 236 |
+
// Sets the rate at which Swiss tables will be sampled.
|
| 237 |
+
int32_t GetHashtablezSampleParameter();
|
| 238 |
+
void SetHashtablezSampleParameter(int32_t rate);
|
| 239 |
+
void SetHashtablezSampleParameterInternal(int32_t rate);
|
| 240 |
+
|
| 241 |
+
// Sets a soft max for the number of samples that will be kept.
|
| 242 |
+
size_t GetHashtablezMaxSamples();
|
| 243 |
+
void SetHashtablezMaxSamples(size_t max);
|
| 244 |
+
void SetHashtablezMaxSamplesInternal(size_t max);
|
| 245 |
+
|
| 246 |
+
// Configuration override.
|
| 247 |
+
// This allows process-wide sampling without depending on order of
|
| 248 |
+
// initialization of static storage duration objects.
|
| 249 |
+
// The definition of this constant is weak, which allows us to inject a
|
| 250 |
+
// different value for it at link time.
|
| 251 |
+
extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)();
|
| 252 |
+
|
| 253 |
+
} // namespace container_internal
|
| 254 |
+
ABSL_NAMESPACE_END
|
| 255 |
+
} // namespace absl
|
| 256 |
+
|
| 257 |
+
#endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
|
weight/_dep/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/hashtablez_sampler.h"
|
| 16 |
+
|
| 17 |
+
#include <atomic>
|
| 18 |
+
#include <limits>
|
| 19 |
+
#include <random>
|
| 20 |
+
|
| 21 |
+
#include "gmock/gmock.h"
|
| 22 |
+
#include "gtest/gtest.h"
|
| 23 |
+
#include "absl/base/attributes.h"
|
| 24 |
+
#include "absl/base/config.h"
|
| 25 |
+
#include "absl/profiling/internal/sample_recorder.h"
|
| 26 |
+
#include "absl/synchronization/blocking_counter.h"
|
| 27 |
+
#include "absl/synchronization/internal/thread_pool.h"
|
| 28 |
+
#include "absl/synchronization/mutex.h"
|
| 29 |
+
#include "absl/synchronization/notification.h"
|
| 30 |
+
#include "absl/time/clock.h"
|
| 31 |
+
#include "absl/time/time.h"
|
| 32 |
+
|
| 33 |
+
#ifdef ABSL_INTERNAL_HAVE_SSE2
|
| 34 |
+
constexpr int kProbeLength = 16;
|
| 35 |
+
#else
|
| 36 |
+
constexpr int kProbeLength = 8;
|
| 37 |
+
#endif
|
| 38 |
+
|
| 39 |
+
namespace absl {
|
| 40 |
+
ABSL_NAMESPACE_BEGIN
|
| 41 |
+
namespace container_internal {
|
| 42 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 43 |
+
class HashtablezInfoHandlePeer {
|
| 44 |
+
public:
|
| 45 |
+
static HashtablezInfo* GetInfo(HashtablezInfoHandle* h) { return h->info_; }
|
| 46 |
+
};
|
| 47 |
+
#else
|
| 48 |
+
class HashtablezInfoHandlePeer {
|
| 49 |
+
public:
|
| 50 |
+
static HashtablezInfo* GetInfo(HashtablezInfoHandle*) { return nullptr; }
|
| 51 |
+
};
|
| 52 |
+
#endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 53 |
+
|
| 54 |
+
namespace {
|
| 55 |
+
using ::absl::synchronization_internal::ThreadPool;
|
| 56 |
+
using ::testing::IsEmpty;
|
| 57 |
+
using ::testing::UnorderedElementsAre;
|
| 58 |
+
|
| 59 |
+
std::vector<size_t> GetSizes(HashtablezSampler* s) {
|
| 60 |
+
std::vector<size_t> res;
|
| 61 |
+
s->Iterate([&](const HashtablezInfo& info) {
|
| 62 |
+
res.push_back(info.size.load(std::memory_order_acquire));
|
| 63 |
+
});
|
| 64 |
+
return res;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
HashtablezInfo* Register(HashtablezSampler* s, size_t size) {
|
| 68 |
+
const int64_t test_stride = 123;
|
| 69 |
+
const size_t test_element_size = 17;
|
| 70 |
+
auto* info = s->Register(test_stride, test_element_size);
|
| 71 |
+
assert(info != nullptr);
|
| 72 |
+
info->size.store(size);
|
| 73 |
+
return info;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
TEST(HashtablezInfoTest, PrepareForSampling) {
|
| 77 |
+
absl::Time test_start = absl::Now();
|
| 78 |
+
const int64_t test_stride = 123;
|
| 79 |
+
const size_t test_element_size = 17;
|
| 80 |
+
HashtablezInfo info;
|
| 81 |
+
absl::MutexLock l(&info.init_mu);
|
| 82 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 83 |
+
|
| 84 |
+
EXPECT_EQ(info.capacity.load(), 0);
|
| 85 |
+
EXPECT_EQ(info.size.load(), 0);
|
| 86 |
+
EXPECT_EQ(info.num_erases.load(), 0);
|
| 87 |
+
EXPECT_EQ(info.num_rehashes.load(), 0);
|
| 88 |
+
EXPECT_EQ(info.max_probe_length.load(), 0);
|
| 89 |
+
EXPECT_EQ(info.total_probe_length.load(), 0);
|
| 90 |
+
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
|
| 91 |
+
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
|
| 92 |
+
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
|
| 93 |
+
EXPECT_EQ(info.max_reserve.load(), 0);
|
| 94 |
+
EXPECT_GE(info.create_time, test_start);
|
| 95 |
+
EXPECT_EQ(info.weight, test_stride);
|
| 96 |
+
EXPECT_EQ(info.inline_element_size, test_element_size);
|
| 97 |
+
|
| 98 |
+
info.capacity.store(1, std::memory_order_relaxed);
|
| 99 |
+
info.size.store(1, std::memory_order_relaxed);
|
| 100 |
+
info.num_erases.store(1, std::memory_order_relaxed);
|
| 101 |
+
info.max_probe_length.store(1, std::memory_order_relaxed);
|
| 102 |
+
info.total_probe_length.store(1, std::memory_order_relaxed);
|
| 103 |
+
info.hashes_bitwise_or.store(1, std::memory_order_relaxed);
|
| 104 |
+
info.hashes_bitwise_and.store(1, std::memory_order_relaxed);
|
| 105 |
+
info.hashes_bitwise_xor.store(1, std::memory_order_relaxed);
|
| 106 |
+
info.max_reserve.store(1, std::memory_order_relaxed);
|
| 107 |
+
info.create_time = test_start - absl::Hours(20);
|
| 108 |
+
|
| 109 |
+
info.PrepareForSampling(test_stride * 2, test_element_size);
|
| 110 |
+
EXPECT_EQ(info.capacity.load(), 0);
|
| 111 |
+
EXPECT_EQ(info.size.load(), 0);
|
| 112 |
+
EXPECT_EQ(info.num_erases.load(), 0);
|
| 113 |
+
EXPECT_EQ(info.num_rehashes.load(), 0);
|
| 114 |
+
EXPECT_EQ(info.max_probe_length.load(), 0);
|
| 115 |
+
EXPECT_EQ(info.total_probe_length.load(), 0);
|
| 116 |
+
EXPECT_EQ(info.hashes_bitwise_or.load(), 0);
|
| 117 |
+
EXPECT_EQ(info.hashes_bitwise_and.load(), ~size_t{});
|
| 118 |
+
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0);
|
| 119 |
+
EXPECT_EQ(info.max_reserve.load(), 0);
|
| 120 |
+
EXPECT_EQ(info.weight, 2 * test_stride);
|
| 121 |
+
EXPECT_EQ(info.inline_element_size, test_element_size);
|
| 122 |
+
EXPECT_GE(info.create_time, test_start);
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
TEST(HashtablezInfoTest, RecordStorageChanged) {
|
| 126 |
+
HashtablezInfo info;
|
| 127 |
+
absl::MutexLock l(&info.init_mu);
|
| 128 |
+
const int64_t test_stride = 21;
|
| 129 |
+
const size_t test_element_size = 19;
|
| 130 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 131 |
+
RecordStorageChangedSlow(&info, 17, 47);
|
| 132 |
+
EXPECT_EQ(info.size.load(), 17);
|
| 133 |
+
EXPECT_EQ(info.capacity.load(), 47);
|
| 134 |
+
RecordStorageChangedSlow(&info, 20, 20);
|
| 135 |
+
EXPECT_EQ(info.size.load(), 20);
|
| 136 |
+
EXPECT_EQ(info.capacity.load(), 20);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
TEST(HashtablezInfoTest, RecordInsert) {
|
| 140 |
+
HashtablezInfo info;
|
| 141 |
+
absl::MutexLock l(&info.init_mu);
|
| 142 |
+
const int64_t test_stride = 25;
|
| 143 |
+
const size_t test_element_size = 23;
|
| 144 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 145 |
+
EXPECT_EQ(info.max_probe_length.load(), 0);
|
| 146 |
+
RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
|
| 147 |
+
EXPECT_EQ(info.max_probe_length.load(), 6);
|
| 148 |
+
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000FF00);
|
| 149 |
+
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x0000FF00);
|
| 150 |
+
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x0000FF00);
|
| 151 |
+
RecordInsertSlow(&info, 0x000FF000, 4 * kProbeLength);
|
| 152 |
+
EXPECT_EQ(info.max_probe_length.load(), 6);
|
| 153 |
+
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x0000F000);
|
| 154 |
+
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x000FFF00);
|
| 155 |
+
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x000F0F00);
|
| 156 |
+
RecordInsertSlow(&info, 0x00FF0000, 12 * kProbeLength);
|
| 157 |
+
EXPECT_EQ(info.max_probe_length.load(), 12);
|
| 158 |
+
EXPECT_EQ(info.hashes_bitwise_and.load(), 0x00000000);
|
| 159 |
+
EXPECT_EQ(info.hashes_bitwise_or.load(), 0x00FFFF00);
|
| 160 |
+
EXPECT_EQ(info.hashes_bitwise_xor.load(), 0x00F00F00);
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
TEST(HashtablezInfoTest, RecordErase) {
|
| 164 |
+
const int64_t test_stride = 31;
|
| 165 |
+
const size_t test_element_size = 29;
|
| 166 |
+
HashtablezInfo info;
|
| 167 |
+
absl::MutexLock l(&info.init_mu);
|
| 168 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 169 |
+
EXPECT_EQ(info.num_erases.load(), 0);
|
| 170 |
+
EXPECT_EQ(info.size.load(), 0);
|
| 171 |
+
RecordInsertSlow(&info, 0x0000FF00, 6 * kProbeLength);
|
| 172 |
+
EXPECT_EQ(info.size.load(), 1);
|
| 173 |
+
RecordEraseSlow(&info);
|
| 174 |
+
EXPECT_EQ(info.size.load(), 0);
|
| 175 |
+
EXPECT_EQ(info.num_erases.load(), 1);
|
| 176 |
+
EXPECT_EQ(info.inline_element_size, test_element_size);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
TEST(HashtablezInfoTest, RecordRehash) {
|
| 180 |
+
const int64_t test_stride = 33;
|
| 181 |
+
const size_t test_element_size = 31;
|
| 182 |
+
HashtablezInfo info;
|
| 183 |
+
absl::MutexLock l(&info.init_mu);
|
| 184 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 185 |
+
RecordInsertSlow(&info, 0x1, 0);
|
| 186 |
+
RecordInsertSlow(&info, 0x2, kProbeLength);
|
| 187 |
+
RecordInsertSlow(&info, 0x4, kProbeLength);
|
| 188 |
+
RecordInsertSlow(&info, 0x8, 2 * kProbeLength);
|
| 189 |
+
EXPECT_EQ(info.size.load(), 4);
|
| 190 |
+
EXPECT_EQ(info.total_probe_length.load(), 4);
|
| 191 |
+
|
| 192 |
+
RecordEraseSlow(&info);
|
| 193 |
+
RecordEraseSlow(&info);
|
| 194 |
+
EXPECT_EQ(info.size.load(), 2);
|
| 195 |
+
EXPECT_EQ(info.total_probe_length.load(), 4);
|
| 196 |
+
EXPECT_EQ(info.num_erases.load(), 2);
|
| 197 |
+
|
| 198 |
+
RecordRehashSlow(&info, 3 * kProbeLength);
|
| 199 |
+
EXPECT_EQ(info.size.load(), 2);
|
| 200 |
+
EXPECT_EQ(info.total_probe_length.load(), 3);
|
| 201 |
+
EXPECT_EQ(info.num_erases.load(), 0);
|
| 202 |
+
EXPECT_EQ(info.num_rehashes.load(), 1);
|
| 203 |
+
EXPECT_EQ(info.inline_element_size, test_element_size);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
TEST(HashtablezInfoTest, RecordReservation) {
|
| 207 |
+
HashtablezInfo info;
|
| 208 |
+
absl::MutexLock l(&info.init_mu);
|
| 209 |
+
const int64_t test_stride = 35;
|
| 210 |
+
const size_t test_element_size = 33;
|
| 211 |
+
info.PrepareForSampling(test_stride, test_element_size);
|
| 212 |
+
RecordReservationSlow(&info, 3);
|
| 213 |
+
EXPECT_EQ(info.max_reserve.load(), 3);
|
| 214 |
+
|
| 215 |
+
RecordReservationSlow(&info, 2);
|
| 216 |
+
// High watermark does not change
|
| 217 |
+
EXPECT_EQ(info.max_reserve.load(), 3);
|
| 218 |
+
|
| 219 |
+
RecordReservationSlow(&info, 10);
|
| 220 |
+
// High watermark does change
|
| 221 |
+
EXPECT_EQ(info.max_reserve.load(), 10);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 225 |
+
TEST(HashtablezSamplerTest, SmallSampleParameter) {
|
| 226 |
+
const size_t test_element_size = 31;
|
| 227 |
+
SetHashtablezEnabled(true);
|
| 228 |
+
SetHashtablezSampleParameter(100);
|
| 229 |
+
|
| 230 |
+
for (int i = 0; i < 1000; ++i) {
|
| 231 |
+
SamplingState next_sample = {0, 0};
|
| 232 |
+
HashtablezInfo* sample = SampleSlow(next_sample, test_element_size);
|
| 233 |
+
EXPECT_GT(next_sample.next_sample, 0);
|
| 234 |
+
EXPECT_EQ(next_sample.next_sample, next_sample.sample_stride);
|
| 235 |
+
EXPECT_NE(sample, nullptr);
|
| 236 |
+
UnsampleSlow(sample);
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
TEST(HashtablezSamplerTest, LargeSampleParameter) {
|
| 241 |
+
const size_t test_element_size = 31;
|
| 242 |
+
SetHashtablezEnabled(true);
|
| 243 |
+
SetHashtablezSampleParameter(std::numeric_limits<int32_t>::max());
|
| 244 |
+
|
| 245 |
+
for (int i = 0; i < 1000; ++i) {
|
| 246 |
+
SamplingState next_sample = {0, 0};
|
| 247 |
+
HashtablezInfo* sample = SampleSlow(next_sample, test_element_size);
|
| 248 |
+
EXPECT_GT(next_sample.next_sample, 0);
|
| 249 |
+
EXPECT_EQ(next_sample.next_sample, next_sample.sample_stride);
|
| 250 |
+
EXPECT_NE(sample, nullptr);
|
| 251 |
+
UnsampleSlow(sample);
|
| 252 |
+
}
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
TEST(HashtablezSamplerTest, Sample) {
|
| 256 |
+
const size_t test_element_size = 31;
|
| 257 |
+
SetHashtablezEnabled(true);
|
| 258 |
+
SetHashtablezSampleParameter(100);
|
| 259 |
+
int64_t num_sampled = 0;
|
| 260 |
+
int64_t total = 0;
|
| 261 |
+
double sample_rate = 0.0;
|
| 262 |
+
for (int i = 0; i < 1000000; ++i) {
|
| 263 |
+
HashtablezInfoHandle h = Sample(test_element_size);
|
| 264 |
+
++total;
|
| 265 |
+
if (h.IsSampled()) {
|
| 266 |
+
++num_sampled;
|
| 267 |
+
}
|
| 268 |
+
sample_rate = static_cast<double>(num_sampled) / total;
|
| 269 |
+
if (0.005 < sample_rate && sample_rate < 0.015) break;
|
| 270 |
+
}
|
| 271 |
+
EXPECT_NEAR(sample_rate, 0.01, 0.005);
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
TEST(HashtablezSamplerTest, Handle) {
|
| 275 |
+
auto& sampler = GlobalHashtablezSampler();
|
| 276 |
+
const int64_t test_stride = 41;
|
| 277 |
+
const size_t test_element_size = 39;
|
| 278 |
+
HashtablezInfoHandle h(sampler.Register(test_stride, test_element_size));
|
| 279 |
+
auto* info = HashtablezInfoHandlePeer::GetInfo(&h);
|
| 280 |
+
info->hashes_bitwise_and.store(0x12345678, std::memory_order_relaxed);
|
| 281 |
+
|
| 282 |
+
bool found = false;
|
| 283 |
+
sampler.Iterate([&](const HashtablezInfo& h) {
|
| 284 |
+
if (&h == info) {
|
| 285 |
+
EXPECT_EQ(h.weight, test_stride);
|
| 286 |
+
EXPECT_EQ(h.hashes_bitwise_and.load(), 0x12345678);
|
| 287 |
+
found = true;
|
| 288 |
+
}
|
| 289 |
+
});
|
| 290 |
+
EXPECT_TRUE(found);
|
| 291 |
+
|
| 292 |
+
h.Unregister();
|
| 293 |
+
h = HashtablezInfoHandle();
|
| 294 |
+
found = false;
|
| 295 |
+
sampler.Iterate([&](const HashtablezInfo& h) {
|
| 296 |
+
if (&h == info) {
|
| 297 |
+
// this will only happen if some other thread has resurrected the info
|
| 298 |
+
// the old handle was using.
|
| 299 |
+
if (h.hashes_bitwise_and.load() == 0x12345678) {
|
| 300 |
+
found = true;
|
| 301 |
+
}
|
| 302 |
+
}
|
| 303 |
+
});
|
| 304 |
+
EXPECT_FALSE(found);
|
| 305 |
+
}
|
| 306 |
+
#endif
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
TEST(HashtablezSamplerTest, Registration) {
|
| 310 |
+
HashtablezSampler sampler;
|
| 311 |
+
auto* info1 = Register(&sampler, 1);
|
| 312 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1));
|
| 313 |
+
|
| 314 |
+
auto* info2 = Register(&sampler, 2);
|
| 315 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(1, 2));
|
| 316 |
+
info1->size.store(3);
|
| 317 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(3, 2));
|
| 318 |
+
|
| 319 |
+
sampler.Unregister(info1);
|
| 320 |
+
sampler.Unregister(info2);
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
TEST(HashtablezSamplerTest, Unregistration) {
|
| 324 |
+
HashtablezSampler sampler;
|
| 325 |
+
std::vector<HashtablezInfo*> infos;
|
| 326 |
+
for (size_t i = 0; i < 3; ++i) {
|
| 327 |
+
infos.push_back(Register(&sampler, i));
|
| 328 |
+
}
|
| 329 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 1, 2));
|
| 330 |
+
|
| 331 |
+
sampler.Unregister(infos[1]);
|
| 332 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2));
|
| 333 |
+
|
| 334 |
+
infos.push_back(Register(&sampler, 3));
|
| 335 |
+
infos.push_back(Register(&sampler, 4));
|
| 336 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 3, 4));
|
| 337 |
+
sampler.Unregister(infos[3]);
|
| 338 |
+
EXPECT_THAT(GetSizes(&sampler), UnorderedElementsAre(0, 2, 4));
|
| 339 |
+
|
| 340 |
+
sampler.Unregister(infos[0]);
|
| 341 |
+
sampler.Unregister(infos[2]);
|
| 342 |
+
sampler.Unregister(infos[4]);
|
| 343 |
+
EXPECT_THAT(GetSizes(&sampler), IsEmpty());
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
TEST(HashtablezSamplerTest, MultiThreaded) {
|
| 347 |
+
HashtablezSampler sampler;
|
| 348 |
+
Notification stop;
|
| 349 |
+
ThreadPool pool(10);
|
| 350 |
+
|
| 351 |
+
for (int i = 0; i < 10; ++i) {
|
| 352 |
+
const int64_t sampling_stride = 11 + i % 3;
|
| 353 |
+
const size_t elt_size = 10 + i % 2;
|
| 354 |
+
pool.Schedule([&sampler, &stop, sampling_stride, elt_size]() {
|
| 355 |
+
std::random_device rd;
|
| 356 |
+
std::mt19937 gen(rd());
|
| 357 |
+
|
| 358 |
+
std::vector<HashtablezInfo*> infoz;
|
| 359 |
+
while (!stop.HasBeenNotified()) {
|
| 360 |
+
if (infoz.empty()) {
|
| 361 |
+
infoz.push_back(sampler.Register(sampling_stride, elt_size));
|
| 362 |
+
}
|
| 363 |
+
switch (std::uniform_int_distribution<>(0, 2)(gen)) {
|
| 364 |
+
case 0: {
|
| 365 |
+
infoz.push_back(sampler.Register(sampling_stride, elt_size));
|
| 366 |
+
break;
|
| 367 |
+
}
|
| 368 |
+
case 1: {
|
| 369 |
+
size_t p =
|
| 370 |
+
std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
|
| 371 |
+
HashtablezInfo* info = infoz[p];
|
| 372 |
+
infoz[p] = infoz.back();
|
| 373 |
+
infoz.pop_back();
|
| 374 |
+
EXPECT_EQ(info->weight, sampling_stride);
|
| 375 |
+
sampler.Unregister(info);
|
| 376 |
+
break;
|
| 377 |
+
}
|
| 378 |
+
case 2: {
|
| 379 |
+
absl::Duration oldest = absl::ZeroDuration();
|
| 380 |
+
sampler.Iterate([&](const HashtablezInfo& info) {
|
| 381 |
+
oldest = std::max(oldest, absl::Now() - info.create_time);
|
| 382 |
+
});
|
| 383 |
+
ASSERT_GE(oldest, absl::ZeroDuration());
|
| 384 |
+
break;
|
| 385 |
+
}
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
});
|
| 389 |
+
}
|
| 390 |
+
// The threads will hammer away. Give it a little bit of time for tsan to
|
| 391 |
+
// spot errors.
|
| 392 |
+
absl::SleepFor(absl::Seconds(3));
|
| 393 |
+
stop.Notify();
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
TEST(HashtablezSamplerTest, Callback) {
|
| 397 |
+
HashtablezSampler sampler;
|
| 398 |
+
|
| 399 |
+
auto* info1 = Register(&sampler, 1);
|
| 400 |
+
auto* info2 = Register(&sampler, 2);
|
| 401 |
+
|
| 402 |
+
static const HashtablezInfo* expected;
|
| 403 |
+
|
| 404 |
+
auto callback = [](const HashtablezInfo& info) {
|
| 405 |
+
// We can't use `info` outside of this callback because the object will be
|
| 406 |
+
// disposed as soon as we return from here.
|
| 407 |
+
EXPECT_EQ(&info, expected);
|
| 408 |
+
};
|
| 409 |
+
|
| 410 |
+
// Set the callback.
|
| 411 |
+
EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
|
| 412 |
+
expected = info1;
|
| 413 |
+
sampler.Unregister(info1);
|
| 414 |
+
|
| 415 |
+
// Unset the callback.
|
| 416 |
+
EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
|
| 417 |
+
expected = nullptr; // no more calls.
|
| 418 |
+
sampler.Unregister(info2);
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
} // namespace
|
| 422 |
+
} // namespace container_internal
|
| 423 |
+
ABSL_NAMESPACE_END
|
| 424 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/layout_benchmark.cc
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// Every benchmark should have the same performance as the corresponding
|
| 16 |
+
// headroom benchmark.
|
| 17 |
+
|
| 18 |
+
#include "absl/base/internal/raw_logging.h"
|
| 19 |
+
#include "absl/container/internal/layout.h"
|
| 20 |
+
#include "benchmark/benchmark.h"
|
| 21 |
+
|
| 22 |
+
namespace absl {
|
| 23 |
+
ABSL_NAMESPACE_BEGIN
|
| 24 |
+
namespace container_internal {
|
| 25 |
+
namespace {
|
| 26 |
+
|
| 27 |
+
using ::benchmark::DoNotOptimize;
|
| 28 |
+
|
| 29 |
+
using Int128 = int64_t[2];
|
| 30 |
+
|
| 31 |
+
// This benchmark provides the upper bound on performance for BM_OffsetConstant.
|
| 32 |
+
template <size_t Offset, class... Ts>
|
| 33 |
+
void BM_OffsetConstantHeadroom(benchmark::State& state) {
|
| 34 |
+
for (auto _ : state) {
|
| 35 |
+
DoNotOptimize(Offset);
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
template <size_t Offset, class... Ts>
|
| 40 |
+
void BM_OffsetConstant(benchmark::State& state) {
|
| 41 |
+
using L = Layout<Ts...>;
|
| 42 |
+
ABSL_RAW_CHECK(L::Partial(3, 5, 7).template Offset<3>() == Offset,
|
| 43 |
+
"Invalid offset");
|
| 44 |
+
for (auto _ : state) {
|
| 45 |
+
DoNotOptimize(L::Partial(3, 5, 7).template Offset<3>());
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
template <class... Ts>
|
| 50 |
+
size_t VariableOffset(size_t n, size_t m, size_t k);
|
| 51 |
+
|
| 52 |
+
template <>
|
| 53 |
+
size_t VariableOffset<int8_t, int16_t, int32_t, Int128>(size_t n, size_t m,
|
| 54 |
+
size_t k) {
|
| 55 |
+
auto Align = [](size_t n, size_t m) { return (n + m - 1) & ~(m - 1); };
|
| 56 |
+
return Align(Align(Align(n * 1, 2) + m * 2, 4) + k * 4, 8);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
template <>
|
| 60 |
+
size_t VariableOffset<Int128, int32_t, int16_t, int8_t>(size_t n, size_t m,
|
| 61 |
+
size_t k) {
|
| 62 |
+
// No alignment is necessary.
|
| 63 |
+
return n * 16 + m * 4 + k * 2;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
// This benchmark provides the upper bound on performance for BM_OffsetVariable.
|
| 67 |
+
template <size_t Offset, class... Ts>
|
| 68 |
+
void BM_OffsetVariableHeadroom(benchmark::State& state) {
|
| 69 |
+
size_t n = 3;
|
| 70 |
+
size_t m = 5;
|
| 71 |
+
size_t k = 7;
|
| 72 |
+
ABSL_RAW_CHECK(VariableOffset<Ts...>(n, m, k) == Offset, "Invalid offset");
|
| 73 |
+
for (auto _ : state) {
|
| 74 |
+
DoNotOptimize(n);
|
| 75 |
+
DoNotOptimize(m);
|
| 76 |
+
DoNotOptimize(k);
|
| 77 |
+
DoNotOptimize(VariableOffset<Ts...>(n, m, k));
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
template <size_t Offset, class... Ts>
|
| 82 |
+
void BM_OffsetVariable(benchmark::State& state) {
|
| 83 |
+
using L = Layout<Ts...>;
|
| 84 |
+
size_t n = 3;
|
| 85 |
+
size_t m = 5;
|
| 86 |
+
size_t k = 7;
|
| 87 |
+
ABSL_RAW_CHECK(L::Partial(n, m, k).template Offset<3>() == Offset,
|
| 88 |
+
"Invalid offset");
|
| 89 |
+
for (auto _ : state) {
|
| 90 |
+
DoNotOptimize(n);
|
| 91 |
+
DoNotOptimize(m);
|
| 92 |
+
DoNotOptimize(k);
|
| 93 |
+
DoNotOptimize(L::Partial(n, m, k).template Offset<3>());
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
// Run all benchmarks in two modes:
|
| 98 |
+
//
|
| 99 |
+
// Layout with padding: int8_t[3], int16_t[5], int32_t[7], Int128[?].
|
| 100 |
+
// Layout without padding: Int128[3], int32_t[5], int16_t[7], int8_t[?].
|
| 101 |
+
|
| 102 |
+
#define OFFSET_BENCHMARK(NAME, OFFSET, T1, T2, T3, T4) \
|
| 103 |
+
auto& NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4 = \
|
| 104 |
+
NAME<OFFSET, T1, T2, T3, T4>; \
|
| 105 |
+
BENCHMARK(NAME##_##OFFSET##_##T1##_##T2##_##T3##_##T4)
|
| 106 |
+
|
| 107 |
+
OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 48, int8_t, int16_t, int32_t,
|
| 108 |
+
Int128);
|
| 109 |
+
OFFSET_BENCHMARK(BM_OffsetConstant, 48, int8_t, int16_t, int32_t, Int128);
|
| 110 |
+
OFFSET_BENCHMARK(BM_OffsetConstantHeadroom, 82, Int128, int32_t, int16_t,
|
| 111 |
+
int8_t);
|
| 112 |
+
OFFSET_BENCHMARK(BM_OffsetConstant, 82, Int128, int32_t, int16_t, int8_t);
|
| 113 |
+
OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 48, int8_t, int16_t, int32_t,
|
| 114 |
+
Int128);
|
| 115 |
+
OFFSET_BENCHMARK(BM_OffsetVariable, 48, int8_t, int16_t, int32_t, Int128);
|
| 116 |
+
OFFSET_BENCHMARK(BM_OffsetVariableHeadroom, 82, Int128, int32_t, int16_t,
|
| 117 |
+
int8_t);
|
| 118 |
+
OFFSET_BENCHMARK(BM_OffsetVariable, 82, Int128, int32_t, int16_t, int8_t);
|
| 119 |
+
} // namespace
|
| 120 |
+
} // namespace container_internal
|
| 121 |
+
ABSL_NAMESPACE_END
|
| 122 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/layout_test.cc
ADDED
|
@@ -0,0 +1,1646 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/layout.h"
|
| 16 |
+
|
| 17 |
+
// We need ::max_align_t because some libstdc++ versions don't provide
|
| 18 |
+
// std::max_align_t
|
| 19 |
+
#include <stddef.h>
|
| 20 |
+
|
| 21 |
+
#include <cstdint>
|
| 22 |
+
#include <cstring>
|
| 23 |
+
#include <initializer_list>
|
| 24 |
+
#include <memory>
|
| 25 |
+
#include <ostream>
|
| 26 |
+
#include <string>
|
| 27 |
+
#include <tuple>
|
| 28 |
+
#include <type_traits>
|
| 29 |
+
|
| 30 |
+
#include "gmock/gmock.h"
|
| 31 |
+
#include "gtest/gtest.h"
|
| 32 |
+
#include "absl/base/config.h"
|
| 33 |
+
#include "absl/log/check.h"
|
| 34 |
+
#include "absl/types/span.h"
|
| 35 |
+
#include "absl/utility/utility.h"
|
| 36 |
+
|
| 37 |
+
namespace absl {
|
| 38 |
+
ABSL_NAMESPACE_BEGIN
|
| 39 |
+
namespace container_internal {
|
| 40 |
+
namespace {
|
| 41 |
+
|
| 42 |
+
using ::absl::Span;
|
| 43 |
+
using ::testing::ElementsAre;
|
| 44 |
+
|
| 45 |
+
size_t Distance(const void* from, const void* to) {
|
| 46 |
+
CHECK_LE(from, to) << "Distance must be non-negative";
|
| 47 |
+
return static_cast<const char*>(to) - static_cast<const char*>(from);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
template <class Expected, class Actual>
|
| 51 |
+
Expected Type(Actual val) {
|
| 52 |
+
static_assert(std::is_same<Expected, Actual>(), "");
|
| 53 |
+
return val;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
// Helper classes to test different size and alignments.
|
| 57 |
+
struct alignas(8) Int128 {
|
| 58 |
+
uint64_t a, b;
|
| 59 |
+
friend bool operator==(Int128 lhs, Int128 rhs) {
|
| 60 |
+
return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
static std::string Name() {
|
| 64 |
+
return internal_layout::adl_barrier::TypeName<Int128>();
|
| 65 |
+
}
|
| 66 |
+
};
|
| 67 |
+
|
| 68 |
+
// int64_t is *not* 8-byte aligned on all platforms!
|
| 69 |
+
struct alignas(8) Int64 {
|
| 70 |
+
int64_t a;
|
| 71 |
+
friend bool operator==(Int64 lhs, Int64 rhs) {
|
| 72 |
+
return lhs.a == rhs.a;
|
| 73 |
+
}
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
// Properties of types that this test relies on.
|
| 77 |
+
static_assert(sizeof(int8_t) == 1, "");
|
| 78 |
+
static_assert(alignof(int8_t) == 1, "");
|
| 79 |
+
static_assert(sizeof(int16_t) == 2, "");
|
| 80 |
+
static_assert(alignof(int16_t) == 2, "");
|
| 81 |
+
static_assert(sizeof(int32_t) == 4, "");
|
| 82 |
+
static_assert(alignof(int32_t) == 4, "");
|
| 83 |
+
static_assert(sizeof(Int64) == 8, "");
|
| 84 |
+
static_assert(alignof(Int64) == 8, "");
|
| 85 |
+
static_assert(sizeof(Int128) == 16, "");
|
| 86 |
+
static_assert(alignof(Int128) == 8, "");
|
| 87 |
+
|
| 88 |
+
template <class Expected, class Actual>
|
| 89 |
+
void SameType() {
|
| 90 |
+
static_assert(std::is_same<Expected, Actual>(), "");
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
TEST(Layout, ElementType) {
|
| 94 |
+
{
|
| 95 |
+
using L = Layout<int32_t>;
|
| 96 |
+
SameType<int32_t, L::ElementType<0>>();
|
| 97 |
+
SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
|
| 98 |
+
SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
|
| 99 |
+
}
|
| 100 |
+
{
|
| 101 |
+
using L = Layout<int32_t, int32_t>;
|
| 102 |
+
SameType<int32_t, L::ElementType<0>>();
|
| 103 |
+
SameType<int32_t, L::ElementType<1>>();
|
| 104 |
+
SameType<int32_t, decltype(L::Partial())::ElementType<0>>();
|
| 105 |
+
SameType<int32_t, decltype(L::Partial())::ElementType<1>>();
|
| 106 |
+
SameType<int32_t, decltype(L::Partial(0))::ElementType<0>>();
|
| 107 |
+
SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
|
| 108 |
+
}
|
| 109 |
+
{
|
| 110 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 111 |
+
SameType<int8_t, L::ElementType<0>>();
|
| 112 |
+
SameType<int32_t, L::ElementType<1>>();
|
| 113 |
+
SameType<Int128, L::ElementType<2>>();
|
| 114 |
+
SameType<int8_t, decltype(L::Partial())::ElementType<0>>();
|
| 115 |
+
SameType<int8_t, decltype(L::Partial(0))::ElementType<0>>();
|
| 116 |
+
SameType<int32_t, decltype(L::Partial(0))::ElementType<1>>();
|
| 117 |
+
SameType<int8_t, decltype(L::Partial(0, 0))::ElementType<0>>();
|
| 118 |
+
SameType<int32_t, decltype(L::Partial(0, 0))::ElementType<1>>();
|
| 119 |
+
SameType<Int128, decltype(L::Partial(0, 0))::ElementType<2>>();
|
| 120 |
+
SameType<int8_t, decltype(L::Partial(0, 0, 0))::ElementType<0>>();
|
| 121 |
+
SameType<int32_t, decltype(L::Partial(0, 0, 0))::ElementType<1>>();
|
| 122 |
+
SameType<Int128, decltype(L::Partial(0, 0, 0))::ElementType<2>>();
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
TEST(Layout, ElementTypes) {
|
| 127 |
+
{
|
| 128 |
+
using L = Layout<int32_t>;
|
| 129 |
+
SameType<std::tuple<int32_t>, L::ElementTypes>();
|
| 130 |
+
SameType<std::tuple<int32_t>, decltype(L::Partial())::ElementTypes>();
|
| 131 |
+
SameType<std::tuple<int32_t>, decltype(L::Partial(0))::ElementTypes>();
|
| 132 |
+
}
|
| 133 |
+
{
|
| 134 |
+
using L = Layout<int32_t, int32_t>;
|
| 135 |
+
SameType<std::tuple<int32_t, int32_t>, L::ElementTypes>();
|
| 136 |
+
SameType<std::tuple<int32_t, int32_t>,
|
| 137 |
+
decltype(L::Partial())::ElementTypes>();
|
| 138 |
+
SameType<std::tuple<int32_t, int32_t>,
|
| 139 |
+
decltype(L::Partial(0))::ElementTypes>();
|
| 140 |
+
}
|
| 141 |
+
{
|
| 142 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 143 |
+
SameType<std::tuple<int8_t, int32_t, Int128>, L::ElementTypes>();
|
| 144 |
+
SameType<std::tuple<int8_t, int32_t, Int128>,
|
| 145 |
+
decltype(L::Partial())::ElementTypes>();
|
| 146 |
+
SameType<std::tuple<int8_t, int32_t, Int128>,
|
| 147 |
+
decltype(L::Partial(0))::ElementTypes>();
|
| 148 |
+
SameType<std::tuple<int8_t, int32_t, Int128>,
|
| 149 |
+
decltype(L::Partial(0, 0))::ElementTypes>();
|
| 150 |
+
SameType<std::tuple<int8_t, int32_t, Int128>,
|
| 151 |
+
decltype(L::Partial(0, 0, 0))::ElementTypes>();
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
TEST(Layout, OffsetByIndex) {
|
| 156 |
+
{
|
| 157 |
+
using L = Layout<int32_t>;
|
| 158 |
+
EXPECT_EQ(0, L::Partial().Offset<0>());
|
| 159 |
+
EXPECT_EQ(0, L::Partial(3).Offset<0>());
|
| 160 |
+
EXPECT_EQ(0, L(3).Offset<0>());
|
| 161 |
+
}
|
| 162 |
+
{
|
| 163 |
+
using L = Layout<int32_t, int32_t>;
|
| 164 |
+
EXPECT_EQ(0, L::Partial().Offset<0>());
|
| 165 |
+
EXPECT_EQ(0, L::Partial(3).Offset<0>());
|
| 166 |
+
EXPECT_EQ(12, L::Partial(3).Offset<1>());
|
| 167 |
+
EXPECT_EQ(0, L::Partial(3, 5).Offset<0>());
|
| 168 |
+
EXPECT_EQ(12, L::Partial(3, 5).Offset<1>());
|
| 169 |
+
EXPECT_EQ(0, L(3, 5).Offset<0>());
|
| 170 |
+
EXPECT_EQ(12, L(3, 5).Offset<1>());
|
| 171 |
+
}
|
| 172 |
+
{
|
| 173 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 174 |
+
EXPECT_EQ(0, L::Partial().Offset<0>());
|
| 175 |
+
EXPECT_EQ(0, L::Partial(0).Offset<0>());
|
| 176 |
+
EXPECT_EQ(0, L::Partial(0).Offset<1>());
|
| 177 |
+
EXPECT_EQ(0, L::Partial(1).Offset<0>());
|
| 178 |
+
EXPECT_EQ(4, L::Partial(1).Offset<1>());
|
| 179 |
+
EXPECT_EQ(0, L::Partial(5).Offset<0>());
|
| 180 |
+
EXPECT_EQ(8, L::Partial(5).Offset<1>());
|
| 181 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<0>());
|
| 182 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<1>());
|
| 183 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<2>());
|
| 184 |
+
EXPECT_EQ(0, L::Partial(1, 0).Offset<0>());
|
| 185 |
+
EXPECT_EQ(4, L::Partial(1, 0).Offset<1>());
|
| 186 |
+
EXPECT_EQ(8, L::Partial(1, 0).Offset<2>());
|
| 187 |
+
EXPECT_EQ(0, L::Partial(5, 3).Offset<0>());
|
| 188 |
+
EXPECT_EQ(8, L::Partial(5, 3).Offset<1>());
|
| 189 |
+
EXPECT_EQ(24, L::Partial(5, 3).Offset<2>());
|
| 190 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<0>());
|
| 191 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<1>());
|
| 192 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<2>());
|
| 193 |
+
EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<0>());
|
| 194 |
+
EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<1>());
|
| 195 |
+
EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<2>());
|
| 196 |
+
EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<0>());
|
| 197 |
+
EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<2>());
|
| 198 |
+
EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<1>());
|
| 199 |
+
EXPECT_EQ(0, L(5, 3, 1).Offset<0>());
|
| 200 |
+
EXPECT_EQ(24, L(5, 3, 1).Offset<2>());
|
| 201 |
+
EXPECT_EQ(8, L(5, 3, 1).Offset<1>());
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
TEST(Layout, OffsetByType) {
|
| 206 |
+
{
|
| 207 |
+
using L = Layout<int32_t>;
|
| 208 |
+
EXPECT_EQ(0, L::Partial().Offset<int32_t>());
|
| 209 |
+
EXPECT_EQ(0, L::Partial(3).Offset<int32_t>());
|
| 210 |
+
EXPECT_EQ(0, L(3).Offset<int32_t>());
|
| 211 |
+
}
|
| 212 |
+
{
|
| 213 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 214 |
+
EXPECT_EQ(0, L::Partial().Offset<int8_t>());
|
| 215 |
+
EXPECT_EQ(0, L::Partial(0).Offset<int8_t>());
|
| 216 |
+
EXPECT_EQ(0, L::Partial(0).Offset<int32_t>());
|
| 217 |
+
EXPECT_EQ(0, L::Partial(1).Offset<int8_t>());
|
| 218 |
+
EXPECT_EQ(4, L::Partial(1).Offset<int32_t>());
|
| 219 |
+
EXPECT_EQ(0, L::Partial(5).Offset<int8_t>());
|
| 220 |
+
EXPECT_EQ(8, L::Partial(5).Offset<int32_t>());
|
| 221 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<int8_t>());
|
| 222 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<int32_t>());
|
| 223 |
+
EXPECT_EQ(0, L::Partial(0, 0).Offset<Int128>());
|
| 224 |
+
EXPECT_EQ(0, L::Partial(1, 0).Offset<int8_t>());
|
| 225 |
+
EXPECT_EQ(4, L::Partial(1, 0).Offset<int32_t>());
|
| 226 |
+
EXPECT_EQ(8, L::Partial(1, 0).Offset<Int128>());
|
| 227 |
+
EXPECT_EQ(0, L::Partial(5, 3).Offset<int8_t>());
|
| 228 |
+
EXPECT_EQ(8, L::Partial(5, 3).Offset<int32_t>());
|
| 229 |
+
EXPECT_EQ(24, L::Partial(5, 3).Offset<Int128>());
|
| 230 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int8_t>());
|
| 231 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<int32_t>());
|
| 232 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).Offset<Int128>());
|
| 233 |
+
EXPECT_EQ(0, L::Partial(1, 0, 0).Offset<int8_t>());
|
| 234 |
+
EXPECT_EQ(4, L::Partial(1, 0, 0).Offset<int32_t>());
|
| 235 |
+
EXPECT_EQ(8, L::Partial(1, 0, 0).Offset<Int128>());
|
| 236 |
+
EXPECT_EQ(0, L::Partial(5, 3, 1).Offset<int8_t>());
|
| 237 |
+
EXPECT_EQ(24, L::Partial(5, 3, 1).Offset<Int128>());
|
| 238 |
+
EXPECT_EQ(8, L::Partial(5, 3, 1).Offset<int32_t>());
|
| 239 |
+
EXPECT_EQ(0, L(5, 3, 1).Offset<int8_t>());
|
| 240 |
+
EXPECT_EQ(24, L(5, 3, 1).Offset<Int128>());
|
| 241 |
+
EXPECT_EQ(8, L(5, 3, 1).Offset<int32_t>());
|
| 242 |
+
}
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
TEST(Layout, Offsets) {
|
| 246 |
+
{
|
| 247 |
+
using L = Layout<int32_t>;
|
| 248 |
+
EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
|
| 249 |
+
EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0));
|
| 250 |
+
EXPECT_THAT(L(3).Offsets(), ElementsAre(0));
|
| 251 |
+
}
|
| 252 |
+
{
|
| 253 |
+
using L = Layout<int32_t, int32_t>;
|
| 254 |
+
EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
|
| 255 |
+
EXPECT_THAT(L::Partial(3).Offsets(), ElementsAre(0, 12));
|
| 256 |
+
EXPECT_THAT(L::Partial(3, 5).Offsets(), ElementsAre(0, 12));
|
| 257 |
+
EXPECT_THAT(L(3, 5).Offsets(), ElementsAre(0, 12));
|
| 258 |
+
}
|
| 259 |
+
{
|
| 260 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 261 |
+
EXPECT_THAT(L::Partial().Offsets(), ElementsAre(0));
|
| 262 |
+
EXPECT_THAT(L::Partial(1).Offsets(), ElementsAre(0, 4));
|
| 263 |
+
EXPECT_THAT(L::Partial(5).Offsets(), ElementsAre(0, 8));
|
| 264 |
+
EXPECT_THAT(L::Partial(0, 0).Offsets(), ElementsAre(0, 0, 0));
|
| 265 |
+
EXPECT_THAT(L::Partial(1, 0).Offsets(), ElementsAre(0, 4, 8));
|
| 266 |
+
EXPECT_THAT(L::Partial(5, 3).Offsets(), ElementsAre(0, 8, 24));
|
| 267 |
+
EXPECT_THAT(L::Partial(0, 0, 0).Offsets(), ElementsAre(0, 0, 0));
|
| 268 |
+
EXPECT_THAT(L::Partial(1, 0, 0).Offsets(), ElementsAre(0, 4, 8));
|
| 269 |
+
EXPECT_THAT(L::Partial(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
|
| 270 |
+
EXPECT_THAT(L(5, 3, 1).Offsets(), ElementsAre(0, 8, 24));
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
TEST(Layout, AllocSize) {
|
| 275 |
+
{
|
| 276 |
+
using L = Layout<int32_t>;
|
| 277 |
+
EXPECT_EQ(0, L::Partial(0).AllocSize());
|
| 278 |
+
EXPECT_EQ(12, L::Partial(3).AllocSize());
|
| 279 |
+
EXPECT_EQ(12, L(3).AllocSize());
|
| 280 |
+
}
|
| 281 |
+
{
|
| 282 |
+
using L = Layout<int32_t, int32_t>;
|
| 283 |
+
EXPECT_EQ(32, L::Partial(3, 5).AllocSize());
|
| 284 |
+
EXPECT_EQ(32, L(3, 5).AllocSize());
|
| 285 |
+
}
|
| 286 |
+
{
|
| 287 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 288 |
+
EXPECT_EQ(0, L::Partial(0, 0, 0).AllocSize());
|
| 289 |
+
EXPECT_EQ(8, L::Partial(1, 0, 0).AllocSize());
|
| 290 |
+
EXPECT_EQ(8, L::Partial(0, 1, 0).AllocSize());
|
| 291 |
+
EXPECT_EQ(16, L::Partial(0, 0, 1).AllocSize());
|
| 292 |
+
EXPECT_EQ(24, L::Partial(1, 1, 1).AllocSize());
|
| 293 |
+
EXPECT_EQ(136, L::Partial(3, 5, 7).AllocSize());
|
| 294 |
+
EXPECT_EQ(136, L(3, 5, 7).AllocSize());
|
| 295 |
+
}
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
TEST(Layout, SizeByIndex) {
|
| 299 |
+
{
|
| 300 |
+
using L = Layout<int32_t>;
|
| 301 |
+
EXPECT_EQ(0, L::Partial(0).Size<0>());
|
| 302 |
+
EXPECT_EQ(3, L::Partial(3).Size<0>());
|
| 303 |
+
EXPECT_EQ(3, L(3).Size<0>());
|
| 304 |
+
}
|
| 305 |
+
{
|
| 306 |
+
using L = Layout<int32_t, int32_t>;
|
| 307 |
+
EXPECT_EQ(0, L::Partial(0).Size<0>());
|
| 308 |
+
EXPECT_EQ(3, L::Partial(3).Size<0>());
|
| 309 |
+
EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
|
| 310 |
+
EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
|
| 311 |
+
EXPECT_EQ(3, L(3, 5).Size<0>());
|
| 312 |
+
EXPECT_EQ(5, L(3, 5).Size<1>());
|
| 313 |
+
}
|
| 314 |
+
{
|
| 315 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 316 |
+
EXPECT_EQ(3, L::Partial(3).Size<0>());
|
| 317 |
+
EXPECT_EQ(3, L::Partial(3, 5).Size<0>());
|
| 318 |
+
EXPECT_EQ(5, L::Partial(3, 5).Size<1>());
|
| 319 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Size<0>());
|
| 320 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Size<1>());
|
| 321 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Size<2>());
|
| 322 |
+
EXPECT_EQ(3, L(3, 5, 7).Size<0>());
|
| 323 |
+
EXPECT_EQ(5, L(3, 5, 7).Size<1>());
|
| 324 |
+
EXPECT_EQ(7, L(3, 5, 7).Size<2>());
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
TEST(Layout, SizeByType) {
|
| 329 |
+
{
|
| 330 |
+
using L = Layout<int32_t>;
|
| 331 |
+
EXPECT_EQ(0, L::Partial(0).Size<int32_t>());
|
| 332 |
+
EXPECT_EQ(3, L::Partial(3).Size<int32_t>());
|
| 333 |
+
EXPECT_EQ(3, L(3).Size<int32_t>());
|
| 334 |
+
}
|
| 335 |
+
{
|
| 336 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 337 |
+
EXPECT_EQ(3, L::Partial(3).Size<int8_t>());
|
| 338 |
+
EXPECT_EQ(3, L::Partial(3, 5).Size<int8_t>());
|
| 339 |
+
EXPECT_EQ(5, L::Partial(3, 5).Size<int32_t>());
|
| 340 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Size<int8_t>());
|
| 341 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Size<int32_t>());
|
| 342 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Size<Int128>());
|
| 343 |
+
EXPECT_EQ(3, L(3, 5, 7).Size<int8_t>());
|
| 344 |
+
EXPECT_EQ(5, L(3, 5, 7).Size<int32_t>());
|
| 345 |
+
EXPECT_EQ(7, L(3, 5, 7).Size<Int128>());
|
| 346 |
+
}
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
TEST(Layout, Sizes) {
|
| 350 |
+
{
|
| 351 |
+
using L = Layout<int32_t>;
|
| 352 |
+
EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
|
| 353 |
+
EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
|
| 354 |
+
EXPECT_THAT(L(3).Sizes(), ElementsAre(3));
|
| 355 |
+
}
|
| 356 |
+
{
|
| 357 |
+
using L = Layout<int32_t, int32_t>;
|
| 358 |
+
EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
|
| 359 |
+
EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
|
| 360 |
+
EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
|
| 361 |
+
EXPECT_THAT(L(3, 5).Sizes(), ElementsAre(3, 5));
|
| 362 |
+
}
|
| 363 |
+
{
|
| 364 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 365 |
+
EXPECT_THAT(L::Partial().Sizes(), ElementsAre());
|
| 366 |
+
EXPECT_THAT(L::Partial(3).Sizes(), ElementsAre(3));
|
| 367 |
+
EXPECT_THAT(L::Partial(3, 5).Sizes(), ElementsAre(3, 5));
|
| 368 |
+
EXPECT_THAT(L::Partial(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
|
| 369 |
+
EXPECT_THAT(L(3, 5, 7).Sizes(), ElementsAre(3, 5, 7));
|
| 370 |
+
}
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
TEST(Layout, PointerByIndex) {
|
| 374 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 375 |
+
{
|
| 376 |
+
using L = Layout<int32_t>;
|
| 377 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
|
| 378 |
+
EXPECT_EQ(0,
|
| 379 |
+
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
|
| 380 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<0>(p))));
|
| 381 |
+
}
|
| 382 |
+
{
|
| 383 |
+
using L = Layout<int32_t, int32_t>;
|
| 384 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<0>(p))));
|
| 385 |
+
EXPECT_EQ(0,
|
| 386 |
+
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<0>(p))));
|
| 387 |
+
EXPECT_EQ(12,
|
| 388 |
+
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<1>(p))));
|
| 389 |
+
EXPECT_EQ(
|
| 390 |
+
0, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
|
| 391 |
+
EXPECT_EQ(
|
| 392 |
+
12, Distance(p, Type<const int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
|
| 393 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<0>(p))));
|
| 394 |
+
EXPECT_EQ(12, Distance(p, Type<const int32_t*>(L(3, 5).Pointer<1>(p))));
|
| 395 |
+
}
|
| 396 |
+
{
|
| 397 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 398 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<0>(p))));
|
| 399 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<0>(p))));
|
| 400 |
+
EXPECT_EQ(0,
|
| 401 |
+
Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<1>(p))));
|
| 402 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<0>(p))));
|
| 403 |
+
EXPECT_EQ(4,
|
| 404 |
+
Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<1>(p))));
|
| 405 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<0>(p))));
|
| 406 |
+
EXPECT_EQ(8,
|
| 407 |
+
Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<1>(p))));
|
| 408 |
+
EXPECT_EQ(0,
|
| 409 |
+
Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
|
| 410 |
+
EXPECT_EQ(
|
| 411 |
+
0, Distance(p, Type<const int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
|
| 412 |
+
EXPECT_EQ(0,
|
| 413 |
+
Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<2>(p))));
|
| 414 |
+
EXPECT_EQ(0,
|
| 415 |
+
Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
|
| 416 |
+
EXPECT_EQ(
|
| 417 |
+
4, Distance(p, Type<const int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
|
| 418 |
+
EXPECT_EQ(8,
|
| 419 |
+
Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<2>(p))));
|
| 420 |
+
EXPECT_EQ(0,
|
| 421 |
+
Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
|
| 422 |
+
EXPECT_EQ(
|
| 423 |
+
8, Distance(p, Type<const int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
|
| 424 |
+
EXPECT_EQ(24,
|
| 425 |
+
Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<2>(p))));
|
| 426 |
+
EXPECT_EQ(
|
| 427 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
|
| 428 |
+
EXPECT_EQ(
|
| 429 |
+
0,
|
| 430 |
+
Distance(p, Type<const int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
|
| 431 |
+
EXPECT_EQ(
|
| 432 |
+
0, Distance(p, Type<const Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
|
| 433 |
+
EXPECT_EQ(
|
| 434 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
|
| 435 |
+
EXPECT_EQ(
|
| 436 |
+
4,
|
| 437 |
+
Distance(p, Type<const int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
|
| 438 |
+
EXPECT_EQ(
|
| 439 |
+
8, Distance(p, Type<const Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
|
| 440 |
+
EXPECT_EQ(
|
| 441 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
|
| 442 |
+
EXPECT_EQ(
|
| 443 |
+
24,
|
| 444 |
+
Distance(p, Type<const Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
|
| 445 |
+
EXPECT_EQ(
|
| 446 |
+
8,
|
| 447 |
+
Distance(p, Type<const int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
|
| 448 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(L(5, 3, 1).Pointer<0>(p))));
|
| 449 |
+
EXPECT_EQ(24, Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<2>(p))));
|
| 450 |
+
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<1>(p))));
|
| 451 |
+
}
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
TEST(Layout, PointerByType) {
|
| 455 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 456 |
+
{
|
| 457 |
+
using L = Layout<int32_t>;
|
| 458 |
+
EXPECT_EQ(
|
| 459 |
+
0, Distance(p, Type<const int32_t*>(L::Partial().Pointer<int32_t>(p))));
|
| 460 |
+
EXPECT_EQ(
|
| 461 |
+
0,
|
| 462 |
+
Distance(p, Type<const int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
|
| 463 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(L(3).Pointer<int32_t>(p))));
|
| 464 |
+
}
|
| 465 |
+
{
|
| 466 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 467 |
+
EXPECT_EQ(
|
| 468 |
+
0, Distance(p, Type<const int8_t*>(L::Partial().Pointer<int8_t>(p))));
|
| 469 |
+
EXPECT_EQ(
|
| 470 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
|
| 471 |
+
EXPECT_EQ(
|
| 472 |
+
0,
|
| 473 |
+
Distance(p, Type<const int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
|
| 474 |
+
EXPECT_EQ(
|
| 475 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
|
| 476 |
+
EXPECT_EQ(
|
| 477 |
+
4,
|
| 478 |
+
Distance(p, Type<const int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
|
| 479 |
+
EXPECT_EQ(
|
| 480 |
+
0, Distance(p, Type<const int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
|
| 481 |
+
EXPECT_EQ(
|
| 482 |
+
8,
|
| 483 |
+
Distance(p, Type<const int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
|
| 484 |
+
EXPECT_EQ(
|
| 485 |
+
0,
|
| 486 |
+
Distance(p, Type<const int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
|
| 487 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
|
| 488 |
+
L::Partial(0, 0).Pointer<int32_t>(p))));
|
| 489 |
+
EXPECT_EQ(
|
| 490 |
+
0,
|
| 491 |
+
Distance(p, Type<const Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
|
| 492 |
+
EXPECT_EQ(
|
| 493 |
+
0,
|
| 494 |
+
Distance(p, Type<const int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
|
| 495 |
+
EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
|
| 496 |
+
L::Partial(1, 0).Pointer<int32_t>(p))));
|
| 497 |
+
EXPECT_EQ(
|
| 498 |
+
8,
|
| 499 |
+
Distance(p, Type<const Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
|
| 500 |
+
EXPECT_EQ(
|
| 501 |
+
0,
|
| 502 |
+
Distance(p, Type<const int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
|
| 503 |
+
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
|
| 504 |
+
L::Partial(5, 3).Pointer<int32_t>(p))));
|
| 505 |
+
EXPECT_EQ(
|
| 506 |
+
24,
|
| 507 |
+
Distance(p, Type<const Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
|
| 508 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
|
| 509 |
+
L::Partial(0, 0, 0).Pointer<int8_t>(p))));
|
| 510 |
+
EXPECT_EQ(0, Distance(p, Type<const int32_t*>(
|
| 511 |
+
L::Partial(0, 0, 0).Pointer<int32_t>(p))));
|
| 512 |
+
EXPECT_EQ(0, Distance(p, Type<const Int128*>(
|
| 513 |
+
L::Partial(0, 0, 0).Pointer<Int128>(p))));
|
| 514 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
|
| 515 |
+
L::Partial(1, 0, 0).Pointer<int8_t>(p))));
|
| 516 |
+
EXPECT_EQ(4, Distance(p, Type<const int32_t*>(
|
| 517 |
+
L::Partial(1, 0, 0).Pointer<int32_t>(p))));
|
| 518 |
+
EXPECT_EQ(8, Distance(p, Type<const Int128*>(
|
| 519 |
+
L::Partial(1, 0, 0).Pointer<Int128>(p))));
|
| 520 |
+
EXPECT_EQ(0, Distance(p, Type<const int8_t*>(
|
| 521 |
+
L::Partial(5, 3, 1).Pointer<int8_t>(p))));
|
| 522 |
+
EXPECT_EQ(24, Distance(p, Type<const Int128*>(
|
| 523 |
+
L::Partial(5, 3, 1).Pointer<Int128>(p))));
|
| 524 |
+
EXPECT_EQ(8, Distance(p, Type<const int32_t*>(
|
| 525 |
+
L::Partial(5, 3, 1).Pointer<int32_t>(p))));
|
| 526 |
+
EXPECT_EQ(24,
|
| 527 |
+
Distance(p, Type<const Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
|
| 528 |
+
EXPECT_EQ(
|
| 529 |
+
8, Distance(p, Type<const int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
|
| 530 |
+
}
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
TEST(Layout, MutablePointerByIndex) {
|
| 534 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 535 |
+
{
|
| 536 |
+
using L = Layout<int32_t>;
|
| 537 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
|
| 538 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
|
| 539 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<0>(p))));
|
| 540 |
+
}
|
| 541 |
+
{
|
| 542 |
+
using L = Layout<int32_t, int32_t>;
|
| 543 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<0>(p))));
|
| 544 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<0>(p))));
|
| 545 |
+
EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3).Pointer<1>(p))));
|
| 546 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<0>(p))));
|
| 547 |
+
EXPECT_EQ(12, Distance(p, Type<int32_t*>(L::Partial(3, 5).Pointer<1>(p))));
|
| 548 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3, 5).Pointer<0>(p))));
|
| 549 |
+
EXPECT_EQ(12, Distance(p, Type<int32_t*>(L(3, 5).Pointer<1>(p))));
|
| 550 |
+
}
|
| 551 |
+
{
|
| 552 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 553 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<0>(p))));
|
| 554 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<0>(p))));
|
| 555 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0).Pointer<1>(p))));
|
| 556 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<0>(p))));
|
| 557 |
+
EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1).Pointer<1>(p))));
|
| 558 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<0>(p))));
|
| 559 |
+
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5).Pointer<1>(p))));
|
| 560 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<0>(p))));
|
| 561 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<1>(p))));
|
| 562 |
+
EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<2>(p))));
|
| 563 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<0>(p))));
|
| 564 |
+
EXPECT_EQ(4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<1>(p))));
|
| 565 |
+
EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<2>(p))));
|
| 566 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<0>(p))));
|
| 567 |
+
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<1>(p))));
|
| 568 |
+
EXPECT_EQ(24, Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<2>(p))));
|
| 569 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<0>(p))));
|
| 570 |
+
EXPECT_EQ(0,
|
| 571 |
+
Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<1>(p))));
|
| 572 |
+
EXPECT_EQ(0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<2>(p))));
|
| 573 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<0>(p))));
|
| 574 |
+
EXPECT_EQ(4,
|
| 575 |
+
Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<1>(p))));
|
| 576 |
+
EXPECT_EQ(8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<2>(p))));
|
| 577 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<0>(p))));
|
| 578 |
+
EXPECT_EQ(24,
|
| 579 |
+
Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<2>(p))));
|
| 580 |
+
EXPECT_EQ(8,
|
| 581 |
+
Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<1>(p))));
|
| 582 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<0>(p))));
|
| 583 |
+
EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<2>(p))));
|
| 584 |
+
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<1>(p))));
|
| 585 |
+
}
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
TEST(Layout, MutablePointerByType) {
|
| 589 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 590 |
+
{
|
| 591 |
+
using L = Layout<int32_t>;
|
| 592 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L::Partial().Pointer<int32_t>(p))));
|
| 593 |
+
EXPECT_EQ(0,
|
| 594 |
+
Distance(p, Type<int32_t*>(L::Partial(3).Pointer<int32_t>(p))));
|
| 595 |
+
EXPECT_EQ(0, Distance(p, Type<int32_t*>(L(3).Pointer<int32_t>(p))));
|
| 596 |
+
}
|
| 597 |
+
{
|
| 598 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 599 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial().Pointer<int8_t>(p))));
|
| 600 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(0).Pointer<int8_t>(p))));
|
| 601 |
+
EXPECT_EQ(0,
|
| 602 |
+
Distance(p, Type<int32_t*>(L::Partial(0).Pointer<int32_t>(p))));
|
| 603 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(1).Pointer<int8_t>(p))));
|
| 604 |
+
EXPECT_EQ(4,
|
| 605 |
+
Distance(p, Type<int32_t*>(L::Partial(1).Pointer<int32_t>(p))));
|
| 606 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L::Partial(5).Pointer<int8_t>(p))));
|
| 607 |
+
EXPECT_EQ(8,
|
| 608 |
+
Distance(p, Type<int32_t*>(L::Partial(5).Pointer<int32_t>(p))));
|
| 609 |
+
EXPECT_EQ(0,
|
| 610 |
+
Distance(p, Type<int8_t*>(L::Partial(0, 0).Pointer<int8_t>(p))));
|
| 611 |
+
EXPECT_EQ(
|
| 612 |
+
0, Distance(p, Type<int32_t*>(L::Partial(0, 0).Pointer<int32_t>(p))));
|
| 613 |
+
EXPECT_EQ(0,
|
| 614 |
+
Distance(p, Type<Int128*>(L::Partial(0, 0).Pointer<Int128>(p))));
|
| 615 |
+
EXPECT_EQ(0,
|
| 616 |
+
Distance(p, Type<int8_t*>(L::Partial(1, 0).Pointer<int8_t>(p))));
|
| 617 |
+
EXPECT_EQ(
|
| 618 |
+
4, Distance(p, Type<int32_t*>(L::Partial(1, 0).Pointer<int32_t>(p))));
|
| 619 |
+
EXPECT_EQ(8,
|
| 620 |
+
Distance(p, Type<Int128*>(L::Partial(1, 0).Pointer<Int128>(p))));
|
| 621 |
+
EXPECT_EQ(0,
|
| 622 |
+
Distance(p, Type<int8_t*>(L::Partial(5, 3).Pointer<int8_t>(p))));
|
| 623 |
+
EXPECT_EQ(
|
| 624 |
+
8, Distance(p, Type<int32_t*>(L::Partial(5, 3).Pointer<int32_t>(p))));
|
| 625 |
+
EXPECT_EQ(24,
|
| 626 |
+
Distance(p, Type<Int128*>(L::Partial(5, 3).Pointer<Int128>(p))));
|
| 627 |
+
EXPECT_EQ(
|
| 628 |
+
0, Distance(p, Type<int8_t*>(L::Partial(0, 0, 0).Pointer<int8_t>(p))));
|
| 629 |
+
EXPECT_EQ(
|
| 630 |
+
0,
|
| 631 |
+
Distance(p, Type<int32_t*>(L::Partial(0, 0, 0).Pointer<int32_t>(p))));
|
| 632 |
+
EXPECT_EQ(
|
| 633 |
+
0, Distance(p, Type<Int128*>(L::Partial(0, 0, 0).Pointer<Int128>(p))));
|
| 634 |
+
EXPECT_EQ(
|
| 635 |
+
0, Distance(p, Type<int8_t*>(L::Partial(1, 0, 0).Pointer<int8_t>(p))));
|
| 636 |
+
EXPECT_EQ(
|
| 637 |
+
4,
|
| 638 |
+
Distance(p, Type<int32_t*>(L::Partial(1, 0, 0).Pointer<int32_t>(p))));
|
| 639 |
+
EXPECT_EQ(
|
| 640 |
+
8, Distance(p, Type<Int128*>(L::Partial(1, 0, 0).Pointer<Int128>(p))));
|
| 641 |
+
EXPECT_EQ(
|
| 642 |
+
0, Distance(p, Type<int8_t*>(L::Partial(5, 3, 1).Pointer<int8_t>(p))));
|
| 643 |
+
EXPECT_EQ(
|
| 644 |
+
24, Distance(p, Type<Int128*>(L::Partial(5, 3, 1).Pointer<Int128>(p))));
|
| 645 |
+
EXPECT_EQ(
|
| 646 |
+
8,
|
| 647 |
+
Distance(p, Type<int32_t*>(L::Partial(5, 3, 1).Pointer<int32_t>(p))));
|
| 648 |
+
EXPECT_EQ(0, Distance(p, Type<int8_t*>(L(5, 3, 1).Pointer<int8_t>(p))));
|
| 649 |
+
EXPECT_EQ(24, Distance(p, Type<Int128*>(L(5, 3, 1).Pointer<Int128>(p))));
|
| 650 |
+
EXPECT_EQ(8, Distance(p, Type<int32_t*>(L(5, 3, 1).Pointer<int32_t>(p))));
|
| 651 |
+
}
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
TEST(Layout, Pointers) {
|
| 655 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 656 |
+
using L = Layout<int8_t, int8_t, Int128>;
|
| 657 |
+
{
|
| 658 |
+
const auto x = L::Partial();
|
| 659 |
+
EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
|
| 660 |
+
Type<std::tuple<const int8_t*>>(x.Pointers(p)));
|
| 661 |
+
}
|
| 662 |
+
{
|
| 663 |
+
const auto x = L::Partial(1);
|
| 664 |
+
EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
|
| 665 |
+
(Type<std::tuple<const int8_t*, const int8_t*>>(x.Pointers(p))));
|
| 666 |
+
}
|
| 667 |
+
{
|
| 668 |
+
const auto x = L::Partial(1, 2);
|
| 669 |
+
EXPECT_EQ(
|
| 670 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 671 |
+
(Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
|
| 672 |
+
x.Pointers(p))));
|
| 673 |
+
}
|
| 674 |
+
{
|
| 675 |
+
const auto x = L::Partial(1, 2, 3);
|
| 676 |
+
EXPECT_EQ(
|
| 677 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 678 |
+
(Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
|
| 679 |
+
x.Pointers(p))));
|
| 680 |
+
}
|
| 681 |
+
{
|
| 682 |
+
const L x(1, 2, 3);
|
| 683 |
+
EXPECT_EQ(
|
| 684 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 685 |
+
(Type<std::tuple<const int8_t*, const int8_t*, const Int128*>>(
|
| 686 |
+
x.Pointers(p))));
|
| 687 |
+
}
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
TEST(Layout, MutablePointers) {
|
| 691 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 692 |
+
using L = Layout<int8_t, int8_t, Int128>;
|
| 693 |
+
{
|
| 694 |
+
const auto x = L::Partial();
|
| 695 |
+
EXPECT_EQ(std::make_tuple(x.Pointer<0>(p)),
|
| 696 |
+
Type<std::tuple<int8_t*>>(x.Pointers(p)));
|
| 697 |
+
}
|
| 698 |
+
{
|
| 699 |
+
const auto x = L::Partial(1);
|
| 700 |
+
EXPECT_EQ(std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p)),
|
| 701 |
+
(Type<std::tuple<int8_t*, int8_t*>>(x.Pointers(p))));
|
| 702 |
+
}
|
| 703 |
+
{
|
| 704 |
+
const auto x = L::Partial(1, 2);
|
| 705 |
+
EXPECT_EQ(
|
| 706 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 707 |
+
(Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
|
| 708 |
+
}
|
| 709 |
+
{
|
| 710 |
+
const auto x = L::Partial(1, 2, 3);
|
| 711 |
+
EXPECT_EQ(
|
| 712 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 713 |
+
(Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
|
| 714 |
+
}
|
| 715 |
+
{
|
| 716 |
+
const L x(1, 2, 3);
|
| 717 |
+
EXPECT_EQ(
|
| 718 |
+
std::make_tuple(x.Pointer<0>(p), x.Pointer<1>(p), x.Pointer<2>(p)),
|
| 719 |
+
(Type<std::tuple<int8_t*, int8_t*, Int128*>>(x.Pointers(p))));
|
| 720 |
+
}
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
TEST(Layout, SliceByIndexSize) {
|
| 724 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 725 |
+
{
|
| 726 |
+
using L = Layout<int32_t>;
|
| 727 |
+
EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
|
| 728 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 729 |
+
EXPECT_EQ(3, L(3).Slice<0>(p).size());
|
| 730 |
+
}
|
| 731 |
+
{
|
| 732 |
+
using L = Layout<int32_t, int32_t>;
|
| 733 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 734 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
|
| 735 |
+
EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
|
| 736 |
+
}
|
| 737 |
+
{
|
| 738 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 739 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 740 |
+
EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
|
| 741 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
|
| 742 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
|
| 743 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
|
| 744 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
|
| 745 |
+
EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
|
| 746 |
+
EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
|
| 747 |
+
EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
|
| 748 |
+
}
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
TEST(Layout, SliceByTypeSize) {
|
| 752 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 753 |
+
{
|
| 754 |
+
using L = Layout<int32_t>;
|
| 755 |
+
EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
|
| 756 |
+
EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
|
| 757 |
+
EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
|
| 758 |
+
}
|
| 759 |
+
{
|
| 760 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 761 |
+
EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
|
| 762 |
+
EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
|
| 763 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
|
| 764 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
|
| 765 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
|
| 766 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
|
| 767 |
+
EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
|
| 768 |
+
EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
|
| 769 |
+
EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
|
| 770 |
+
}
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
TEST(Layout, MutableSliceByIndexSize) {
|
| 774 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 775 |
+
{
|
| 776 |
+
using L = Layout<int32_t>;
|
| 777 |
+
EXPECT_EQ(0, L::Partial(0).Slice<0>(p).size());
|
| 778 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 779 |
+
EXPECT_EQ(3, L(3).Slice<0>(p).size());
|
| 780 |
+
}
|
| 781 |
+
{
|
| 782 |
+
using L = Layout<int32_t, int32_t>;
|
| 783 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 784 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
|
| 785 |
+
EXPECT_EQ(5, L(3, 5).Slice<1>(p).size());
|
| 786 |
+
}
|
| 787 |
+
{
|
| 788 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 789 |
+
EXPECT_EQ(3, L::Partial(3).Slice<0>(p).size());
|
| 790 |
+
EXPECT_EQ(3, L::Partial(3, 5).Slice<0>(p).size());
|
| 791 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<1>(p).size());
|
| 792 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<0>(p).size());
|
| 793 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<1>(p).size());
|
| 794 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<2>(p).size());
|
| 795 |
+
EXPECT_EQ(3, L(3, 5, 7).Slice<0>(p).size());
|
| 796 |
+
EXPECT_EQ(5, L(3, 5, 7).Slice<1>(p).size());
|
| 797 |
+
EXPECT_EQ(7, L(3, 5, 7).Slice<2>(p).size());
|
| 798 |
+
}
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
TEST(Layout, MutableSliceByTypeSize) {
|
| 802 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 803 |
+
{
|
| 804 |
+
using L = Layout<int32_t>;
|
| 805 |
+
EXPECT_EQ(0, L::Partial(0).Slice<int32_t>(p).size());
|
| 806 |
+
EXPECT_EQ(3, L::Partial(3).Slice<int32_t>(p).size());
|
| 807 |
+
EXPECT_EQ(3, L(3).Slice<int32_t>(p).size());
|
| 808 |
+
}
|
| 809 |
+
{
|
| 810 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 811 |
+
EXPECT_EQ(3, L::Partial(3).Slice<int8_t>(p).size());
|
| 812 |
+
EXPECT_EQ(3, L::Partial(3, 5).Slice<int8_t>(p).size());
|
| 813 |
+
EXPECT_EQ(5, L::Partial(3, 5).Slice<int32_t>(p).size());
|
| 814 |
+
EXPECT_EQ(3, L::Partial(3, 5, 7).Slice<int8_t>(p).size());
|
| 815 |
+
EXPECT_EQ(5, L::Partial(3, 5, 7).Slice<int32_t>(p).size());
|
| 816 |
+
EXPECT_EQ(7, L::Partial(3, 5, 7).Slice<Int128>(p).size());
|
| 817 |
+
EXPECT_EQ(3, L(3, 5, 7).Slice<int8_t>(p).size());
|
| 818 |
+
EXPECT_EQ(5, L(3, 5, 7).Slice<int32_t>(p).size());
|
| 819 |
+
EXPECT_EQ(7, L(3, 5, 7).Slice<Int128>(p).size());
|
| 820 |
+
}
|
| 821 |
+
}
|
| 822 |
+
|
| 823 |
+
TEST(Layout, SliceByIndexData) {
|
| 824 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 825 |
+
{
|
| 826 |
+
using L = Layout<int32_t>;
|
| 827 |
+
EXPECT_EQ(
|
| 828 |
+
0, Distance(
|
| 829 |
+
p, Type<Span<const int32_t>>(L::Partial(0).Slice<0>(p)).data()));
|
| 830 |
+
EXPECT_EQ(
|
| 831 |
+
0, Distance(
|
| 832 |
+
p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
|
| 833 |
+
EXPECT_EQ(0,
|
| 834 |
+
Distance(p, Type<Span<const int32_t>>(L(3).Slice<0>(p)).data()));
|
| 835 |
+
}
|
| 836 |
+
{
|
| 837 |
+
using L = Layout<int32_t, int32_t>;
|
| 838 |
+
EXPECT_EQ(
|
| 839 |
+
0, Distance(
|
| 840 |
+
p, Type<Span<const int32_t>>(L::Partial(3).Slice<0>(p)).data()));
|
| 841 |
+
EXPECT_EQ(
|
| 842 |
+
0,
|
| 843 |
+
Distance(
|
| 844 |
+
p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
|
| 845 |
+
EXPECT_EQ(
|
| 846 |
+
12,
|
| 847 |
+
Distance(
|
| 848 |
+
p, Type<Span<const int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
|
| 849 |
+
EXPECT_EQ(
|
| 850 |
+
0, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<0>(p)).data()));
|
| 851 |
+
EXPECT_EQ(
|
| 852 |
+
12, Distance(p, Type<Span<const int32_t>>(L(3, 5).Slice<1>(p)).data()));
|
| 853 |
+
}
|
| 854 |
+
{
|
| 855 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 856 |
+
EXPECT_EQ(
|
| 857 |
+
0, Distance(
|
| 858 |
+
p, Type<Span<const int8_t>>(L::Partial(0).Slice<0>(p)).data()));
|
| 859 |
+
EXPECT_EQ(
|
| 860 |
+
0, Distance(
|
| 861 |
+
p, Type<Span<const int8_t>>(L::Partial(1).Slice<0>(p)).data()));
|
| 862 |
+
EXPECT_EQ(
|
| 863 |
+
0, Distance(
|
| 864 |
+
p, Type<Span<const int8_t>>(L::Partial(5).Slice<0>(p)).data()));
|
| 865 |
+
EXPECT_EQ(
|
| 866 |
+
0,
|
| 867 |
+
Distance(
|
| 868 |
+
p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
|
| 869 |
+
EXPECT_EQ(
|
| 870 |
+
0,
|
| 871 |
+
Distance(
|
| 872 |
+
p, Type<Span<const int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
|
| 873 |
+
EXPECT_EQ(
|
| 874 |
+
0,
|
| 875 |
+
Distance(
|
| 876 |
+
p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
|
| 877 |
+
EXPECT_EQ(
|
| 878 |
+
4,
|
| 879 |
+
Distance(
|
| 880 |
+
p, Type<Span<const int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
|
| 881 |
+
EXPECT_EQ(
|
| 882 |
+
0,
|
| 883 |
+
Distance(
|
| 884 |
+
p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
|
| 885 |
+
EXPECT_EQ(
|
| 886 |
+
8,
|
| 887 |
+
Distance(
|
| 888 |
+
p, Type<Span<const int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
|
| 889 |
+
EXPECT_EQ(
|
| 890 |
+
0,
|
| 891 |
+
Distance(
|
| 892 |
+
p,
|
| 893 |
+
Type<Span<const int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
|
| 894 |
+
EXPECT_EQ(
|
| 895 |
+
0,
|
| 896 |
+
Distance(
|
| 897 |
+
p,
|
| 898 |
+
Type<Span<const int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
|
| 899 |
+
EXPECT_EQ(
|
| 900 |
+
0,
|
| 901 |
+
Distance(
|
| 902 |
+
p,
|
| 903 |
+
Type<Span<const Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
|
| 904 |
+
EXPECT_EQ(
|
| 905 |
+
0,
|
| 906 |
+
Distance(
|
| 907 |
+
p,
|
| 908 |
+
Type<Span<const int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
|
| 909 |
+
EXPECT_EQ(
|
| 910 |
+
4,
|
| 911 |
+
Distance(
|
| 912 |
+
p,
|
| 913 |
+
Type<Span<const int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
|
| 914 |
+
EXPECT_EQ(
|
| 915 |
+
8,
|
| 916 |
+
Distance(
|
| 917 |
+
p,
|
| 918 |
+
Type<Span<const Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
|
| 919 |
+
EXPECT_EQ(
|
| 920 |
+
0,
|
| 921 |
+
Distance(
|
| 922 |
+
p,
|
| 923 |
+
Type<Span<const int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
|
| 924 |
+
EXPECT_EQ(
|
| 925 |
+
24,
|
| 926 |
+
Distance(
|
| 927 |
+
p,
|
| 928 |
+
Type<Span<const Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
|
| 929 |
+
EXPECT_EQ(
|
| 930 |
+
8,
|
| 931 |
+
Distance(
|
| 932 |
+
p,
|
| 933 |
+
Type<Span<const int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
|
| 934 |
+
EXPECT_EQ(
|
| 935 |
+
0,
|
| 936 |
+
Distance(p, Type<Span<const int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
|
| 937 |
+
EXPECT_EQ(
|
| 938 |
+
24,
|
| 939 |
+
Distance(p, Type<Span<const Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
|
| 940 |
+
EXPECT_EQ(
|
| 941 |
+
8,
|
| 942 |
+
Distance(p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
|
| 943 |
+
}
|
| 944 |
+
}
|
| 945 |
+
|
| 946 |
+
TEST(Layout, SliceByTypeData) {
|
| 947 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 948 |
+
{
|
| 949 |
+
using L = Layout<int32_t>;
|
| 950 |
+
EXPECT_EQ(
|
| 951 |
+
0,
|
| 952 |
+
Distance(
|
| 953 |
+
p,
|
| 954 |
+
Type<Span<const int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
|
| 955 |
+
EXPECT_EQ(
|
| 956 |
+
0,
|
| 957 |
+
Distance(
|
| 958 |
+
p,
|
| 959 |
+
Type<Span<const int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
|
| 960 |
+
EXPECT_EQ(
|
| 961 |
+
0,
|
| 962 |
+
Distance(p, Type<Span<const int32_t>>(L(3).Slice<int32_t>(p)).data()));
|
| 963 |
+
}
|
| 964 |
+
{
|
| 965 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 966 |
+
EXPECT_EQ(
|
| 967 |
+
0,
|
| 968 |
+
Distance(
|
| 969 |
+
p,
|
| 970 |
+
Type<Span<const int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
|
| 971 |
+
EXPECT_EQ(
|
| 972 |
+
0,
|
| 973 |
+
Distance(
|
| 974 |
+
p,
|
| 975 |
+
Type<Span<const int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
|
| 976 |
+
EXPECT_EQ(
|
| 977 |
+
0,
|
| 978 |
+
Distance(
|
| 979 |
+
p,
|
| 980 |
+
Type<Span<const int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
|
| 981 |
+
EXPECT_EQ(
|
| 982 |
+
0,
|
| 983 |
+
Distance(p, Type<Span<const int8_t>>(L::Partial(0, 0).Slice<int8_t>(p))
|
| 984 |
+
.data()));
|
| 985 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
|
| 986 |
+
L::Partial(0, 0).Slice<int32_t>(p))
|
| 987 |
+
.data()));
|
| 988 |
+
EXPECT_EQ(
|
| 989 |
+
0,
|
| 990 |
+
Distance(p, Type<Span<const int8_t>>(L::Partial(1, 0).Slice<int8_t>(p))
|
| 991 |
+
.data()));
|
| 992 |
+
EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
|
| 993 |
+
L::Partial(1, 0).Slice<int32_t>(p))
|
| 994 |
+
.data()));
|
| 995 |
+
EXPECT_EQ(
|
| 996 |
+
0,
|
| 997 |
+
Distance(p, Type<Span<const int8_t>>(L::Partial(5, 3).Slice<int8_t>(p))
|
| 998 |
+
.data()));
|
| 999 |
+
EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
|
| 1000 |
+
L::Partial(5, 3).Slice<int32_t>(p))
|
| 1001 |
+
.data()));
|
| 1002 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
|
| 1003 |
+
L::Partial(0, 0, 0).Slice<int8_t>(p))
|
| 1004 |
+
.data()));
|
| 1005 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const int32_t>>(
|
| 1006 |
+
L::Partial(0, 0, 0).Slice<int32_t>(p))
|
| 1007 |
+
.data()));
|
| 1008 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const Int128>>(
|
| 1009 |
+
L::Partial(0, 0, 0).Slice<Int128>(p))
|
| 1010 |
+
.data()));
|
| 1011 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
|
| 1012 |
+
L::Partial(1, 0, 0).Slice<int8_t>(p))
|
| 1013 |
+
.data()));
|
| 1014 |
+
EXPECT_EQ(4, Distance(p, Type<Span<const int32_t>>(
|
| 1015 |
+
L::Partial(1, 0, 0).Slice<int32_t>(p))
|
| 1016 |
+
.data()));
|
| 1017 |
+
EXPECT_EQ(8, Distance(p, Type<Span<const Int128>>(
|
| 1018 |
+
L::Partial(1, 0, 0).Slice<Int128>(p))
|
| 1019 |
+
.data()));
|
| 1020 |
+
EXPECT_EQ(0, Distance(p, Type<Span<const int8_t>>(
|
| 1021 |
+
L::Partial(5, 3, 1).Slice<int8_t>(p))
|
| 1022 |
+
.data()));
|
| 1023 |
+
EXPECT_EQ(24, Distance(p, Type<Span<const Int128>>(
|
| 1024 |
+
L::Partial(5, 3, 1).Slice<Int128>(p))
|
| 1025 |
+
.data()));
|
| 1026 |
+
EXPECT_EQ(8, Distance(p, Type<Span<const int32_t>>(
|
| 1027 |
+
L::Partial(5, 3, 1).Slice<int32_t>(p))
|
| 1028 |
+
.data()));
|
| 1029 |
+
EXPECT_EQ(
|
| 1030 |
+
0,
|
| 1031 |
+
Distance(p,
|
| 1032 |
+
Type<Span<const int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
|
| 1033 |
+
EXPECT_EQ(
|
| 1034 |
+
24,
|
| 1035 |
+
Distance(p,
|
| 1036 |
+
Type<Span<const Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
|
| 1037 |
+
EXPECT_EQ(
|
| 1038 |
+
8,
|
| 1039 |
+
Distance(
|
| 1040 |
+
p, Type<Span<const int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
|
| 1041 |
+
}
|
| 1042 |
+
}
|
| 1043 |
+
|
| 1044 |
+
TEST(Layout, MutableSliceByIndexData) {
|
| 1045 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 1046 |
+
{
|
| 1047 |
+
using L = Layout<int32_t>;
|
| 1048 |
+
EXPECT_EQ(
|
| 1049 |
+
0, Distance(p, Type<Span<int32_t>>(L::Partial(0).Slice<0>(p)).data()));
|
| 1050 |
+
EXPECT_EQ(
|
| 1051 |
+
0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
|
| 1052 |
+
EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3).Slice<0>(p)).data()));
|
| 1053 |
+
}
|
| 1054 |
+
{
|
| 1055 |
+
using L = Layout<int32_t, int32_t>;
|
| 1056 |
+
EXPECT_EQ(
|
| 1057 |
+
0, Distance(p, Type<Span<int32_t>>(L::Partial(3).Slice<0>(p)).data()));
|
| 1058 |
+
EXPECT_EQ(
|
| 1059 |
+
0,
|
| 1060 |
+
Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<0>(p)).data()));
|
| 1061 |
+
EXPECT_EQ(
|
| 1062 |
+
12,
|
| 1063 |
+
Distance(p, Type<Span<int32_t>>(L::Partial(3, 5).Slice<1>(p)).data()));
|
| 1064 |
+
EXPECT_EQ(0, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<0>(p)).data()));
|
| 1065 |
+
EXPECT_EQ(12, Distance(p, Type<Span<int32_t>>(L(3, 5).Slice<1>(p)).data()));
|
| 1066 |
+
}
|
| 1067 |
+
{
|
| 1068 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 1069 |
+
EXPECT_EQ(
|
| 1070 |
+
0, Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<0>(p)).data()));
|
| 1071 |
+
EXPECT_EQ(
|
| 1072 |
+
0, Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<0>(p)).data()));
|
| 1073 |
+
EXPECT_EQ(
|
| 1074 |
+
0, Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<0>(p)).data()));
|
| 1075 |
+
EXPECT_EQ(
|
| 1076 |
+
0,
|
| 1077 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(0, 0).Slice<0>(p)).data()));
|
| 1078 |
+
EXPECT_EQ(
|
| 1079 |
+
0,
|
| 1080 |
+
Distance(p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<1>(p)).data()));
|
| 1081 |
+
EXPECT_EQ(
|
| 1082 |
+
0,
|
| 1083 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(1, 0).Slice<0>(p)).data()));
|
| 1084 |
+
EXPECT_EQ(
|
| 1085 |
+
4,
|
| 1086 |
+
Distance(p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<1>(p)).data()));
|
| 1087 |
+
EXPECT_EQ(
|
| 1088 |
+
0,
|
| 1089 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(5, 3).Slice<0>(p)).data()));
|
| 1090 |
+
EXPECT_EQ(
|
| 1091 |
+
8,
|
| 1092 |
+
Distance(p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<1>(p)).data()));
|
| 1093 |
+
EXPECT_EQ(
|
| 1094 |
+
0, Distance(
|
| 1095 |
+
p, Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<0>(p)).data()));
|
| 1096 |
+
EXPECT_EQ(
|
| 1097 |
+
0, Distance(
|
| 1098 |
+
p, Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<1>(p)).data()));
|
| 1099 |
+
EXPECT_EQ(
|
| 1100 |
+
0, Distance(
|
| 1101 |
+
p, Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<2>(p)).data()));
|
| 1102 |
+
EXPECT_EQ(
|
| 1103 |
+
0, Distance(
|
| 1104 |
+
p, Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<0>(p)).data()));
|
| 1105 |
+
EXPECT_EQ(
|
| 1106 |
+
4, Distance(
|
| 1107 |
+
p, Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<1>(p)).data()));
|
| 1108 |
+
EXPECT_EQ(
|
| 1109 |
+
8, Distance(
|
| 1110 |
+
p, Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<2>(p)).data()));
|
| 1111 |
+
EXPECT_EQ(
|
| 1112 |
+
0, Distance(
|
| 1113 |
+
p, Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<0>(p)).data()));
|
| 1114 |
+
EXPECT_EQ(
|
| 1115 |
+
24, Distance(
|
| 1116 |
+
p, Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<2>(p)).data()));
|
| 1117 |
+
EXPECT_EQ(
|
| 1118 |
+
8, Distance(
|
| 1119 |
+
p, Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<1>(p)).data()));
|
| 1120 |
+
EXPECT_EQ(0,
|
| 1121 |
+
Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<0>(p)).data()));
|
| 1122 |
+
EXPECT_EQ(24,
|
| 1123 |
+
Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<2>(p)).data()));
|
| 1124 |
+
EXPECT_EQ(8,
|
| 1125 |
+
Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<1>(p)).data()));
|
| 1126 |
+
}
|
| 1127 |
+
}
|
| 1128 |
+
|
| 1129 |
+
TEST(Layout, MutableSliceByTypeData) {
|
| 1130 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 1131 |
+
{
|
| 1132 |
+
using L = Layout<int32_t>;
|
| 1133 |
+
EXPECT_EQ(
|
| 1134 |
+
0, Distance(
|
| 1135 |
+
p, Type<Span<int32_t>>(L::Partial(0).Slice<int32_t>(p)).data()));
|
| 1136 |
+
EXPECT_EQ(
|
| 1137 |
+
0, Distance(
|
| 1138 |
+
p, Type<Span<int32_t>>(L::Partial(3).Slice<int32_t>(p)).data()));
|
| 1139 |
+
EXPECT_EQ(0,
|
| 1140 |
+
Distance(p, Type<Span<int32_t>>(L(3).Slice<int32_t>(p)).data()));
|
| 1141 |
+
}
|
| 1142 |
+
{
|
| 1143 |
+
using L = Layout<int8_t, int32_t, Int128>;
|
| 1144 |
+
EXPECT_EQ(
|
| 1145 |
+
0,
|
| 1146 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(0).Slice<int8_t>(p)).data()));
|
| 1147 |
+
EXPECT_EQ(
|
| 1148 |
+
0,
|
| 1149 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(1).Slice<int8_t>(p)).data()));
|
| 1150 |
+
EXPECT_EQ(
|
| 1151 |
+
0,
|
| 1152 |
+
Distance(p, Type<Span<int8_t>>(L::Partial(5).Slice<int8_t>(p)).data()));
|
| 1153 |
+
EXPECT_EQ(
|
| 1154 |
+
0,
|
| 1155 |
+
Distance(p,
|
| 1156 |
+
Type<Span<int8_t>>(L::Partial(0, 0).Slice<int8_t>(p)).data()));
|
| 1157 |
+
EXPECT_EQ(
|
| 1158 |
+
0,
|
| 1159 |
+
Distance(
|
| 1160 |
+
p, Type<Span<int32_t>>(L::Partial(0, 0).Slice<int32_t>(p)).data()));
|
| 1161 |
+
EXPECT_EQ(
|
| 1162 |
+
0,
|
| 1163 |
+
Distance(p,
|
| 1164 |
+
Type<Span<int8_t>>(L::Partial(1, 0).Slice<int8_t>(p)).data()));
|
| 1165 |
+
EXPECT_EQ(
|
| 1166 |
+
4,
|
| 1167 |
+
Distance(
|
| 1168 |
+
p, Type<Span<int32_t>>(L::Partial(1, 0).Slice<int32_t>(p)).data()));
|
| 1169 |
+
EXPECT_EQ(
|
| 1170 |
+
0,
|
| 1171 |
+
Distance(p,
|
| 1172 |
+
Type<Span<int8_t>>(L::Partial(5, 3).Slice<int8_t>(p)).data()));
|
| 1173 |
+
EXPECT_EQ(
|
| 1174 |
+
8,
|
| 1175 |
+
Distance(
|
| 1176 |
+
p, Type<Span<int32_t>>(L::Partial(5, 3).Slice<int32_t>(p)).data()));
|
| 1177 |
+
EXPECT_EQ(
|
| 1178 |
+
0,
|
| 1179 |
+
Distance(
|
| 1180 |
+
p,
|
| 1181 |
+
Type<Span<int8_t>>(L::Partial(0, 0, 0).Slice<int8_t>(p)).data()));
|
| 1182 |
+
EXPECT_EQ(
|
| 1183 |
+
0,
|
| 1184 |
+
Distance(
|
| 1185 |
+
p,
|
| 1186 |
+
Type<Span<int32_t>>(L::Partial(0, 0, 0).Slice<int32_t>(p)).data()));
|
| 1187 |
+
EXPECT_EQ(
|
| 1188 |
+
0,
|
| 1189 |
+
Distance(
|
| 1190 |
+
p,
|
| 1191 |
+
Type<Span<Int128>>(L::Partial(0, 0, 0).Slice<Int128>(p)).data()));
|
| 1192 |
+
EXPECT_EQ(
|
| 1193 |
+
0,
|
| 1194 |
+
Distance(
|
| 1195 |
+
p,
|
| 1196 |
+
Type<Span<int8_t>>(L::Partial(1, 0, 0).Slice<int8_t>(p)).data()));
|
| 1197 |
+
EXPECT_EQ(
|
| 1198 |
+
4,
|
| 1199 |
+
Distance(
|
| 1200 |
+
p,
|
| 1201 |
+
Type<Span<int32_t>>(L::Partial(1, 0, 0).Slice<int32_t>(p)).data()));
|
| 1202 |
+
EXPECT_EQ(
|
| 1203 |
+
8,
|
| 1204 |
+
Distance(
|
| 1205 |
+
p,
|
| 1206 |
+
Type<Span<Int128>>(L::Partial(1, 0, 0).Slice<Int128>(p)).data()));
|
| 1207 |
+
EXPECT_EQ(
|
| 1208 |
+
0,
|
| 1209 |
+
Distance(
|
| 1210 |
+
p,
|
| 1211 |
+
Type<Span<int8_t>>(L::Partial(5, 3, 1).Slice<int8_t>(p)).data()));
|
| 1212 |
+
EXPECT_EQ(
|
| 1213 |
+
24,
|
| 1214 |
+
Distance(
|
| 1215 |
+
p,
|
| 1216 |
+
Type<Span<Int128>>(L::Partial(5, 3, 1).Slice<Int128>(p)).data()));
|
| 1217 |
+
EXPECT_EQ(
|
| 1218 |
+
8,
|
| 1219 |
+
Distance(
|
| 1220 |
+
p,
|
| 1221 |
+
Type<Span<int32_t>>(L::Partial(5, 3, 1).Slice<int32_t>(p)).data()));
|
| 1222 |
+
EXPECT_EQ(
|
| 1223 |
+
0, Distance(p, Type<Span<int8_t>>(L(5, 3, 1).Slice<int8_t>(p)).data()));
|
| 1224 |
+
EXPECT_EQ(
|
| 1225 |
+
24,
|
| 1226 |
+
Distance(p, Type<Span<Int128>>(L(5, 3, 1).Slice<Int128>(p)).data()));
|
| 1227 |
+
EXPECT_EQ(
|
| 1228 |
+
8,
|
| 1229 |
+
Distance(p, Type<Span<int32_t>>(L(5, 3, 1).Slice<int32_t>(p)).data()));
|
| 1230 |
+
}
|
| 1231 |
+
}
|
| 1232 |
+
|
| 1233 |
+
MATCHER_P(IsSameSlice, slice, "") {
|
| 1234 |
+
return arg.size() == slice.size() && arg.data() == slice.data();
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
+
template <typename... M>
|
| 1238 |
+
class TupleMatcher {
|
| 1239 |
+
public:
|
| 1240 |
+
explicit TupleMatcher(M... matchers) : matchers_(std::move(matchers)...) {}
|
| 1241 |
+
|
| 1242 |
+
template <typename Tuple>
|
| 1243 |
+
bool MatchAndExplain(const Tuple& p,
|
| 1244 |
+
testing::MatchResultListener* /* listener */) const {
|
| 1245 |
+
static_assert(std::tuple_size<Tuple>::value == sizeof...(M), "");
|
| 1246 |
+
return MatchAndExplainImpl(
|
| 1247 |
+
p, absl::make_index_sequence<std::tuple_size<Tuple>::value>{});
|
| 1248 |
+
}
|
| 1249 |
+
|
| 1250 |
+
// For the matcher concept. Left empty as we don't really need the diagnostics
|
| 1251 |
+
// right now.
|
| 1252 |
+
void DescribeTo(::std::ostream* os) const {}
|
| 1253 |
+
void DescribeNegationTo(::std::ostream* os) const {}
|
| 1254 |
+
|
| 1255 |
+
private:
|
| 1256 |
+
template <typename Tuple, size_t... Is>
|
| 1257 |
+
bool MatchAndExplainImpl(const Tuple& p, absl::index_sequence<Is...>) const {
|
| 1258 |
+
// Using std::min as a simple variadic "and".
|
| 1259 |
+
return std::min(
|
| 1260 |
+
{true, testing::SafeMatcherCast<
|
| 1261 |
+
const typename std::tuple_element<Is, Tuple>::type&>(
|
| 1262 |
+
std::get<Is>(matchers_))
|
| 1263 |
+
.Matches(std::get<Is>(p))...});
|
| 1264 |
+
}
|
| 1265 |
+
|
| 1266 |
+
std::tuple<M...> matchers_;
|
| 1267 |
+
};
|
| 1268 |
+
|
| 1269 |
+
template <typename... M>
|
| 1270 |
+
testing::PolymorphicMatcher<TupleMatcher<M...>> Tuple(M... matchers) {
|
| 1271 |
+
return testing::MakePolymorphicMatcher(
|
| 1272 |
+
TupleMatcher<M...>(std::move(matchers)...));
|
| 1273 |
+
}
|
| 1274 |
+
|
| 1275 |
+
TEST(Layout, Slices) {
|
| 1276 |
+
alignas(max_align_t) const unsigned char p[100] = {0};
|
| 1277 |
+
using L = Layout<int8_t, int8_t, Int128>;
|
| 1278 |
+
{
|
| 1279 |
+
const auto x = L::Partial();
|
| 1280 |
+
EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
|
| 1281 |
+
}
|
| 1282 |
+
{
|
| 1283 |
+
const auto x = L::Partial(1);
|
| 1284 |
+
EXPECT_THAT(Type<std::tuple<Span<const int8_t>>>(x.Slices(p)),
|
| 1285 |
+
Tuple(IsSameSlice(x.Slice<0>(p))));
|
| 1286 |
+
}
|
| 1287 |
+
{
|
| 1288 |
+
const auto x = L::Partial(1, 2);
|
| 1289 |
+
EXPECT_THAT(
|
| 1290 |
+
(Type<std::tuple<Span<const int8_t>, Span<const int8_t>>>(x.Slices(p))),
|
| 1291 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
|
| 1292 |
+
}
|
| 1293 |
+
{
|
| 1294 |
+
const auto x = L::Partial(1, 2, 3);
|
| 1295 |
+
EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
|
| 1296 |
+
Span<const Int128>>>(x.Slices(p))),
|
| 1297 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
|
| 1298 |
+
IsSameSlice(x.Slice<2>(p))));
|
| 1299 |
+
}
|
| 1300 |
+
{
|
| 1301 |
+
const L x(1, 2, 3);
|
| 1302 |
+
EXPECT_THAT((Type<std::tuple<Span<const int8_t>, Span<const int8_t>,
|
| 1303 |
+
Span<const Int128>>>(x.Slices(p))),
|
| 1304 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
|
| 1305 |
+
IsSameSlice(x.Slice<2>(p))));
|
| 1306 |
+
}
|
| 1307 |
+
}
|
| 1308 |
+
|
| 1309 |
+
TEST(Layout, MutableSlices) {
|
| 1310 |
+
alignas(max_align_t) unsigned char p[100] = {0};
|
| 1311 |
+
using L = Layout<int8_t, int8_t, Int128>;
|
| 1312 |
+
{
|
| 1313 |
+
const auto x = L::Partial();
|
| 1314 |
+
EXPECT_THAT(Type<std::tuple<>>(x.Slices(p)), Tuple());
|
| 1315 |
+
}
|
| 1316 |
+
{
|
| 1317 |
+
const auto x = L::Partial(1);
|
| 1318 |
+
EXPECT_THAT(Type<std::tuple<Span<int8_t>>>(x.Slices(p)),
|
| 1319 |
+
Tuple(IsSameSlice(x.Slice<0>(p))));
|
| 1320 |
+
}
|
| 1321 |
+
{
|
| 1322 |
+
const auto x = L::Partial(1, 2);
|
| 1323 |
+
EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>>>(x.Slices(p))),
|
| 1324 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p))));
|
| 1325 |
+
}
|
| 1326 |
+
{
|
| 1327 |
+
const auto x = L::Partial(1, 2, 3);
|
| 1328 |
+
EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
|
| 1329 |
+
x.Slices(p))),
|
| 1330 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
|
| 1331 |
+
IsSameSlice(x.Slice<2>(p))));
|
| 1332 |
+
}
|
| 1333 |
+
{
|
| 1334 |
+
const L x(1, 2, 3);
|
| 1335 |
+
EXPECT_THAT((Type<std::tuple<Span<int8_t>, Span<int8_t>, Span<Int128>>>(
|
| 1336 |
+
x.Slices(p))),
|
| 1337 |
+
Tuple(IsSameSlice(x.Slice<0>(p)), IsSameSlice(x.Slice<1>(p)),
|
| 1338 |
+
IsSameSlice(x.Slice<2>(p))));
|
| 1339 |
+
}
|
| 1340 |
+
}
|
| 1341 |
+
|
| 1342 |
+
TEST(Layout, UnalignedTypes) {
|
| 1343 |
+
constexpr Layout<unsigned char, unsigned char, unsigned char> x(1, 2, 3);
|
| 1344 |
+
alignas(max_align_t) unsigned char p[x.AllocSize() + 1];
|
| 1345 |
+
EXPECT_THAT(x.Pointers(p + 1), Tuple(p + 1, p + 2, p + 4));
|
| 1346 |
+
}
|
| 1347 |
+
|
| 1348 |
+
TEST(Layout, CustomAlignment) {
|
| 1349 |
+
constexpr Layout<unsigned char, Aligned<unsigned char, 8>> x(1, 2);
|
| 1350 |
+
alignas(max_align_t) unsigned char p[x.AllocSize()];
|
| 1351 |
+
EXPECT_EQ(10, x.AllocSize());
|
| 1352 |
+
EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 8));
|
| 1353 |
+
}
|
| 1354 |
+
|
| 1355 |
+
TEST(Layout, OverAligned) {
|
| 1356 |
+
constexpr size_t M = alignof(max_align_t);
|
| 1357 |
+
constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
|
| 1358 |
+
#ifdef __GNUC__
|
| 1359 |
+
// Using __attribute__ ((aligned ())) instead of alignas to bypass a gcc bug:
|
| 1360 |
+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357
|
| 1361 |
+
__attribute__((aligned(2 * M))) unsigned char p[x.AllocSize()];
|
| 1362 |
+
#else
|
| 1363 |
+
alignas(2 * M) unsigned char p[x.AllocSize()];
|
| 1364 |
+
#endif
|
| 1365 |
+
EXPECT_EQ(2 * M + 3, x.AllocSize());
|
| 1366 |
+
EXPECT_THAT(x.Pointers(p), Tuple(p + 0, p + 2 * M));
|
| 1367 |
+
}
|
| 1368 |
+
|
| 1369 |
+
TEST(Layout, Alignment) {
|
| 1370 |
+
static_assert(Layout<int8_t>::Alignment() == 1, "");
|
| 1371 |
+
static_assert(Layout<int32_t>::Alignment() == 4, "");
|
| 1372 |
+
static_assert(Layout<Int64>::Alignment() == 8, "");
|
| 1373 |
+
static_assert(Layout<Aligned<int8_t, 64>>::Alignment() == 64, "");
|
| 1374 |
+
static_assert(Layout<int8_t, int32_t, Int64>::Alignment() == 8, "");
|
| 1375 |
+
static_assert(Layout<int8_t, Int64, int32_t>::Alignment() == 8, "");
|
| 1376 |
+
static_assert(Layout<int32_t, int8_t, Int64>::Alignment() == 8, "");
|
| 1377 |
+
static_assert(Layout<int32_t, Int64, int8_t>::Alignment() == 8, "");
|
| 1378 |
+
static_assert(Layout<Int64, int8_t, int32_t>::Alignment() == 8, "");
|
| 1379 |
+
static_assert(Layout<Int64, int32_t, int8_t>::Alignment() == 8, "");
|
| 1380 |
+
}
|
| 1381 |
+
|
| 1382 |
+
TEST(Layout, ConstexprPartial) {
|
| 1383 |
+
constexpr size_t M = alignof(max_align_t);
|
| 1384 |
+
constexpr Layout<unsigned char, Aligned<unsigned char, 2 * M>> x(1, 3);
|
| 1385 |
+
static_assert(x.Partial(1).template Offset<1>() == 2 * M, "");
|
| 1386 |
+
}
|
| 1387 |
+
// [from, to)
|
| 1388 |
+
struct Region {
|
| 1389 |
+
size_t from;
|
| 1390 |
+
size_t to;
|
| 1391 |
+
};
|
| 1392 |
+
|
| 1393 |
+
void ExpectRegionPoisoned(const unsigned char* p, size_t n, bool poisoned) {
|
| 1394 |
+
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
|
| 1395 |
+
for (size_t i = 0; i != n; ++i) {
|
| 1396 |
+
EXPECT_EQ(poisoned, __asan_address_is_poisoned(p + i));
|
| 1397 |
+
}
|
| 1398 |
+
#endif
|
| 1399 |
+
}
|
| 1400 |
+
|
| 1401 |
+
template <size_t N>
|
| 1402 |
+
void ExpectPoisoned(const unsigned char (&buf)[N],
|
| 1403 |
+
std::initializer_list<Region> reg) {
|
| 1404 |
+
size_t prev = 0;
|
| 1405 |
+
for (const Region& r : reg) {
|
| 1406 |
+
ExpectRegionPoisoned(buf + prev, r.from - prev, false);
|
| 1407 |
+
ExpectRegionPoisoned(buf + r.from, r.to - r.from, true);
|
| 1408 |
+
prev = r.to;
|
| 1409 |
+
}
|
| 1410 |
+
ExpectRegionPoisoned(buf + prev, N - prev, false);
|
| 1411 |
+
}
|
| 1412 |
+
|
| 1413 |
+
TEST(Layout, PoisonPadding) {
|
| 1414 |
+
using L = Layout<int8_t, Int64, int32_t, Int128>;
|
| 1415 |
+
|
| 1416 |
+
constexpr size_t n = L::Partial(1, 2, 3, 4).AllocSize();
|
| 1417 |
+
{
|
| 1418 |
+
constexpr auto x = L::Partial();
|
| 1419 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1420 |
+
x.PoisonPadding(c);
|
| 1421 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1422 |
+
ExpectPoisoned(c, {});
|
| 1423 |
+
}
|
| 1424 |
+
{
|
| 1425 |
+
constexpr auto x = L::Partial(1);
|
| 1426 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1427 |
+
x.PoisonPadding(c);
|
| 1428 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1429 |
+
ExpectPoisoned(c, {{1, 8}});
|
| 1430 |
+
}
|
| 1431 |
+
{
|
| 1432 |
+
constexpr auto x = L::Partial(1, 2);
|
| 1433 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1434 |
+
x.PoisonPadding(c);
|
| 1435 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1436 |
+
ExpectPoisoned(c, {{1, 8}});
|
| 1437 |
+
}
|
| 1438 |
+
{
|
| 1439 |
+
constexpr auto x = L::Partial(1, 2, 3);
|
| 1440 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1441 |
+
x.PoisonPadding(c);
|
| 1442 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1443 |
+
ExpectPoisoned(c, {{1, 8}, {36, 40}});
|
| 1444 |
+
}
|
| 1445 |
+
{
|
| 1446 |
+
constexpr auto x = L::Partial(1, 2, 3, 4);
|
| 1447 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1448 |
+
x.PoisonPadding(c);
|
| 1449 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1450 |
+
ExpectPoisoned(c, {{1, 8}, {36, 40}});
|
| 1451 |
+
}
|
| 1452 |
+
{
|
| 1453 |
+
constexpr L x(1, 2, 3, 4);
|
| 1454 |
+
alignas(max_align_t) const unsigned char c[n] = {};
|
| 1455 |
+
x.PoisonPadding(c);
|
| 1456 |
+
EXPECT_EQ(x.Slices(c), x.Slices(c));
|
| 1457 |
+
ExpectPoisoned(c, {{1, 8}, {36, 40}});
|
| 1458 |
+
}
|
| 1459 |
+
}
|
| 1460 |
+
|
| 1461 |
+
TEST(Layout, DebugString) {
|
| 1462 |
+
{
|
| 1463 |
+
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial();
|
| 1464 |
+
EXPECT_EQ("@0<signed char>(1)", x.DebugString());
|
| 1465 |
+
}
|
| 1466 |
+
{
|
| 1467 |
+
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1);
|
| 1468 |
+
EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)", x.DebugString());
|
| 1469 |
+
}
|
| 1470 |
+
{
|
| 1471 |
+
constexpr auto x = Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2);
|
| 1472 |
+
EXPECT_EQ("@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)",
|
| 1473 |
+
x.DebugString());
|
| 1474 |
+
}
|
| 1475 |
+
{
|
| 1476 |
+
constexpr auto x =
|
| 1477 |
+
Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3);
|
| 1478 |
+
EXPECT_EQ(
|
| 1479 |
+
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
|
| 1480 |
+
"@16" +
|
| 1481 |
+
Int128::Name() + "(16)",
|
| 1482 |
+
x.DebugString());
|
| 1483 |
+
}
|
| 1484 |
+
{
|
| 1485 |
+
constexpr auto x =
|
| 1486 |
+
Layout<int8_t, int32_t, int8_t, Int128>::Partial(1, 2, 3, 4);
|
| 1487 |
+
EXPECT_EQ(
|
| 1488 |
+
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
|
| 1489 |
+
"@16" +
|
| 1490 |
+
Int128::Name() + "(16)[4]",
|
| 1491 |
+
x.DebugString());
|
| 1492 |
+
}
|
| 1493 |
+
{
|
| 1494 |
+
constexpr Layout<int8_t, int32_t, int8_t, Int128> x(1, 2, 3, 4);
|
| 1495 |
+
EXPECT_EQ(
|
| 1496 |
+
"@0<signed char>(1)[1]; @4<int>(4)[2]; @12<signed char>(1)[3]; "
|
| 1497 |
+
"@16" +
|
| 1498 |
+
Int128::Name() + "(16)[4]",
|
| 1499 |
+
x.DebugString());
|
| 1500 |
+
}
|
| 1501 |
+
}
|
| 1502 |
+
|
| 1503 |
+
TEST(Layout, CharTypes) {
|
| 1504 |
+
constexpr Layout<int32_t> x(1);
|
| 1505 |
+
alignas(max_align_t) char c[x.AllocSize()] = {};
|
| 1506 |
+
alignas(max_align_t) unsigned char uc[x.AllocSize()] = {};
|
| 1507 |
+
alignas(max_align_t) signed char sc[x.AllocSize()] = {};
|
| 1508 |
+
alignas(max_align_t) const char cc[x.AllocSize()] = {};
|
| 1509 |
+
alignas(max_align_t) const unsigned char cuc[x.AllocSize()] = {};
|
| 1510 |
+
alignas(max_align_t) const signed char csc[x.AllocSize()] = {};
|
| 1511 |
+
|
| 1512 |
+
Type<int32_t*>(x.Pointer<0>(c));
|
| 1513 |
+
Type<int32_t*>(x.Pointer<0>(uc));
|
| 1514 |
+
Type<int32_t*>(x.Pointer<0>(sc));
|
| 1515 |
+
Type<const int32_t*>(x.Pointer<0>(cc));
|
| 1516 |
+
Type<const int32_t*>(x.Pointer<0>(cuc));
|
| 1517 |
+
Type<const int32_t*>(x.Pointer<0>(csc));
|
| 1518 |
+
|
| 1519 |
+
Type<int32_t*>(x.Pointer<int32_t>(c));
|
| 1520 |
+
Type<int32_t*>(x.Pointer<int32_t>(uc));
|
| 1521 |
+
Type<int32_t*>(x.Pointer<int32_t>(sc));
|
| 1522 |
+
Type<const int32_t*>(x.Pointer<int32_t>(cc));
|
| 1523 |
+
Type<const int32_t*>(x.Pointer<int32_t>(cuc));
|
| 1524 |
+
Type<const int32_t*>(x.Pointer<int32_t>(csc));
|
| 1525 |
+
|
| 1526 |
+
Type<std::tuple<int32_t*>>(x.Pointers(c));
|
| 1527 |
+
Type<std::tuple<int32_t*>>(x.Pointers(uc));
|
| 1528 |
+
Type<std::tuple<int32_t*>>(x.Pointers(sc));
|
| 1529 |
+
Type<std::tuple<const int32_t*>>(x.Pointers(cc));
|
| 1530 |
+
Type<std::tuple<const int32_t*>>(x.Pointers(cuc));
|
| 1531 |
+
Type<std::tuple<const int32_t*>>(x.Pointers(csc));
|
| 1532 |
+
|
| 1533 |
+
Type<Span<int32_t>>(x.Slice<0>(c));
|
| 1534 |
+
Type<Span<int32_t>>(x.Slice<0>(uc));
|
| 1535 |
+
Type<Span<int32_t>>(x.Slice<0>(sc));
|
| 1536 |
+
Type<Span<const int32_t>>(x.Slice<0>(cc));
|
| 1537 |
+
Type<Span<const int32_t>>(x.Slice<0>(cuc));
|
| 1538 |
+
Type<Span<const int32_t>>(x.Slice<0>(csc));
|
| 1539 |
+
|
| 1540 |
+
Type<std::tuple<Span<int32_t>>>(x.Slices(c));
|
| 1541 |
+
Type<std::tuple<Span<int32_t>>>(x.Slices(uc));
|
| 1542 |
+
Type<std::tuple<Span<int32_t>>>(x.Slices(sc));
|
| 1543 |
+
Type<std::tuple<Span<const int32_t>>>(x.Slices(cc));
|
| 1544 |
+
Type<std::tuple<Span<const int32_t>>>(x.Slices(cuc));
|
| 1545 |
+
Type<std::tuple<Span<const int32_t>>>(x.Slices(csc));
|
| 1546 |
+
}
|
| 1547 |
+
|
| 1548 |
+
TEST(Layout, ConstElementType) {
|
| 1549 |
+
constexpr Layout<const int32_t> x(1);
|
| 1550 |
+
alignas(int32_t) char c[x.AllocSize()] = {};
|
| 1551 |
+
const char* cc = c;
|
| 1552 |
+
const int32_t* p = reinterpret_cast<const int32_t*>(cc);
|
| 1553 |
+
|
| 1554 |
+
EXPECT_EQ(alignof(int32_t), x.Alignment());
|
| 1555 |
+
|
| 1556 |
+
EXPECT_EQ(0, x.Offset<0>());
|
| 1557 |
+
EXPECT_EQ(0, x.Offset<const int32_t>());
|
| 1558 |
+
|
| 1559 |
+
EXPECT_THAT(x.Offsets(), ElementsAre(0));
|
| 1560 |
+
|
| 1561 |
+
EXPECT_EQ(1, x.Size<0>());
|
| 1562 |
+
EXPECT_EQ(1, x.Size<const int32_t>());
|
| 1563 |
+
|
| 1564 |
+
EXPECT_THAT(x.Sizes(), ElementsAre(1));
|
| 1565 |
+
|
| 1566 |
+
EXPECT_EQ(sizeof(int32_t), x.AllocSize());
|
| 1567 |
+
|
| 1568 |
+
EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(c)));
|
| 1569 |
+
EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<0>(cc)));
|
| 1570 |
+
|
| 1571 |
+
EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(c)));
|
| 1572 |
+
EXPECT_EQ(p, Type<const int32_t*>(x.Pointer<const int32_t>(cc)));
|
| 1573 |
+
|
| 1574 |
+
EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(c)), Tuple(p));
|
| 1575 |
+
EXPECT_THAT(Type<std::tuple<const int32_t*>>(x.Pointers(cc)), Tuple(p));
|
| 1576 |
+
|
| 1577 |
+
EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(c)),
|
| 1578 |
+
IsSameSlice(Span<const int32_t>(p, 1)));
|
| 1579 |
+
EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<0>(cc)),
|
| 1580 |
+
IsSameSlice(Span<const int32_t>(p, 1)));
|
| 1581 |
+
|
| 1582 |
+
EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(c)),
|
| 1583 |
+
IsSameSlice(Span<const int32_t>(p, 1)));
|
| 1584 |
+
EXPECT_THAT(Type<Span<const int32_t>>(x.Slice<const int32_t>(cc)),
|
| 1585 |
+
IsSameSlice(Span<const int32_t>(p, 1)));
|
| 1586 |
+
|
| 1587 |
+
EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(c)),
|
| 1588 |
+
Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
|
| 1589 |
+
EXPECT_THAT(Type<std::tuple<Span<const int32_t>>>(x.Slices(cc)),
|
| 1590 |
+
Tuple(IsSameSlice(Span<const int32_t>(p, 1))));
|
| 1591 |
+
}
|
| 1592 |
+
|
| 1593 |
+
namespace example {
|
| 1594 |
+
|
| 1595 |
+
// Immutable move-only string with sizeof equal to sizeof(void*). The string
|
| 1596 |
+
// size and the characters are kept in the same heap allocation.
|
| 1597 |
+
class CompactString {
|
| 1598 |
+
public:
|
| 1599 |
+
CompactString(const char* s = "") { // NOLINT
|
| 1600 |
+
const size_t size = strlen(s);
|
| 1601 |
+
// size_t[1], followed by char[size + 1].
|
| 1602 |
+
// This statement doesn't allocate memory.
|
| 1603 |
+
const L layout(1, size + 1);
|
| 1604 |
+
// AllocSize() tells us how much memory we need to allocate for all our
|
| 1605 |
+
// data.
|
| 1606 |
+
p_.reset(new unsigned char[layout.AllocSize()]);
|
| 1607 |
+
// If running under ASAN, mark the padding bytes, if any, to catch memory
|
| 1608 |
+
// errors.
|
| 1609 |
+
layout.PoisonPadding(p_.get());
|
| 1610 |
+
// Store the size in the allocation.
|
| 1611 |
+
// Pointer<size_t>() is a synonym for Pointer<0>().
|
| 1612 |
+
*layout.Pointer<size_t>(p_.get()) = size;
|
| 1613 |
+
// Store the characters in the allocation.
|
| 1614 |
+
memcpy(layout.Pointer<char>(p_.get()), s, size + 1);
|
| 1615 |
+
}
|
| 1616 |
+
|
| 1617 |
+
size_t size() const {
|
| 1618 |
+
// Equivalent to reinterpret_cast<size_t&>(*p).
|
| 1619 |
+
return *L::Partial().Pointer<size_t>(p_.get());
|
| 1620 |
+
}
|
| 1621 |
+
|
| 1622 |
+
const char* c_str() const {
|
| 1623 |
+
// Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)).
|
| 1624 |
+
// The argument in Partial(1) specifies that we have size_t[1] in front of
|
| 1625 |
+
// the characters.
|
| 1626 |
+
return L::Partial(1).Pointer<char>(p_.get());
|
| 1627 |
+
}
|
| 1628 |
+
|
| 1629 |
+
private:
|
| 1630 |
+
// Our heap allocation contains a size_t followed by an array of chars.
|
| 1631 |
+
using L = Layout<size_t, char>;
|
| 1632 |
+
std::unique_ptr<unsigned char[]> p_;
|
| 1633 |
+
};
|
| 1634 |
+
|
| 1635 |
+
TEST(CompactString, Works) {
|
| 1636 |
+
CompactString s = "hello";
|
| 1637 |
+
EXPECT_EQ(5, s.size());
|
| 1638 |
+
EXPECT_STREQ("hello", s.c_str());
|
| 1639 |
+
}
|
| 1640 |
+
|
| 1641 |
+
} // namespace example
|
| 1642 |
+
|
| 1643 |
+
} // namespace
|
| 1644 |
+
} // namespace container_internal
|
| 1645 |
+
ABSL_NAMESPACE_END
|
| 1646 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/raw_hash_map.h
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
|
| 17 |
+
|
| 18 |
+
#include <tuple>
|
| 19 |
+
#include <type_traits>
|
| 20 |
+
#include <utility>
|
| 21 |
+
|
| 22 |
+
#include "absl/base/attributes.h"
|
| 23 |
+
#include "absl/base/config.h"
|
| 24 |
+
#include "absl/base/internal/throw_delegate.h"
|
| 25 |
+
#include "absl/container/internal/container_memory.h"
|
| 26 |
+
#include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export
|
| 27 |
+
|
| 28 |
+
namespace absl {
|
| 29 |
+
ABSL_NAMESPACE_BEGIN
|
| 30 |
+
namespace container_internal {
|
| 31 |
+
|
| 32 |
+
template <class Policy, class Hash, class Eq, class Alloc>
|
| 33 |
+
class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> {
|
| 34 |
+
// P is Policy. It's passed as a template argument to support maps that have
|
| 35 |
+
// incomplete types as values, as in unordered_map<K, IncompleteType>.
|
| 36 |
+
// MappedReference<> may be a non-reference type.
|
| 37 |
+
template <class P>
|
| 38 |
+
using MappedReference = decltype(P::value(
|
| 39 |
+
std::addressof(std::declval<typename raw_hash_map::reference>())));
|
| 40 |
+
|
| 41 |
+
// MappedConstReference<> may be a non-reference type.
|
| 42 |
+
template <class P>
|
| 43 |
+
using MappedConstReference = decltype(P::value(
|
| 44 |
+
std::addressof(std::declval<typename raw_hash_map::const_reference>())));
|
| 45 |
+
|
| 46 |
+
using KeyArgImpl =
|
| 47 |
+
KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>;
|
| 48 |
+
|
| 49 |
+
public:
|
| 50 |
+
using key_type = typename Policy::key_type;
|
| 51 |
+
using mapped_type = typename Policy::mapped_type;
|
| 52 |
+
template <class K>
|
| 53 |
+
using key_arg = typename KeyArgImpl::template type<K, key_type>;
|
| 54 |
+
|
| 55 |
+
static_assert(!std::is_reference<key_type>::value, "");
|
| 56 |
+
|
| 57 |
+
// TODO(b/187807849): Evaluate whether to support reference mapped_type and
|
| 58 |
+
// remove this assertion if/when it is supported.
|
| 59 |
+
static_assert(!std::is_reference<mapped_type>::value, "");
|
| 60 |
+
|
| 61 |
+
using iterator = typename raw_hash_map::raw_hash_set::iterator;
|
| 62 |
+
using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator;
|
| 63 |
+
|
| 64 |
+
raw_hash_map() {}
|
| 65 |
+
using raw_hash_map::raw_hash_set::raw_hash_set;
|
| 66 |
+
|
| 67 |
+
// The last two template parameters ensure that both arguments are rvalues
|
| 68 |
+
// (lvalue arguments are handled by the overloads below). This is necessary
|
| 69 |
+
// for supporting bitfield arguments.
|
| 70 |
+
//
|
| 71 |
+
// union { int n : 1; };
|
| 72 |
+
// flat_hash_map<int, int> m;
|
| 73 |
+
// m.insert_or_assign(n, n);
|
| 74 |
+
template <class K = key_type, class V = mapped_type, K* = nullptr,
|
| 75 |
+
V* = nullptr>
|
| 76 |
+
std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v)
|
| 77 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 78 |
+
return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v));
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
template <class K = key_type, class V = mapped_type, K* = nullptr>
|
| 82 |
+
std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v)
|
| 83 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 84 |
+
return insert_or_assign_impl(std::forward<K>(k), v);
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
template <class K = key_type, class V = mapped_type, V* = nullptr>
|
| 88 |
+
std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v)
|
| 89 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 90 |
+
return insert_or_assign_impl(k, std::forward<V>(v));
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
template <class K = key_type, class V = mapped_type>
|
| 94 |
+
std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v)
|
| 95 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 96 |
+
return insert_or_assign_impl(k, v);
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
template <class K = key_type, class V = mapped_type, K* = nullptr,
|
| 100 |
+
V* = nullptr>
|
| 101 |
+
iterator insert_or_assign(const_iterator, key_arg<K>&& k,
|
| 102 |
+
V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 103 |
+
return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
template <class K = key_type, class V = mapped_type, K* = nullptr>
|
| 107 |
+
iterator insert_or_assign(const_iterator, key_arg<K>&& k,
|
| 108 |
+
const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 109 |
+
return insert_or_assign(std::forward<K>(k), v).first;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
template <class K = key_type, class V = mapped_type, V* = nullptr>
|
| 113 |
+
iterator insert_or_assign(const_iterator, const key_arg<K>& k,
|
| 114 |
+
V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 115 |
+
return insert_or_assign(k, std::forward<V>(v)).first;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
template <class K = key_type, class V = mapped_type>
|
| 119 |
+
iterator insert_or_assign(const_iterator, const key_arg<K>& k,
|
| 120 |
+
const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 121 |
+
return insert_or_assign(k, v).first;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// All `try_emplace()` overloads make the same guarantees regarding rvalue
|
| 125 |
+
// arguments as `std::unordered_map::try_emplace()`, namely that these
|
| 126 |
+
// functions will not move from rvalue arguments if insertions do not happen.
|
| 127 |
+
template <class K = key_type, class... Args,
|
| 128 |
+
typename std::enable_if<
|
| 129 |
+
!std::is_convertible<K, const_iterator>::value, int>::type = 0,
|
| 130 |
+
K* = nullptr>
|
| 131 |
+
std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args)
|
| 132 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 133 |
+
return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
template <class K = key_type, class... Args,
|
| 137 |
+
typename std::enable_if<
|
| 138 |
+
!std::is_convertible<K, const_iterator>::value, int>::type = 0>
|
| 139 |
+
std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args)
|
| 140 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 141 |
+
return try_emplace_impl(k, std::forward<Args>(args)...);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
template <class K = key_type, class... Args, K* = nullptr>
|
| 145 |
+
iterator try_emplace(const_iterator, key_arg<K>&& k,
|
| 146 |
+
Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 147 |
+
return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
template <class K = key_type, class... Args>
|
| 151 |
+
iterator try_emplace(const_iterator, const key_arg<K>& k,
|
| 152 |
+
Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 153 |
+
return try_emplace(k, std::forward<Args>(args)...).first;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
template <class K = key_type, class P = Policy>
|
| 157 |
+
MappedReference<P> at(const key_arg<K>& key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 158 |
+
auto it = this->find(key);
|
| 159 |
+
if (it == this->end()) {
|
| 160 |
+
base_internal::ThrowStdOutOfRange(
|
| 161 |
+
"absl::container_internal::raw_hash_map<>::at");
|
| 162 |
+
}
|
| 163 |
+
return Policy::value(&*it);
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
template <class K = key_type, class P = Policy>
|
| 167 |
+
MappedConstReference<P> at(const key_arg<K>& key) const
|
| 168 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 169 |
+
auto it = this->find(key);
|
| 170 |
+
if (it == this->end()) {
|
| 171 |
+
base_internal::ThrowStdOutOfRange(
|
| 172 |
+
"absl::container_internal::raw_hash_map<>::at");
|
| 173 |
+
}
|
| 174 |
+
return Policy::value(&*it);
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
template <class K = key_type, class P = Policy, K* = nullptr>
|
| 178 |
+
MappedReference<P> operator[](key_arg<K>&& key)
|
| 179 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 180 |
+
// It is safe to use unchecked_deref here because try_emplace
|
| 181 |
+
// will always return an iterator pointing to a valid item in the table,
|
| 182 |
+
// since it inserts if nothing is found for the given key.
|
| 183 |
+
return Policy::value(
|
| 184 |
+
&this->unchecked_deref(try_emplace(std::forward<K>(key)).first));
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
template <class K = key_type, class P = Policy>
|
| 188 |
+
MappedReference<P> operator[](const key_arg<K>& key)
|
| 189 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 190 |
+
// It is safe to use unchecked_deref here because try_emplace
|
| 191 |
+
// will always return an iterator pointing to a valid item in the table,
|
| 192 |
+
// since it inserts if nothing is found for the given key.
|
| 193 |
+
return Policy::value(&this->unchecked_deref(try_emplace(key).first));
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
private:
|
| 197 |
+
template <class K, class V>
|
| 198 |
+
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v)
|
| 199 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 200 |
+
auto res = this->find_or_prepare_insert(k);
|
| 201 |
+
if (res.second)
|
| 202 |
+
this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v));
|
| 203 |
+
else
|
| 204 |
+
Policy::value(&*this->iterator_at(res.first)) = std::forward<V>(v);
|
| 205 |
+
return {this->iterator_at(res.first), res.second};
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
template <class K = key_type, class... Args>
|
| 209 |
+
std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args)
|
| 210 |
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
| 211 |
+
auto res = this->find_or_prepare_insert(k);
|
| 212 |
+
if (res.second)
|
| 213 |
+
this->emplace_at(res.first, std::piecewise_construct,
|
| 214 |
+
std::forward_as_tuple(std::forward<K>(k)),
|
| 215 |
+
std::forward_as_tuple(std::forward<Args>(args)...));
|
| 216 |
+
return {this->iterator_at(res.first), res.second};
|
| 217 |
+
}
|
| 218 |
+
};
|
| 219 |
+
|
| 220 |
+
} // namespace container_internal
|
| 221 |
+
ABSL_NAMESPACE_END
|
| 222 |
+
} // namespace absl
|
| 223 |
+
|
| 224 |
+
#endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_
|
weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set.cc
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/raw_hash_set.h"
|
| 16 |
+
|
| 17 |
+
#include <atomic>
|
| 18 |
+
#include <cassert>
|
| 19 |
+
#include <cstddef>
|
| 20 |
+
#include <cstdint>
|
| 21 |
+
#include <cstring>
|
| 22 |
+
|
| 23 |
+
#include "absl/base/attributes.h"
|
| 24 |
+
#include "absl/base/config.h"
|
| 25 |
+
#include "absl/base/dynamic_annotations.h"
|
| 26 |
+
#include "absl/container/internal/container_memory.h"
|
| 27 |
+
#include "absl/hash/hash.h"
|
| 28 |
+
|
| 29 |
+
namespace absl {
|
| 30 |
+
ABSL_NAMESPACE_BEGIN
|
| 31 |
+
namespace container_internal {
|
| 32 |
+
|
| 33 |
+
// We have space for `growth_left` before a single block of control bytes. A
|
| 34 |
+
// single block of empty control bytes for tables without any slots allocated.
|
| 35 |
+
// This enables removing a branch in the hot path of find(). In order to ensure
|
| 36 |
+
// that the control bytes are aligned to 16, we have 16 bytes before the control
|
| 37 |
+
// bytes even though growth_left only needs 8.
|
| 38 |
+
constexpr ctrl_t ZeroCtrlT() { return static_cast<ctrl_t>(0); }
|
| 39 |
+
alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[32] = {
|
| 40 |
+
ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(),
|
| 41 |
+
ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(),
|
| 42 |
+
ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(),
|
| 43 |
+
ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(), ZeroCtrlT(),
|
| 44 |
+
ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
|
| 45 |
+
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
|
| 46 |
+
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
|
| 47 |
+
ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty};
|
| 48 |
+
|
| 49 |
+
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
| 50 |
+
constexpr size_t Group::kWidth;
|
| 51 |
+
#endif
|
| 52 |
+
|
| 53 |
+
namespace {
|
| 54 |
+
|
| 55 |
+
// Returns "random" seed.
|
| 56 |
+
inline size_t RandomSeed() {
|
| 57 |
+
#ifdef ABSL_HAVE_THREAD_LOCAL
|
| 58 |
+
static thread_local size_t counter = 0;
|
| 59 |
+
// On Linux kernels >= 5.4 the MSAN runtime has a false-positive when
|
| 60 |
+
// accessing thread local storage data from loaded libraries
|
| 61 |
+
// (https://github.com/google/sanitizers/issues/1265), for this reason counter
|
| 62 |
+
// needs to be annotated as initialized.
|
| 63 |
+
ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(&counter, sizeof(size_t));
|
| 64 |
+
size_t value = ++counter;
|
| 65 |
+
#else // ABSL_HAVE_THREAD_LOCAL
|
| 66 |
+
static std::atomic<size_t> counter(0);
|
| 67 |
+
size_t value = counter.fetch_add(1, std::memory_order_relaxed);
|
| 68 |
+
#endif // ABSL_HAVE_THREAD_LOCAL
|
| 69 |
+
return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
bool ShouldRehashForBugDetection(const ctrl_t* ctrl, size_t capacity) {
|
| 73 |
+
// Note: we can't use the abseil-random library because abseil-random
|
| 74 |
+
// depends on swisstable. We want to return true with probability
|
| 75 |
+
// `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
|
| 76 |
+
// we probe based on a random hash and see if the offset is less than
|
| 77 |
+
// RehashProbabilityConstant().
|
| 78 |
+
return probe(ctrl, capacity, absl::HashOf(RandomSeed())).offset() <
|
| 79 |
+
RehashProbabilityConstant();
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
} // namespace
|
| 83 |
+
|
| 84 |
+
GenerationType* EmptyGeneration() {
|
| 85 |
+
if (SwisstableGenerationsEnabled()) {
|
| 86 |
+
constexpr size_t kNumEmptyGenerations = 1024;
|
| 87 |
+
static constexpr GenerationType kEmptyGenerations[kNumEmptyGenerations]{};
|
| 88 |
+
return const_cast<GenerationType*>(
|
| 89 |
+
&kEmptyGenerations[RandomSeed() % kNumEmptyGenerations]);
|
| 90 |
+
}
|
| 91 |
+
return nullptr;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
bool CommonFieldsGenerationInfoEnabled::
|
| 95 |
+
should_rehash_for_bug_detection_on_insert(const ctrl_t* ctrl,
|
| 96 |
+
size_t capacity) const {
|
| 97 |
+
if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
|
| 98 |
+
if (reserved_growth_ > 0) return false;
|
| 99 |
+
return ShouldRehashForBugDetection(ctrl, capacity);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
bool CommonFieldsGenerationInfoEnabled::should_rehash_for_bug_detection_on_move(
|
| 103 |
+
const ctrl_t* ctrl, size_t capacity) const {
|
| 104 |
+
return ShouldRehashForBugDetection(ctrl, capacity);
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
bool ShouldInsertBackwards(size_t hash, const ctrl_t* ctrl) {
|
| 108 |
+
// To avoid problems with weak hashes and single bit tests, we use % 13.
|
| 109 |
+
// TODO(kfm,sbenza): revisit after we do unconditional mixing
|
| 110 |
+
return (H1(hash, ctrl) ^ RandomSeed()) % 13 > 6;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
|
| 114 |
+
assert(ctrl[capacity] == ctrl_t::kSentinel);
|
| 115 |
+
assert(IsValidCapacity(capacity));
|
| 116 |
+
for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
|
| 117 |
+
Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
|
| 118 |
+
}
|
| 119 |
+
// Copy the cloned ctrl bytes.
|
| 120 |
+
std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
|
| 121 |
+
ctrl[capacity] = ctrl_t::kSentinel;
|
| 122 |
+
}
|
| 123 |
+
// Extern template instantiation for inline function.
|
| 124 |
+
template FindInfo find_first_non_full(const CommonFields&, size_t);
|
| 125 |
+
|
| 126 |
+
FindInfo find_first_non_full_outofline(const CommonFields& common,
|
| 127 |
+
size_t hash) {
|
| 128 |
+
return find_first_non_full(common, hash);
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
// Returns the address of the slot just after slot assuming each slot has the
|
| 132 |
+
// specified size.
|
| 133 |
+
static inline void* NextSlot(void* slot, size_t slot_size) {
|
| 134 |
+
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) + slot_size);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
// Returns the address of the slot just before slot assuming each slot has the
|
| 138 |
+
// specified size.
|
| 139 |
+
static inline void* PrevSlot(void* slot, size_t slot_size) {
|
| 140 |
+
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
void DropDeletesWithoutResize(CommonFields& common,
|
| 144 |
+
const PolicyFunctions& policy, void* tmp_space) {
|
| 145 |
+
void* set = &common;
|
| 146 |
+
void* slot_array = common.slot_array();
|
| 147 |
+
const size_t capacity = common.capacity();
|
| 148 |
+
assert(IsValidCapacity(capacity));
|
| 149 |
+
assert(!is_small(capacity));
|
| 150 |
+
// Algorithm:
|
| 151 |
+
// - mark all DELETED slots as EMPTY
|
| 152 |
+
// - mark all FULL slots as DELETED
|
| 153 |
+
// - for each slot marked as DELETED
|
| 154 |
+
// hash = Hash(element)
|
| 155 |
+
// target = find_first_non_full(hash)
|
| 156 |
+
// if target is in the same group
|
| 157 |
+
// mark slot as FULL
|
| 158 |
+
// else if target is EMPTY
|
| 159 |
+
// transfer element to target
|
| 160 |
+
// mark slot as EMPTY
|
| 161 |
+
// mark target as FULL
|
| 162 |
+
// else if target is DELETED
|
| 163 |
+
// swap current element with target element
|
| 164 |
+
// mark target as FULL
|
| 165 |
+
// repeat procedure for current slot with moved from element (target)
|
| 166 |
+
ctrl_t* ctrl = common.control();
|
| 167 |
+
ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity);
|
| 168 |
+
auto hasher = policy.hash_slot;
|
| 169 |
+
auto transfer = policy.transfer;
|
| 170 |
+
const size_t slot_size = policy.slot_size;
|
| 171 |
+
|
| 172 |
+
size_t total_probe_length = 0;
|
| 173 |
+
void* slot_ptr = SlotAddress(slot_array, 0, slot_size);
|
| 174 |
+
for (size_t i = 0; i != capacity;
|
| 175 |
+
++i, slot_ptr = NextSlot(slot_ptr, slot_size)) {
|
| 176 |
+
assert(slot_ptr == SlotAddress(slot_array, i, slot_size));
|
| 177 |
+
if (!IsDeleted(ctrl[i])) continue;
|
| 178 |
+
const size_t hash = (*hasher)(set, slot_ptr);
|
| 179 |
+
const FindInfo target = find_first_non_full(common, hash);
|
| 180 |
+
const size_t new_i = target.offset;
|
| 181 |
+
total_probe_length += target.probe_length;
|
| 182 |
+
|
| 183 |
+
// Verify if the old and new i fall within the same group wrt the hash.
|
| 184 |
+
// If they do, we don't need to move the object as it falls already in the
|
| 185 |
+
// best probe we can.
|
| 186 |
+
const size_t probe_offset = probe(common, hash).offset();
|
| 187 |
+
const auto probe_index = [probe_offset, capacity](size_t pos) {
|
| 188 |
+
return ((pos - probe_offset) & capacity) / Group::kWidth;
|
| 189 |
+
};
|
| 190 |
+
|
| 191 |
+
// Element doesn't move.
|
| 192 |
+
if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
|
| 193 |
+
SetCtrl(common, i, H2(hash), slot_size);
|
| 194 |
+
continue;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size);
|
| 198 |
+
if (IsEmpty(ctrl[new_i])) {
|
| 199 |
+
// Transfer element to the empty spot.
|
| 200 |
+
// SetCtrl poisons/unpoisons the slots so we have to call it at the
|
| 201 |
+
// right time.
|
| 202 |
+
SetCtrl(common, new_i, H2(hash), slot_size);
|
| 203 |
+
(*transfer)(set, new_slot_ptr, slot_ptr);
|
| 204 |
+
SetCtrl(common, i, ctrl_t::kEmpty, slot_size);
|
| 205 |
+
} else {
|
| 206 |
+
assert(IsDeleted(ctrl[new_i]));
|
| 207 |
+
SetCtrl(common, new_i, H2(hash), slot_size);
|
| 208 |
+
// Until we are done rehashing, DELETED marks previously FULL slots.
|
| 209 |
+
|
| 210 |
+
// Swap i and new_i elements.
|
| 211 |
+
(*transfer)(set, tmp_space, new_slot_ptr);
|
| 212 |
+
(*transfer)(set, new_slot_ptr, slot_ptr);
|
| 213 |
+
(*transfer)(set, slot_ptr, tmp_space);
|
| 214 |
+
|
| 215 |
+
// repeat the processing of the ith slot
|
| 216 |
+
--i;
|
| 217 |
+
slot_ptr = PrevSlot(slot_ptr, slot_size);
|
| 218 |
+
}
|
| 219 |
+
}
|
| 220 |
+
ResetGrowthLeft(common);
|
| 221 |
+
common.infoz().RecordRehash(total_probe_length);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
static bool WasNeverFull(CommonFields& c, size_t index) {
|
| 225 |
+
if (is_single_group(c.capacity())) {
|
| 226 |
+
return true;
|
| 227 |
+
}
|
| 228 |
+
const size_t index_before = (index - Group::kWidth) & c.capacity();
|
| 229 |
+
const auto empty_after = Group(c.control() + index).MaskEmpty();
|
| 230 |
+
const auto empty_before = Group(c.control() + index_before).MaskEmpty();
|
| 231 |
+
|
| 232 |
+
// We count how many consecutive non empties we have to the right and to the
|
| 233 |
+
// left of `it`. If the sum is >= kWidth then there is at least one probe
|
| 234 |
+
// window that might have seen a full group.
|
| 235 |
+
return empty_before && empty_after &&
|
| 236 |
+
static_cast<size_t>(empty_after.TrailingZeros()) +
|
| 237 |
+
empty_before.LeadingZeros() <
|
| 238 |
+
Group::kWidth;
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
|
| 242 |
+
assert(IsFull(c.control()[index]) && "erasing a dangling iterator");
|
| 243 |
+
c.decrement_size();
|
| 244 |
+
c.infoz().RecordErase();
|
| 245 |
+
|
| 246 |
+
if (WasNeverFull(c, index)) {
|
| 247 |
+
SetCtrl(c, index, ctrl_t::kEmpty, slot_size);
|
| 248 |
+
c.set_growth_left(c.growth_left() + 1);
|
| 249 |
+
return;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
SetCtrl(c, index, ctrl_t::kDeleted, slot_size);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
|
| 256 |
+
bool reuse) {
|
| 257 |
+
c.set_size(0);
|
| 258 |
+
if (reuse) {
|
| 259 |
+
ResetCtrl(c, policy.slot_size);
|
| 260 |
+
ResetGrowthLeft(c);
|
| 261 |
+
c.infoz().RecordStorageChanged(0, c.capacity());
|
| 262 |
+
} else {
|
| 263 |
+
// We need to record infoz before calling dealloc, which will unregister
|
| 264 |
+
// infoz.
|
| 265 |
+
c.infoz().RecordClearedReservation();
|
| 266 |
+
c.infoz().RecordStorageChanged(0, 0);
|
| 267 |
+
(*policy.dealloc)(c, policy);
|
| 268 |
+
c.set_control(EmptyGroup());
|
| 269 |
+
c.set_generation_ptr(EmptyGeneration());
|
| 270 |
+
c.set_slots(nullptr);
|
| 271 |
+
c.set_capacity(0);
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
void HashSetResizeHelper::GrowIntoSingleGroupShuffleControlBytes(
|
| 276 |
+
ctrl_t* new_ctrl, size_t new_capacity) const {
|
| 277 |
+
assert(is_single_group(new_capacity));
|
| 278 |
+
constexpr size_t kHalfWidth = Group::kWidth / 2;
|
| 279 |
+
assert(old_capacity_ < kHalfWidth);
|
| 280 |
+
|
| 281 |
+
const size_t half_old_capacity = old_capacity_ / 2;
|
| 282 |
+
|
| 283 |
+
// NOTE: operations are done with compile time known size = kHalfWidth.
|
| 284 |
+
// Compiler optimizes that into single ASM operation.
|
| 285 |
+
|
| 286 |
+
// Copy second half of bytes to the beginning.
|
| 287 |
+
// We potentially copy more bytes in order to have compile time known size.
|
| 288 |
+
// Mirrored bytes from the old_ctrl_ will also be copied.
|
| 289 |
+
// In case of old_capacity_ == 3, we will copy 1st element twice.
|
| 290 |
+
// Examples:
|
| 291 |
+
// old_ctrl = 0S0EEEEEEE...
|
| 292 |
+
// new_ctrl = S0EEEEEEEE...
|
| 293 |
+
//
|
| 294 |
+
// old_ctrl = 01S01EEEEE...
|
| 295 |
+
// new_ctrl = 1S01EEEEEE...
|
| 296 |
+
//
|
| 297 |
+
// old_ctrl = 0123456S0123456EE...
|
| 298 |
+
// new_ctrl = 456S0123?????????...
|
| 299 |
+
std::memcpy(new_ctrl, old_ctrl_ + half_old_capacity + 1, kHalfWidth);
|
| 300 |
+
// Clean up copied kSentinel from old_ctrl.
|
| 301 |
+
new_ctrl[half_old_capacity] = ctrl_t::kEmpty;
|
| 302 |
+
|
| 303 |
+
// Clean up damaged or uninitialized bytes.
|
| 304 |
+
|
| 305 |
+
// Clean bytes after the intended size of the copy.
|
| 306 |
+
// Example:
|
| 307 |
+
// new_ctrl = 1E01EEEEEEE????
|
| 308 |
+
// *new_ctrl= 1E0EEEEEEEE????
|
| 309 |
+
// position /
|
| 310 |
+
std::memset(new_ctrl + old_capacity_ + 1, static_cast<int8_t>(ctrl_t::kEmpty),
|
| 311 |
+
kHalfWidth);
|
| 312 |
+
// Clean non-mirrored bytes that are not initialized.
|
| 313 |
+
// For small old_capacity that may be inside of mirrored bytes zone.
|
| 314 |
+
// Examples:
|
| 315 |
+
// new_ctrl = 1E0EEEEEEEE??????????....
|
| 316 |
+
// *new_ctrl= 1E0EEEEEEEEEEEEE?????....
|
| 317 |
+
// position /
|
| 318 |
+
//
|
| 319 |
+
// new_ctrl = 456E0123???????????...
|
| 320 |
+
// *new_ctrl= 456E0123EEEEEEEE???...
|
| 321 |
+
// position /
|
| 322 |
+
std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty),
|
| 323 |
+
kHalfWidth);
|
| 324 |
+
// Clean last mirrored bytes that are not initialized
|
| 325 |
+
// and will not be overwritten by mirroring.
|
| 326 |
+
// Examples:
|
| 327 |
+
// new_ctrl = 1E0EEEEEEEEEEEEE????????
|
| 328 |
+
// *new_ctrl= 1E0EEEEEEEEEEEEEEEEEEEEE
|
| 329 |
+
// position S /
|
| 330 |
+
//
|
| 331 |
+
// new_ctrl = 456E0123EEEEEEEE???????????????
|
| 332 |
+
// *new_ctrl= 456E0123EEEEEEEE???????EEEEEEEE
|
| 333 |
+
// position S /
|
| 334 |
+
std::memset(new_ctrl + new_capacity + kHalfWidth,
|
| 335 |
+
static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth);
|
| 336 |
+
|
| 337 |
+
// Create mirrored bytes. old_capacity_ < kHalfWidth
|
| 338 |
+
// Example:
|
| 339 |
+
// new_ctrl = 456E0123EEEEEEEE???????EEEEEEEE
|
| 340 |
+
// *new_ctrl= 456E0123EEEEEEEE456E0123EEEEEEE
|
| 341 |
+
// position S/
|
| 342 |
+
ctrl_t g[kHalfWidth];
|
| 343 |
+
std::memcpy(g, new_ctrl, kHalfWidth);
|
| 344 |
+
std::memcpy(new_ctrl + new_capacity + 1, g, kHalfWidth);
|
| 345 |
+
|
| 346 |
+
// Finally set sentinel to its place.
|
| 347 |
+
new_ctrl[new_capacity] = ctrl_t::kSentinel;
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
void HashSetResizeHelper::GrowIntoSingleGroupShuffleTransferableSlots(
|
| 351 |
+
void* old_slots, void* new_slots, size_t slot_size) const {
|
| 352 |
+
assert(old_capacity_ > 0);
|
| 353 |
+
const size_t half_old_capacity = old_capacity_ / 2;
|
| 354 |
+
|
| 355 |
+
SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_);
|
| 356 |
+
std::memcpy(new_slots,
|
| 357 |
+
SlotAddress(old_slots, half_old_capacity + 1, slot_size),
|
| 358 |
+
slot_size * half_old_capacity);
|
| 359 |
+
std::memcpy(SlotAddress(new_slots, half_old_capacity + 1, slot_size),
|
| 360 |
+
old_slots, slot_size * (half_old_capacity + 1));
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
void HashSetResizeHelper::GrowSizeIntoSingleGroupTransferable(
|
| 364 |
+
CommonFields& c, void* old_slots, size_t slot_size) {
|
| 365 |
+
assert(old_capacity_ < Group::kWidth / 2);
|
| 366 |
+
assert(is_single_group(c.capacity()));
|
| 367 |
+
assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
|
| 368 |
+
|
| 369 |
+
GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity());
|
| 370 |
+
GrowIntoSingleGroupShuffleTransferableSlots(old_slots, c.slot_array(),
|
| 371 |
+
slot_size);
|
| 372 |
+
|
| 373 |
+
// We poison since GrowIntoSingleGroupShuffleTransferableSlots
|
| 374 |
+
// may leave empty slots unpoisoned.
|
| 375 |
+
PoisonSingleGroupEmptySlots(c, slot_size);
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
} // namespace container_internal
|
| 379 |
+
ABSL_NAMESPACE_END
|
| 380 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set.h
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
weight/_dep/abseil-cpp/absl/container/internal/raw_hash_set_test.cc
ADDED
|
@@ -0,0 +1,2684 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/container/internal/raw_hash_set.h"
|
| 16 |
+
|
| 17 |
+
#include <algorithm>
|
| 18 |
+
#include <atomic>
|
| 19 |
+
#include <cmath>
|
| 20 |
+
#include <cstddef>
|
| 21 |
+
#include <cstdint>
|
| 22 |
+
#include <deque>
|
| 23 |
+
#include <functional>
|
| 24 |
+
#include <iostream>
|
| 25 |
+
#include <iterator>
|
| 26 |
+
#include <list>
|
| 27 |
+
#include <map>
|
| 28 |
+
#include <memory>
|
| 29 |
+
#include <numeric>
|
| 30 |
+
#include <ostream>
|
| 31 |
+
#include <random>
|
| 32 |
+
#include <string>
|
| 33 |
+
#include <tuple>
|
| 34 |
+
#include <type_traits>
|
| 35 |
+
#include <unordered_map>
|
| 36 |
+
#include <unordered_set>
|
| 37 |
+
#include <utility>
|
| 38 |
+
#include <vector>
|
| 39 |
+
|
| 40 |
+
#include "gmock/gmock.h"
|
| 41 |
+
#include "gtest/gtest.h"
|
| 42 |
+
#include "absl/base/attributes.h"
|
| 43 |
+
#include "absl/base/config.h"
|
| 44 |
+
#include "absl/base/internal/cycleclock.h"
|
| 45 |
+
#include "absl/base/prefetch.h"
|
| 46 |
+
#include "absl/container/flat_hash_map.h"
|
| 47 |
+
#include "absl/container/flat_hash_set.h"
|
| 48 |
+
#include "absl/container/internal/container_memory.h"
|
| 49 |
+
#include "absl/container/internal/hash_function_defaults.h"
|
| 50 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 51 |
+
#include "absl/container/internal/hashtable_debug.h"
|
| 52 |
+
#include "absl/container/internal/hashtablez_sampler.h"
|
| 53 |
+
#include "absl/container/internal/test_allocator.h"
|
| 54 |
+
#include "absl/hash/hash.h"
|
| 55 |
+
#include "absl/log/log.h"
|
| 56 |
+
#include "absl/memory/memory.h"
|
| 57 |
+
#include "absl/meta/type_traits.h"
|
| 58 |
+
#include "absl/strings/string_view.h"
|
| 59 |
+
|
| 60 |
+
namespace absl {
|
| 61 |
+
ABSL_NAMESPACE_BEGIN
|
| 62 |
+
namespace container_internal {
|
| 63 |
+
|
| 64 |
+
struct RawHashSetTestOnlyAccess {
|
| 65 |
+
template <typename C>
|
| 66 |
+
static auto GetSlots(const C& c) -> decltype(c.slot_array()) {
|
| 67 |
+
return c.slot_array();
|
| 68 |
+
}
|
| 69 |
+
template <typename C>
|
| 70 |
+
static size_t CountTombstones(const C& c) {
|
| 71 |
+
return c.common().TombstonesCount();
|
| 72 |
+
}
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
namespace {
|
| 76 |
+
|
| 77 |
+
using ::testing::ElementsAre;
|
| 78 |
+
using ::testing::Eq;
|
| 79 |
+
using ::testing::Ge;
|
| 80 |
+
using ::testing::Lt;
|
| 81 |
+
using ::testing::Pair;
|
| 82 |
+
using ::testing::UnorderedElementsAre;
|
| 83 |
+
|
| 84 |
+
// Convenience function to static cast to ctrl_t.
|
| 85 |
+
ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); }
|
| 86 |
+
|
| 87 |
+
TEST(Util, NormalizeCapacity) {
|
| 88 |
+
EXPECT_EQ(1, NormalizeCapacity(0));
|
| 89 |
+
EXPECT_EQ(1, NormalizeCapacity(1));
|
| 90 |
+
EXPECT_EQ(3, NormalizeCapacity(2));
|
| 91 |
+
EXPECT_EQ(3, NormalizeCapacity(3));
|
| 92 |
+
EXPECT_EQ(7, NormalizeCapacity(4));
|
| 93 |
+
EXPECT_EQ(7, NormalizeCapacity(7));
|
| 94 |
+
EXPECT_EQ(15, NormalizeCapacity(8));
|
| 95 |
+
EXPECT_EQ(15, NormalizeCapacity(15));
|
| 96 |
+
EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1));
|
| 97 |
+
EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2));
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
TEST(Util, GrowthAndCapacity) {
|
| 101 |
+
// Verify that GrowthToCapacity gives the minimum capacity that has enough
|
| 102 |
+
// growth.
|
| 103 |
+
for (size_t growth = 0; growth < 10000; ++growth) {
|
| 104 |
+
SCOPED_TRACE(growth);
|
| 105 |
+
size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
|
| 106 |
+
// The capacity is large enough for `growth`.
|
| 107 |
+
EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
|
| 108 |
+
// For (capacity+1) < kWidth, growth should equal capacity.
|
| 109 |
+
if (capacity + 1 < Group::kWidth) {
|
| 110 |
+
EXPECT_THAT(CapacityToGrowth(capacity), Eq(capacity));
|
| 111 |
+
} else {
|
| 112 |
+
EXPECT_THAT(CapacityToGrowth(capacity), Lt(capacity));
|
| 113 |
+
}
|
| 114 |
+
if (growth != 0 && capacity > 1) {
|
| 115 |
+
// There is no smaller capacity that works.
|
| 116 |
+
EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
for (size_t capacity = Group::kWidth - 1; capacity < 10000;
|
| 121 |
+
capacity = 2 * capacity + 1) {
|
| 122 |
+
SCOPED_TRACE(capacity);
|
| 123 |
+
size_t growth = CapacityToGrowth(capacity);
|
| 124 |
+
EXPECT_THAT(growth, Lt(capacity));
|
| 125 |
+
EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity);
|
| 126 |
+
EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity);
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
TEST(Util, probe_seq) {
|
| 131 |
+
probe_seq<16> seq(0, 127);
|
| 132 |
+
auto gen = [&]() {
|
| 133 |
+
size_t res = seq.offset();
|
| 134 |
+
seq.next();
|
| 135 |
+
return res;
|
| 136 |
+
};
|
| 137 |
+
std::vector<size_t> offsets(8);
|
| 138 |
+
std::generate_n(offsets.begin(), 8, gen);
|
| 139 |
+
EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
|
| 140 |
+
seq = probe_seq<16>(128, 127);
|
| 141 |
+
std::generate_n(offsets.begin(), 8, gen);
|
| 142 |
+
EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
TEST(BitMask, Smoke) {
|
| 146 |
+
EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
|
| 147 |
+
EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
|
| 148 |
+
|
| 149 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
|
| 150 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
|
| 151 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
|
| 152 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
|
| 153 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
|
| 154 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
|
| 155 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
|
| 156 |
+
EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
TEST(BitMask, WithShift) {
|
| 160 |
+
// See the non-SSE version of Group for details on what this math is for.
|
| 161 |
+
uint64_t ctrl = 0x1716151413121110;
|
| 162 |
+
uint64_t hash = 0x12;
|
| 163 |
+
constexpr uint64_t msbs = 0x8080808080808080ULL;
|
| 164 |
+
constexpr uint64_t lsbs = 0x0101010101010101ULL;
|
| 165 |
+
auto x = ctrl ^ (lsbs * hash);
|
| 166 |
+
uint64_t mask = (x - lsbs) & ~x & msbs;
|
| 167 |
+
EXPECT_EQ(0x0000000080800000, mask);
|
| 168 |
+
|
| 169 |
+
BitMask<uint64_t, 8, 3> b(mask);
|
| 170 |
+
EXPECT_EQ(*b, 2);
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
TEST(BitMask, LeadingTrailing) {
|
| 174 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
|
| 175 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
|
| 176 |
+
|
| 177 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
|
| 178 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
|
| 179 |
+
|
| 180 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
|
| 181 |
+
EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
|
| 182 |
+
|
| 183 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
|
| 184 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
|
| 185 |
+
|
| 186 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
|
| 187 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
|
| 188 |
+
|
| 189 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
|
| 190 |
+
EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
TEST(Group, EmptyGroup) {
|
| 194 |
+
for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
TEST(Group, Match) {
|
| 198 |
+
if (Group::kWidth == 16) {
|
| 199 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
|
| 200 |
+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
|
| 201 |
+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
|
| 202 |
+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
|
| 203 |
+
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
|
| 204 |
+
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
|
| 205 |
+
EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
|
| 206 |
+
EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
|
| 207 |
+
EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
|
| 208 |
+
} else if (Group::kWidth == 8) {
|
| 209 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
|
| 210 |
+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
|
| 211 |
+
ctrl_t::kSentinel, CtrlT(1)};
|
| 212 |
+
EXPECT_THAT(Group{group}.Match(0), ElementsAre());
|
| 213 |
+
EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
|
| 214 |
+
EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
|
| 215 |
+
} else {
|
| 216 |
+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
|
| 217 |
+
}
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
TEST(Group, MaskEmpty) {
|
| 221 |
+
if (Group::kWidth == 16) {
|
| 222 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
|
| 223 |
+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
|
| 224 |
+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
|
| 225 |
+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
|
| 226 |
+
EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
|
| 227 |
+
EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 4);
|
| 228 |
+
} else if (Group::kWidth == 8) {
|
| 229 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
|
| 230 |
+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
|
| 231 |
+
ctrl_t::kSentinel, CtrlT(1)};
|
| 232 |
+
EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
|
| 233 |
+
EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 0);
|
| 234 |
+
} else {
|
| 235 |
+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
TEST(Group, MaskFull) {
|
| 240 |
+
if (Group::kWidth == 16) {
|
| 241 |
+
ctrl_t group[] = {
|
| 242 |
+
ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
|
| 243 |
+
ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
|
| 244 |
+
CtrlT(7), CtrlT(5), ctrl_t::kDeleted, CtrlT(1),
|
| 245 |
+
CtrlT(1), ctrl_t::kSentinel, ctrl_t::kEmpty, CtrlT(1)};
|
| 246 |
+
EXPECT_THAT(Group{group}.MaskFull(),
|
| 247 |
+
ElementsAre(1, 3, 5, 7, 8, 9, 11, 12, 15));
|
| 248 |
+
} else if (Group::kWidth == 8) {
|
| 249 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty,
|
| 250 |
+
ctrl_t::kDeleted, CtrlT(2), ctrl_t::kSentinel,
|
| 251 |
+
ctrl_t::kSentinel, CtrlT(1)};
|
| 252 |
+
EXPECT_THAT(Group{group}.MaskFull(), ElementsAre(1, 4, 7));
|
| 253 |
+
} else {
|
| 254 |
+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
TEST(Group, MaskEmptyOrDeleted) {
|
| 259 |
+
if (Group::kWidth == 16) {
|
| 260 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty, CtrlT(3),
|
| 261 |
+
ctrl_t::kDeleted, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
|
| 262 |
+
CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
|
| 263 |
+
CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
|
| 264 |
+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
|
| 265 |
+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 4);
|
| 266 |
+
} else if (Group::kWidth == 8) {
|
| 267 |
+
ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
|
| 268 |
+
ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
|
| 269 |
+
ctrl_t::kSentinel, CtrlT(1)};
|
| 270 |
+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
|
| 271 |
+
EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 3);
|
| 272 |
+
} else {
|
| 273 |
+
FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
TEST(Batch, DropDeletes) {
|
| 278 |
+
constexpr size_t kCapacity = 63;
|
| 279 |
+
constexpr size_t kGroupWidth = container_internal::Group::kWidth;
|
| 280 |
+
std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
|
| 281 |
+
ctrl[kCapacity] = ctrl_t::kSentinel;
|
| 282 |
+
std::vector<ctrl_t> pattern = {
|
| 283 |
+
ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2),
|
| 284 |
+
ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted};
|
| 285 |
+
for (size_t i = 0; i != kCapacity; ++i) {
|
| 286 |
+
ctrl[i] = pattern[i % pattern.size()];
|
| 287 |
+
if (i < kGroupWidth - 1)
|
| 288 |
+
ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
|
| 289 |
+
}
|
| 290 |
+
ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
|
| 291 |
+
ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel);
|
| 292 |
+
for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) {
|
| 293 |
+
ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
|
| 294 |
+
if (i == kCapacity) expected = ctrl_t::kSentinel;
|
| 295 |
+
if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty;
|
| 296 |
+
if (IsFull(expected)) expected = ctrl_t::kDeleted;
|
| 297 |
+
EXPECT_EQ(ctrl[i], expected)
|
| 298 |
+
<< i << " " << static_cast<int>(pattern[i % pattern.size()]);
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
TEST(Group, CountLeadingEmptyOrDeleted) {
|
| 303 |
+
const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted};
|
| 304 |
+
const std::vector<ctrl_t> full_examples = {
|
| 305 |
+
CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3),
|
| 306 |
+
CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel};
|
| 307 |
+
|
| 308 |
+
for (ctrl_t empty : empty_examples) {
|
| 309 |
+
std::vector<ctrl_t> e(Group::kWidth, empty);
|
| 310 |
+
EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
|
| 311 |
+
for (ctrl_t full : full_examples) {
|
| 312 |
+
for (size_t i = 0; i != Group::kWidth; ++i) {
|
| 313 |
+
std::vector<ctrl_t> f(Group::kWidth, empty);
|
| 314 |
+
f[i] = full;
|
| 315 |
+
EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
|
| 316 |
+
}
|
| 317 |
+
std::vector<ctrl_t> f(Group::kWidth, empty);
|
| 318 |
+
f[Group::kWidth * 2 / 3] = full;
|
| 319 |
+
f[Group::kWidth / 2] = full;
|
| 320 |
+
EXPECT_EQ(Group::kWidth / 2,
|
| 321 |
+
Group{f.data()}.CountLeadingEmptyOrDeleted());
|
| 322 |
+
}
|
| 323 |
+
}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
template <class T, bool kTransferable = false>
|
| 327 |
+
struct ValuePolicy {
|
| 328 |
+
using slot_type = T;
|
| 329 |
+
using key_type = T;
|
| 330 |
+
using init_type = T;
|
| 331 |
+
|
| 332 |
+
template <class Allocator, class... Args>
|
| 333 |
+
static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
|
| 334 |
+
absl::allocator_traits<Allocator>::construct(*alloc, slot,
|
| 335 |
+
std::forward<Args>(args)...);
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
template <class Allocator>
|
| 339 |
+
static void destroy(Allocator* alloc, slot_type* slot) {
|
| 340 |
+
absl::allocator_traits<Allocator>::destroy(*alloc, slot);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
template <class Allocator>
|
| 344 |
+
static std::integral_constant<bool, kTransferable> transfer(
|
| 345 |
+
Allocator* alloc, slot_type* new_slot, slot_type* old_slot) {
|
| 346 |
+
construct(alloc, new_slot, std::move(*old_slot));
|
| 347 |
+
destroy(alloc, old_slot);
|
| 348 |
+
return {};
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
static T& element(slot_type* slot) { return *slot; }
|
| 352 |
+
|
| 353 |
+
template <class F, class... Args>
|
| 354 |
+
static decltype(absl::container_internal::DecomposeValue(
|
| 355 |
+
std::declval<F>(), std::declval<Args>()...))
|
| 356 |
+
apply(F&& f, Args&&... args) {
|
| 357 |
+
return absl::container_internal::DecomposeValue(
|
| 358 |
+
std::forward<F>(f), std::forward<Args>(args)...);
|
| 359 |
+
}
|
| 360 |
+
};
|
| 361 |
+
|
| 362 |
+
using IntPolicy = ValuePolicy<int64_t>;
|
| 363 |
+
using Uint8Policy = ValuePolicy<uint8_t>;
|
| 364 |
+
|
| 365 |
+
using TranferableIntPolicy = ValuePolicy<int64_t, /*kTransferable=*/true>;
|
| 366 |
+
|
| 367 |
+
class StringPolicy {
|
| 368 |
+
template <class F, class K, class V,
|
| 369 |
+
class = typename std::enable_if<
|
| 370 |
+
std::is_convertible<const K&, absl::string_view>::value>::type>
|
| 371 |
+
decltype(std::declval<F>()(
|
| 372 |
+
std::declval<const absl::string_view&>(), std::piecewise_construct,
|
| 373 |
+
std::declval<std::tuple<K>>(),
|
| 374 |
+
std::declval<V>())) static apply_impl(F&& f,
|
| 375 |
+
std::pair<std::tuple<K>, V> p) {
|
| 376 |
+
const absl::string_view& key = std::get<0>(p.first);
|
| 377 |
+
return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
|
| 378 |
+
std::move(p.second));
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
public:
|
| 382 |
+
struct slot_type {
|
| 383 |
+
struct ctor {};
|
| 384 |
+
|
| 385 |
+
template <class... Ts>
|
| 386 |
+
explicit slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
|
| 387 |
+
|
| 388 |
+
std::pair<std::string, std::string> pair;
|
| 389 |
+
};
|
| 390 |
+
|
| 391 |
+
using key_type = std::string;
|
| 392 |
+
using init_type = std::pair<std::string, std::string>;
|
| 393 |
+
|
| 394 |
+
template <class allocator_type, class... Args>
|
| 395 |
+
static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
|
| 396 |
+
std::allocator_traits<allocator_type>::construct(
|
| 397 |
+
*alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
template <class allocator_type>
|
| 401 |
+
static void destroy(allocator_type* alloc, slot_type* slot) {
|
| 402 |
+
std::allocator_traits<allocator_type>::destroy(*alloc, slot);
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
template <class allocator_type>
|
| 406 |
+
static void transfer(allocator_type* alloc, slot_type* new_slot,
|
| 407 |
+
slot_type* old_slot) {
|
| 408 |
+
construct(alloc, new_slot, std::move(old_slot->pair));
|
| 409 |
+
destroy(alloc, old_slot);
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
static std::pair<std::string, std::string>& element(slot_type* slot) {
|
| 413 |
+
return slot->pair;
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
template <class F, class... Args>
|
| 417 |
+
static auto apply(F&& f, Args&&... args)
|
| 418 |
+
-> decltype(apply_impl(std::forward<F>(f),
|
| 419 |
+
PairArgs(std::forward<Args>(args)...))) {
|
| 420 |
+
return apply_impl(std::forward<F>(f),
|
| 421 |
+
PairArgs(std::forward<Args>(args)...));
|
| 422 |
+
}
|
| 423 |
+
};
|
| 424 |
+
|
| 425 |
+
struct StringHash : absl::Hash<absl::string_view> {
|
| 426 |
+
using is_transparent = void;
|
| 427 |
+
};
|
| 428 |
+
struct StringEq : std::equal_to<absl::string_view> {
|
| 429 |
+
using is_transparent = void;
|
| 430 |
+
};
|
| 431 |
+
|
| 432 |
+
struct StringTable
|
| 433 |
+
: raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
|
| 434 |
+
using Base = typename StringTable::raw_hash_set;
|
| 435 |
+
StringTable() = default;
|
| 436 |
+
using Base::Base;
|
| 437 |
+
};
|
| 438 |
+
|
| 439 |
+
template <typename T, bool kTransferable = false>
|
| 440 |
+
struct ValueTable
|
| 441 |
+
: raw_hash_set<ValuePolicy<T, kTransferable>, hash_default_hash<T>,
|
| 442 |
+
std::equal_to<T>, std::allocator<T>> {
|
| 443 |
+
using Base = typename ValueTable::raw_hash_set;
|
| 444 |
+
using Base::Base;
|
| 445 |
+
};
|
| 446 |
+
|
| 447 |
+
using IntTable = ValueTable<int64_t>;
|
| 448 |
+
using Uint8Table = ValueTable<uint8_t>;
|
| 449 |
+
|
| 450 |
+
using TransferableIntTable = ValueTable<int64_t, /*kTransferable=*/true>;
|
| 451 |
+
|
| 452 |
+
template <typename T>
|
| 453 |
+
struct CustomAlloc : std::allocator<T> {
|
| 454 |
+
CustomAlloc() = default;
|
| 455 |
+
|
| 456 |
+
template <typename U>
|
| 457 |
+
explicit CustomAlloc(const CustomAlloc<U>& /*other*/) {}
|
| 458 |
+
|
| 459 |
+
template <class U>
|
| 460 |
+
struct rebind {
|
| 461 |
+
using other = CustomAlloc<U>;
|
| 462 |
+
};
|
| 463 |
+
};
|
| 464 |
+
|
| 465 |
+
struct CustomAllocIntTable
|
| 466 |
+
: raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
|
| 467 |
+
std::equal_to<int64_t>, CustomAlloc<int64_t>> {
|
| 468 |
+
using Base = typename CustomAllocIntTable::raw_hash_set;
|
| 469 |
+
using Base::Base;
|
| 470 |
+
};
|
| 471 |
+
|
| 472 |
+
struct MinimumAlignmentUint8Table
|
| 473 |
+
: raw_hash_set<Uint8Policy, hash_default_hash<uint8_t>,
|
| 474 |
+
std::equal_to<uint8_t>, MinimumAlignmentAlloc<uint8_t>> {
|
| 475 |
+
using Base = typename MinimumAlignmentUint8Table::raw_hash_set;
|
| 476 |
+
using Base::Base;
|
| 477 |
+
};
|
| 478 |
+
|
| 479 |
+
// Allows for freezing the allocator to expect no further allocations.
|
| 480 |
+
template <typename T>
|
| 481 |
+
struct FreezableAlloc : std::allocator<T> {
|
| 482 |
+
explicit FreezableAlloc(bool* f) : frozen(f) {}
|
| 483 |
+
|
| 484 |
+
template <typename U>
|
| 485 |
+
explicit FreezableAlloc(const FreezableAlloc<U>& other)
|
| 486 |
+
: frozen(other.frozen) {}
|
| 487 |
+
|
| 488 |
+
template <class U>
|
| 489 |
+
struct rebind {
|
| 490 |
+
using other = FreezableAlloc<U>;
|
| 491 |
+
};
|
| 492 |
+
|
| 493 |
+
T* allocate(size_t n) {
|
| 494 |
+
EXPECT_FALSE(*frozen);
|
| 495 |
+
return std::allocator<T>::allocate(n);
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
bool* frozen;
|
| 499 |
+
};
|
| 500 |
+
|
| 501 |
+
struct BadFastHash {
|
| 502 |
+
template <class T>
|
| 503 |
+
size_t operator()(const T&) const {
|
| 504 |
+
return 0;
|
| 505 |
+
}
|
| 506 |
+
};
|
| 507 |
+
|
| 508 |
+
struct BadHashFreezableIntTable
|
| 509 |
+
: raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int64_t>,
|
| 510 |
+
FreezableAlloc<int64_t>> {
|
| 511 |
+
using Base = typename BadHashFreezableIntTable::raw_hash_set;
|
| 512 |
+
using Base::Base;
|
| 513 |
+
};
|
| 514 |
+
|
| 515 |
+
struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
|
| 516 |
+
std::allocator<int>> {
|
| 517 |
+
using Base = typename BadTable::raw_hash_set;
|
| 518 |
+
BadTable() = default;
|
| 519 |
+
using Base::Base;
|
| 520 |
+
};
|
| 521 |
+
|
| 522 |
+
TEST(Table, EmptyFunctorOptimization) {
|
| 523 |
+
static_assert(std::is_empty<std::equal_to<absl::string_view>>::value, "");
|
| 524 |
+
static_assert(std::is_empty<std::allocator<int>>::value, "");
|
| 525 |
+
|
| 526 |
+
struct MockTable {
|
| 527 |
+
void* ctrl;
|
| 528 |
+
void* slots;
|
| 529 |
+
size_t size;
|
| 530 |
+
size_t capacity;
|
| 531 |
+
};
|
| 532 |
+
struct StatelessHash {
|
| 533 |
+
size_t operator()(absl::string_view) const { return 0; }
|
| 534 |
+
};
|
| 535 |
+
struct StatefulHash : StatelessHash {
|
| 536 |
+
size_t dummy;
|
| 537 |
+
};
|
| 538 |
+
|
| 539 |
+
struct GenerationData {
|
| 540 |
+
size_t reserved_growth;
|
| 541 |
+
size_t reservation_size;
|
| 542 |
+
GenerationType* generation;
|
| 543 |
+
};
|
| 544 |
+
|
| 545 |
+
// Ignore unreachable-code warning. Compiler thinks one branch of each ternary
|
| 546 |
+
// conditional is unreachable.
|
| 547 |
+
#if defined(__clang__)
|
| 548 |
+
#pragma clang diagnostic push
|
| 549 |
+
#pragma clang diagnostic ignored "-Wunreachable-code"
|
| 550 |
+
#endif
|
| 551 |
+
constexpr size_t mock_size = sizeof(MockTable);
|
| 552 |
+
constexpr size_t generation_size =
|
| 553 |
+
SwisstableGenerationsEnabled() ? sizeof(GenerationData) : 0;
|
| 554 |
+
#if defined(__clang__)
|
| 555 |
+
#pragma clang diagnostic pop
|
| 556 |
+
#endif
|
| 557 |
+
|
| 558 |
+
EXPECT_EQ(
|
| 559 |
+
mock_size + generation_size,
|
| 560 |
+
sizeof(
|
| 561 |
+
raw_hash_set<StringPolicy, StatelessHash,
|
| 562 |
+
std::equal_to<absl::string_view>, std::allocator<int>>));
|
| 563 |
+
|
| 564 |
+
EXPECT_EQ(
|
| 565 |
+
mock_size + sizeof(StatefulHash) + generation_size,
|
| 566 |
+
sizeof(
|
| 567 |
+
raw_hash_set<StringPolicy, StatefulHash,
|
| 568 |
+
std::equal_to<absl::string_view>, std::allocator<int>>));
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
TEST(Table, Empty) {
|
| 572 |
+
IntTable t;
|
| 573 |
+
EXPECT_EQ(0, t.size());
|
| 574 |
+
EXPECT_TRUE(t.empty());
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
TEST(Table, LookupEmpty) {
|
| 578 |
+
IntTable t;
|
| 579 |
+
auto it = t.find(0);
|
| 580 |
+
EXPECT_TRUE(it == t.end());
|
| 581 |
+
}
|
| 582 |
+
|
| 583 |
+
TEST(Table, Insert1) {
|
| 584 |
+
IntTable t;
|
| 585 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 586 |
+
auto res = t.emplace(0);
|
| 587 |
+
EXPECT_TRUE(res.second);
|
| 588 |
+
EXPECT_THAT(*res.first, 0);
|
| 589 |
+
EXPECT_EQ(1, t.size());
|
| 590 |
+
EXPECT_THAT(*t.find(0), 0);
|
| 591 |
+
}
|
| 592 |
+
|
| 593 |
+
TEST(Table, Insert2) {
|
| 594 |
+
IntTable t;
|
| 595 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 596 |
+
auto res = t.emplace(0);
|
| 597 |
+
EXPECT_TRUE(res.second);
|
| 598 |
+
EXPECT_THAT(*res.first, 0);
|
| 599 |
+
EXPECT_EQ(1, t.size());
|
| 600 |
+
EXPECT_TRUE(t.find(1) == t.end());
|
| 601 |
+
res = t.emplace(1);
|
| 602 |
+
EXPECT_TRUE(res.second);
|
| 603 |
+
EXPECT_THAT(*res.first, 1);
|
| 604 |
+
EXPECT_EQ(2, t.size());
|
| 605 |
+
EXPECT_THAT(*t.find(0), 0);
|
| 606 |
+
EXPECT_THAT(*t.find(1), 1);
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
TEST(Table, InsertCollision) {
|
| 610 |
+
BadTable t;
|
| 611 |
+
EXPECT_TRUE(t.find(1) == t.end());
|
| 612 |
+
auto res = t.emplace(1);
|
| 613 |
+
EXPECT_TRUE(res.second);
|
| 614 |
+
EXPECT_THAT(*res.first, 1);
|
| 615 |
+
EXPECT_EQ(1, t.size());
|
| 616 |
+
|
| 617 |
+
EXPECT_TRUE(t.find(2) == t.end());
|
| 618 |
+
res = t.emplace(2);
|
| 619 |
+
EXPECT_THAT(*res.first, 2);
|
| 620 |
+
EXPECT_TRUE(res.second);
|
| 621 |
+
EXPECT_EQ(2, t.size());
|
| 622 |
+
|
| 623 |
+
EXPECT_THAT(*t.find(1), 1);
|
| 624 |
+
EXPECT_THAT(*t.find(2), 2);
|
| 625 |
+
}
|
| 626 |
+
|
| 627 |
+
// Test that we do not add existent element in case we need to search through
|
| 628 |
+
// many groups with deleted elements
|
| 629 |
+
TEST(Table, InsertCollisionAndFindAfterDelete) {
|
| 630 |
+
BadTable t; // all elements go to the same group.
|
| 631 |
+
// Have at least 2 groups with Group::kWidth collisions
|
| 632 |
+
// plus some extra collisions in the last group.
|
| 633 |
+
constexpr size_t kNumInserts = Group::kWidth * 2 + 5;
|
| 634 |
+
for (size_t i = 0; i < kNumInserts; ++i) {
|
| 635 |
+
auto res = t.emplace(i);
|
| 636 |
+
EXPECT_TRUE(res.second);
|
| 637 |
+
EXPECT_THAT(*res.first, i);
|
| 638 |
+
EXPECT_EQ(i + 1, t.size());
|
| 639 |
+
}
|
| 640 |
+
|
| 641 |
+
// Remove elements one by one and check
|
| 642 |
+
// that we still can find all other elements.
|
| 643 |
+
for (size_t i = 0; i < kNumInserts; ++i) {
|
| 644 |
+
EXPECT_EQ(1, t.erase(i)) << i;
|
| 645 |
+
for (size_t j = i + 1; j < kNumInserts; ++j) {
|
| 646 |
+
EXPECT_THAT(*t.find(j), j);
|
| 647 |
+
auto res = t.emplace(j);
|
| 648 |
+
EXPECT_FALSE(res.second) << i << " " << j;
|
| 649 |
+
EXPECT_THAT(*res.first, j);
|
| 650 |
+
EXPECT_EQ(kNumInserts - i - 1, t.size());
|
| 651 |
+
}
|
| 652 |
+
}
|
| 653 |
+
EXPECT_TRUE(t.empty());
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
TEST(Table, EraseInSmallTables) {
|
| 657 |
+
for (int64_t size = 0; size < 64; ++size) {
|
| 658 |
+
IntTable t;
|
| 659 |
+
for (int64_t i = 0; i < size; ++i) {
|
| 660 |
+
t.insert(i);
|
| 661 |
+
}
|
| 662 |
+
for (int64_t i = 0; i < size; ++i) {
|
| 663 |
+
t.erase(i);
|
| 664 |
+
EXPECT_EQ(t.size(), size - i - 1);
|
| 665 |
+
for (int64_t j = i + 1; j < size; ++j) {
|
| 666 |
+
EXPECT_THAT(*t.find(j), j);
|
| 667 |
+
}
|
| 668 |
+
}
|
| 669 |
+
EXPECT_TRUE(t.empty());
|
| 670 |
+
}
|
| 671 |
+
}
|
| 672 |
+
|
| 673 |
+
TEST(Table, InsertWithinCapacity) {
|
| 674 |
+
IntTable t;
|
| 675 |
+
t.reserve(10);
|
| 676 |
+
const size_t original_capacity = t.capacity();
|
| 677 |
+
const auto addr = [&](int i) {
|
| 678 |
+
return reinterpret_cast<uintptr_t>(&*t.find(i));
|
| 679 |
+
};
|
| 680 |
+
// Inserting an element does not change capacity.
|
| 681 |
+
t.insert(0);
|
| 682 |
+
EXPECT_THAT(t.capacity(), original_capacity);
|
| 683 |
+
const uintptr_t original_addr_0 = addr(0);
|
| 684 |
+
// Inserting another element does not rehash.
|
| 685 |
+
t.insert(1);
|
| 686 |
+
EXPECT_THAT(t.capacity(), original_capacity);
|
| 687 |
+
EXPECT_THAT(addr(0), original_addr_0);
|
| 688 |
+
// Inserting lots of duplicate elements does not rehash.
|
| 689 |
+
for (int i = 0; i < 100; ++i) {
|
| 690 |
+
t.insert(i % 10);
|
| 691 |
+
}
|
| 692 |
+
EXPECT_THAT(t.capacity(), original_capacity);
|
| 693 |
+
EXPECT_THAT(addr(0), original_addr_0);
|
| 694 |
+
// Inserting a range of duplicate elements does not rehash.
|
| 695 |
+
std::vector<int> dup_range;
|
| 696 |
+
for (int i = 0; i < 100; ++i) {
|
| 697 |
+
dup_range.push_back(i % 10);
|
| 698 |
+
}
|
| 699 |
+
t.insert(dup_range.begin(), dup_range.end());
|
| 700 |
+
EXPECT_THAT(t.capacity(), original_capacity);
|
| 701 |
+
EXPECT_THAT(addr(0), original_addr_0);
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
template <class TableType>
|
| 705 |
+
class SmallTableResizeTest : public testing::Test {};
|
| 706 |
+
|
| 707 |
+
TYPED_TEST_SUITE_P(SmallTableResizeTest);
|
| 708 |
+
|
| 709 |
+
TYPED_TEST_P(SmallTableResizeTest, InsertIntoSmallTable) {
|
| 710 |
+
TypeParam t;
|
| 711 |
+
for (int i = 0; i < 32; ++i) {
|
| 712 |
+
t.insert(i);
|
| 713 |
+
ASSERT_EQ(t.size(), i + 1);
|
| 714 |
+
for (int j = 0; j < i + 1; ++j) {
|
| 715 |
+
EXPECT_TRUE(t.find(j) != t.end());
|
| 716 |
+
EXPECT_EQ(*t.find(j), j);
|
| 717 |
+
}
|
| 718 |
+
}
|
| 719 |
+
}
|
| 720 |
+
|
| 721 |
+
TYPED_TEST_P(SmallTableResizeTest, ResizeGrowSmallTables) {
|
| 722 |
+
TypeParam t;
|
| 723 |
+
for (size_t source_size = 0; source_size < 32; ++source_size) {
|
| 724 |
+
for (size_t target_size = source_size; target_size < 32; ++target_size) {
|
| 725 |
+
for (bool rehash : {false, true}) {
|
| 726 |
+
for (size_t i = 0; i < source_size; ++i) {
|
| 727 |
+
t.insert(static_cast<int>(i));
|
| 728 |
+
}
|
| 729 |
+
if (rehash) {
|
| 730 |
+
t.rehash(target_size);
|
| 731 |
+
} else {
|
| 732 |
+
t.reserve(target_size);
|
| 733 |
+
}
|
| 734 |
+
for (size_t i = 0; i < source_size; ++i) {
|
| 735 |
+
EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
|
| 736 |
+
EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
|
| 737 |
+
}
|
| 738 |
+
}
|
| 739 |
+
}
|
| 740 |
+
}
|
| 741 |
+
}
|
| 742 |
+
|
| 743 |
+
TYPED_TEST_P(SmallTableResizeTest, ResizeReduceSmallTables) {
|
| 744 |
+
TypeParam t;
|
| 745 |
+
for (size_t source_size = 0; source_size < 32; ++source_size) {
|
| 746 |
+
for (size_t target_size = 0; target_size <= source_size; ++target_size) {
|
| 747 |
+
size_t inserted_count = std::min<size_t>(source_size, 5);
|
| 748 |
+
for (size_t i = 0; i < inserted_count; ++i) {
|
| 749 |
+
t.insert(static_cast<int>(i));
|
| 750 |
+
}
|
| 751 |
+
t.rehash(target_size);
|
| 752 |
+
for (size_t i = 0; i < inserted_count; ++i) {
|
| 753 |
+
EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end());
|
| 754 |
+
EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i));
|
| 755 |
+
}
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
+
}
|
| 759 |
+
|
| 760 |
+
REGISTER_TYPED_TEST_SUITE_P(SmallTableResizeTest, InsertIntoSmallTable,
|
| 761 |
+
ResizeGrowSmallTables, ResizeReduceSmallTables);
|
| 762 |
+
using SmallTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
|
| 763 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSmallTableResizeTest,
|
| 764 |
+
SmallTableResizeTest, SmallTableTypes);
|
| 765 |
+
|
| 766 |
+
TEST(Table, LazyEmplace) {
|
| 767 |
+
StringTable t;
|
| 768 |
+
bool called = false;
|
| 769 |
+
auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
|
| 770 |
+
called = true;
|
| 771 |
+
f("abc", "ABC");
|
| 772 |
+
});
|
| 773 |
+
EXPECT_TRUE(called);
|
| 774 |
+
EXPECT_THAT(*it, Pair("abc", "ABC"));
|
| 775 |
+
called = false;
|
| 776 |
+
it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
|
| 777 |
+
called = true;
|
| 778 |
+
f("abc", "DEF");
|
| 779 |
+
});
|
| 780 |
+
EXPECT_FALSE(called);
|
| 781 |
+
EXPECT_THAT(*it, Pair("abc", "ABC"));
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
TEST(Table, ContainsEmpty) {
|
| 785 |
+
IntTable t;
|
| 786 |
+
|
| 787 |
+
EXPECT_FALSE(t.contains(0));
|
| 788 |
+
}
|
| 789 |
+
|
| 790 |
+
TEST(Table, Contains1) {
|
| 791 |
+
IntTable t;
|
| 792 |
+
|
| 793 |
+
EXPECT_TRUE(t.insert(0).second);
|
| 794 |
+
EXPECT_TRUE(t.contains(0));
|
| 795 |
+
EXPECT_FALSE(t.contains(1));
|
| 796 |
+
|
| 797 |
+
EXPECT_EQ(1, t.erase(0));
|
| 798 |
+
EXPECT_FALSE(t.contains(0));
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
TEST(Table, Contains2) {
|
| 802 |
+
IntTable t;
|
| 803 |
+
|
| 804 |
+
EXPECT_TRUE(t.insert(0).second);
|
| 805 |
+
EXPECT_TRUE(t.contains(0));
|
| 806 |
+
EXPECT_FALSE(t.contains(1));
|
| 807 |
+
|
| 808 |
+
t.clear();
|
| 809 |
+
EXPECT_FALSE(t.contains(0));
|
| 810 |
+
}
|
| 811 |
+
|
| 812 |
+
int decompose_constructed;
|
| 813 |
+
int decompose_copy_constructed;
|
| 814 |
+
int decompose_copy_assigned;
|
| 815 |
+
int decompose_move_constructed;
|
| 816 |
+
int decompose_move_assigned;
|
| 817 |
+
struct DecomposeType {
|
| 818 |
+
DecomposeType(int i = 0) : i(i) { // NOLINT
|
| 819 |
+
++decompose_constructed;
|
| 820 |
+
}
|
| 821 |
+
|
| 822 |
+
explicit DecomposeType(const char* d) : DecomposeType(*d) {}
|
| 823 |
+
|
| 824 |
+
DecomposeType(const DecomposeType& other) : i(other.i) {
|
| 825 |
+
++decompose_copy_constructed;
|
| 826 |
+
}
|
| 827 |
+
DecomposeType& operator=(const DecomposeType& other) {
|
| 828 |
+
++decompose_copy_assigned;
|
| 829 |
+
i = other.i;
|
| 830 |
+
return *this;
|
| 831 |
+
}
|
| 832 |
+
DecomposeType(DecomposeType&& other) : i(other.i) {
|
| 833 |
+
++decompose_move_constructed;
|
| 834 |
+
}
|
| 835 |
+
DecomposeType& operator=(DecomposeType&& other) {
|
| 836 |
+
++decompose_move_assigned;
|
| 837 |
+
i = other.i;
|
| 838 |
+
return *this;
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
int i;
|
| 842 |
+
};
|
| 843 |
+
|
| 844 |
+
struct DecomposeHash {
|
| 845 |
+
using is_transparent = void;
|
| 846 |
+
size_t operator()(const DecomposeType& a) const { return a.i; }
|
| 847 |
+
size_t operator()(int a) const { return a; }
|
| 848 |
+
size_t operator()(const char* a) const { return *a; }
|
| 849 |
+
};
|
| 850 |
+
|
| 851 |
+
struct DecomposeEq {
|
| 852 |
+
using is_transparent = void;
|
| 853 |
+
bool operator()(const DecomposeType& a, const DecomposeType& b) const {
|
| 854 |
+
return a.i == b.i;
|
| 855 |
+
}
|
| 856 |
+
bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
|
| 857 |
+
bool operator()(const DecomposeType& a, const char* b) const {
|
| 858 |
+
return a.i == *b;
|
| 859 |
+
}
|
| 860 |
+
};
|
| 861 |
+
|
| 862 |
+
struct DecomposePolicy {
|
| 863 |
+
using slot_type = DecomposeType;
|
| 864 |
+
using key_type = DecomposeType;
|
| 865 |
+
using init_type = DecomposeType;
|
| 866 |
+
|
| 867 |
+
template <typename T>
|
| 868 |
+
static void construct(void*, DecomposeType* slot, T&& v) {
|
| 869 |
+
::new (slot) DecomposeType(std::forward<T>(v));
|
| 870 |
+
}
|
| 871 |
+
static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); }
|
| 872 |
+
static DecomposeType& element(slot_type* slot) { return *slot; }
|
| 873 |
+
|
| 874 |
+
template <class F, class T>
|
| 875 |
+
static auto apply(F&& f, const T& x) -> decltype(std::forward<F>(f)(x, x)) {
|
| 876 |
+
return std::forward<F>(f)(x, x);
|
| 877 |
+
}
|
| 878 |
+
};
|
| 879 |
+
|
| 880 |
+
template <typename Hash, typename Eq>
|
| 881 |
+
void TestDecompose(bool construct_three) {
|
| 882 |
+
DecomposeType elem{0};
|
| 883 |
+
const int one = 1;
|
| 884 |
+
const char* three_p = "3";
|
| 885 |
+
const auto& three = three_p;
|
| 886 |
+
const int elem_vector_count = 256;
|
| 887 |
+
std::vector<DecomposeType> elem_vector(elem_vector_count, DecomposeType{0});
|
| 888 |
+
std::iota(elem_vector.begin(), elem_vector.end(), 0);
|
| 889 |
+
|
| 890 |
+
using DecomposeSet =
|
| 891 |
+
raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>>;
|
| 892 |
+
DecomposeSet set1;
|
| 893 |
+
|
| 894 |
+
decompose_constructed = 0;
|
| 895 |
+
int expected_constructed = 0;
|
| 896 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 897 |
+
set1.insert(elem);
|
| 898 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 899 |
+
set1.insert(1);
|
| 900 |
+
EXPECT_EQ(++expected_constructed, decompose_constructed);
|
| 901 |
+
set1.emplace("3");
|
| 902 |
+
EXPECT_EQ(++expected_constructed, decompose_constructed);
|
| 903 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 904 |
+
|
| 905 |
+
{ // insert(T&&)
|
| 906 |
+
set1.insert(1);
|
| 907 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 908 |
+
}
|
| 909 |
+
|
| 910 |
+
{ // insert(const T&)
|
| 911 |
+
set1.insert(one);
|
| 912 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 913 |
+
}
|
| 914 |
+
|
| 915 |
+
{ // insert(hint, T&&)
|
| 916 |
+
set1.insert(set1.begin(), 1);
|
| 917 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
{ // insert(hint, const T&)
|
| 921 |
+
set1.insert(set1.begin(), one);
|
| 922 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 923 |
+
}
|
| 924 |
+
|
| 925 |
+
{ // emplace(...)
|
| 926 |
+
set1.emplace(1);
|
| 927 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 928 |
+
set1.emplace("3");
|
| 929 |
+
expected_constructed += construct_three;
|
| 930 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 931 |
+
set1.emplace(one);
|
| 932 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 933 |
+
set1.emplace(three);
|
| 934 |
+
expected_constructed += construct_three;
|
| 935 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 936 |
+
}
|
| 937 |
+
|
| 938 |
+
{ // emplace_hint(...)
|
| 939 |
+
set1.emplace_hint(set1.begin(), 1);
|
| 940 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 941 |
+
set1.emplace_hint(set1.begin(), "3");
|
| 942 |
+
expected_constructed += construct_three;
|
| 943 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 944 |
+
set1.emplace_hint(set1.begin(), one);
|
| 945 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 946 |
+
set1.emplace_hint(set1.begin(), three);
|
| 947 |
+
expected_constructed += construct_three;
|
| 948 |
+
EXPECT_EQ(expected_constructed, decompose_constructed);
|
| 949 |
+
}
|
| 950 |
+
|
| 951 |
+
decompose_copy_constructed = 0;
|
| 952 |
+
decompose_copy_assigned = 0;
|
| 953 |
+
decompose_move_constructed = 0;
|
| 954 |
+
decompose_move_assigned = 0;
|
| 955 |
+
int expected_copy_constructed = 0;
|
| 956 |
+
int expected_move_constructed = 0;
|
| 957 |
+
{ // raw_hash_set(first, last) with random-access iterators
|
| 958 |
+
DecomposeSet set2(elem_vector.begin(), elem_vector.end());
|
| 959 |
+
// Expect exactly one copy-constructor call for each element if no
|
| 960 |
+
// rehashing is done.
|
| 961 |
+
expected_copy_constructed += elem_vector_count;
|
| 962 |
+
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
|
| 963 |
+
EXPECT_EQ(expected_move_constructed, decompose_move_constructed);
|
| 964 |
+
EXPECT_EQ(0, decompose_move_assigned);
|
| 965 |
+
EXPECT_EQ(0, decompose_copy_assigned);
|
| 966 |
+
}
|
| 967 |
+
|
| 968 |
+
{ // raw_hash_set(first, last) with forward iterators
|
| 969 |
+
std::list<DecomposeType> elem_list(elem_vector.begin(), elem_vector.end());
|
| 970 |
+
expected_copy_constructed = decompose_copy_constructed;
|
| 971 |
+
DecomposeSet set2(elem_list.begin(), elem_list.end());
|
| 972 |
+
// Expect exactly N elements copied into set, expect at most 2*N elements
|
| 973 |
+
// moving internally for all resizing needed (for a growth factor of 2).
|
| 974 |
+
expected_copy_constructed += elem_vector_count;
|
| 975 |
+
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
|
| 976 |
+
expected_move_constructed += elem_vector_count;
|
| 977 |
+
EXPECT_LT(expected_move_constructed, decompose_move_constructed);
|
| 978 |
+
expected_move_constructed += elem_vector_count;
|
| 979 |
+
EXPECT_GE(expected_move_constructed, decompose_move_constructed);
|
| 980 |
+
EXPECT_EQ(0, decompose_move_assigned);
|
| 981 |
+
EXPECT_EQ(0, decompose_copy_assigned);
|
| 982 |
+
expected_copy_constructed = decompose_copy_constructed;
|
| 983 |
+
expected_move_constructed = decompose_move_constructed;
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
{ // insert(first, last)
|
| 987 |
+
DecomposeSet set2;
|
| 988 |
+
set2.insert(elem_vector.begin(), elem_vector.end());
|
| 989 |
+
// Expect exactly N elements copied into set, expect at most 2*N elements
|
| 990 |
+
// moving internally for all resizing needed (for a growth factor of 2).
|
| 991 |
+
const int expected_new_elements = elem_vector_count;
|
| 992 |
+
const int expected_max_element_moves = 2 * elem_vector_count;
|
| 993 |
+
expected_copy_constructed += expected_new_elements;
|
| 994 |
+
EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
|
| 995 |
+
expected_move_constructed += expected_max_element_moves;
|
| 996 |
+
EXPECT_GE(expected_move_constructed, decompose_move_constructed);
|
| 997 |
+
EXPECT_EQ(0, decompose_move_assigned);
|
| 998 |
+
EXPECT_EQ(0, decompose_copy_assigned);
|
| 999 |
+
expected_copy_constructed = decompose_copy_constructed;
|
| 1000 |
+
expected_move_constructed = decompose_move_constructed;
|
| 1001 |
+
}
|
| 1002 |
+
}
|
| 1003 |
+
|
| 1004 |
+
TEST(Table, Decompose) {
|
| 1005 |
+
if (SwisstableGenerationsEnabled()) {
|
| 1006 |
+
GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
|
| 1007 |
+
}
|
| 1008 |
+
|
| 1009 |
+
TestDecompose<DecomposeHash, DecomposeEq>(false);
|
| 1010 |
+
|
| 1011 |
+
struct TransparentHashIntOverload {
|
| 1012 |
+
size_t operator()(const DecomposeType& a) const { return a.i; }
|
| 1013 |
+
size_t operator()(int a) const { return a; }
|
| 1014 |
+
};
|
| 1015 |
+
struct TransparentEqIntOverload {
|
| 1016 |
+
bool operator()(const DecomposeType& a, const DecomposeType& b) const {
|
| 1017 |
+
return a.i == b.i;
|
| 1018 |
+
}
|
| 1019 |
+
bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
|
| 1020 |
+
};
|
| 1021 |
+
TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
|
| 1022 |
+
TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
|
| 1023 |
+
TestDecompose<DecomposeHash, TransparentEqIntOverload>(true);
|
| 1024 |
+
}
|
| 1025 |
+
|
| 1026 |
+
// Returns the largest m such that a table with m elements has the same number
|
| 1027 |
+
// of buckets as a table with n elements.
|
| 1028 |
+
size_t MaxDensitySize(size_t n) {
|
| 1029 |
+
IntTable t;
|
| 1030 |
+
t.reserve(n);
|
| 1031 |
+
for (size_t i = 0; i != n; ++i) t.emplace(i);
|
| 1032 |
+
const size_t c = t.bucket_count();
|
| 1033 |
+
while (c == t.bucket_count()) t.emplace(n++);
|
| 1034 |
+
return t.size() - 1;
|
| 1035 |
+
}
|
| 1036 |
+
|
| 1037 |
+
struct Modulo1000Hash {
|
| 1038 |
+
size_t operator()(int x) const { return x % 1000; }
|
| 1039 |
+
};
|
| 1040 |
+
|
| 1041 |
+
struct Modulo1000HashTable
|
| 1042 |
+
: public raw_hash_set<IntPolicy, Modulo1000Hash, std::equal_to<int>,
|
| 1043 |
+
std::allocator<int>> {};
|
| 1044 |
+
|
| 1045 |
+
// Test that rehash with no resize happen in case of many deleted slots.
|
| 1046 |
+
TEST(Table, RehashWithNoResize) {
|
| 1047 |
+
if (SwisstableGenerationsEnabled()) {
|
| 1048 |
+
GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
|
| 1049 |
+
}
|
| 1050 |
+
|
| 1051 |
+
Modulo1000HashTable t;
|
| 1052 |
+
// Adding the same length (and the same hash) strings
|
| 1053 |
+
// to have at least kMinFullGroups groups
|
| 1054 |
+
// with Group::kWidth collisions. Then fill up to MaxDensitySize;
|
| 1055 |
+
const size_t kMinFullGroups = 7;
|
| 1056 |
+
std::vector<int> keys;
|
| 1057 |
+
for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
|
| 1058 |
+
int k = i * 1000;
|
| 1059 |
+
t.emplace(k);
|
| 1060 |
+
keys.push_back(k);
|
| 1061 |
+
}
|
| 1062 |
+
const size_t capacity = t.capacity();
|
| 1063 |
+
|
| 1064 |
+
// Remove elements from all groups except the first and the last one.
|
| 1065 |
+
// All elements removed from full groups will be marked as ctrl_t::kDeleted.
|
| 1066 |
+
const size_t erase_begin = Group::kWidth / 2;
|
| 1067 |
+
const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
|
| 1068 |
+
for (size_t i = erase_begin; i < erase_end; ++i) {
|
| 1069 |
+
EXPECT_EQ(1, t.erase(keys[i])) << i;
|
| 1070 |
+
}
|
| 1071 |
+
keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end);
|
| 1072 |
+
|
| 1073 |
+
auto last_key = keys.back();
|
| 1074 |
+
size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key);
|
| 1075 |
+
|
| 1076 |
+
// Make sure that we have to make a lot of probes for last key.
|
| 1077 |
+
ASSERT_GT(last_key_num_probes, kMinFullGroups);
|
| 1078 |
+
|
| 1079 |
+
int x = 1;
|
| 1080 |
+
// Insert and erase one element, before inplace rehash happen.
|
| 1081 |
+
while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) {
|
| 1082 |
+
t.emplace(x);
|
| 1083 |
+
ASSERT_EQ(capacity, t.capacity());
|
| 1084 |
+
// All elements should be there.
|
| 1085 |
+
ASSERT_TRUE(t.find(x) != t.end()) << x;
|
| 1086 |
+
for (const auto& k : keys) {
|
| 1087 |
+
ASSERT_TRUE(t.find(k) != t.end()) << k;
|
| 1088 |
+
}
|
| 1089 |
+
t.erase(x);
|
| 1090 |
+
++x;
|
| 1091 |
+
}
|
| 1092 |
+
}
|
| 1093 |
+
|
| 1094 |
+
TEST(Table, InsertEraseStressTest) {
|
| 1095 |
+
IntTable t;
|
| 1096 |
+
const size_t kMinElementCount = 250;
|
| 1097 |
+
std::deque<int> keys;
|
| 1098 |
+
size_t i = 0;
|
| 1099 |
+
for (; i < MaxDensitySize(kMinElementCount); ++i) {
|
| 1100 |
+
t.emplace(i);
|
| 1101 |
+
keys.push_back(i);
|
| 1102 |
+
}
|
| 1103 |
+
const size_t kNumIterations = 1000000;
|
| 1104 |
+
for (; i < kNumIterations; ++i) {
|
| 1105 |
+
ASSERT_EQ(1, t.erase(keys.front()));
|
| 1106 |
+
keys.pop_front();
|
| 1107 |
+
t.emplace(i);
|
| 1108 |
+
keys.push_back(i);
|
| 1109 |
+
}
|
| 1110 |
+
}
|
| 1111 |
+
|
| 1112 |
+
TEST(Table, InsertOverloads) {
|
| 1113 |
+
StringTable t;
|
| 1114 |
+
// These should all trigger the insert(init_type) overload.
|
| 1115 |
+
t.insert({{}, {}});
|
| 1116 |
+
t.insert({"ABC", {}});
|
| 1117 |
+
t.insert({"DEF", "!!!"});
|
| 1118 |
+
|
| 1119 |
+
EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""),
|
| 1120 |
+
Pair("DEF", "!!!")));
|
| 1121 |
+
}
|
| 1122 |
+
|
| 1123 |
+
TEST(Table, LargeTable) {
|
| 1124 |
+
IntTable t;
|
| 1125 |
+
for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40);
|
| 1126 |
+
for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40));
|
| 1127 |
+
}
|
| 1128 |
+
|
| 1129 |
+
// Timeout if copy is quadratic as it was in Rust.
|
| 1130 |
+
TEST(Table, EnsureNonQuadraticAsInRust) {
|
| 1131 |
+
static const size_t kLargeSize = 1 << 15;
|
| 1132 |
+
|
| 1133 |
+
IntTable t;
|
| 1134 |
+
for (size_t i = 0; i != kLargeSize; ++i) {
|
| 1135 |
+
t.insert(i);
|
| 1136 |
+
}
|
| 1137 |
+
|
| 1138 |
+
// If this is quadratic, the test will timeout.
|
| 1139 |
+
IntTable t2;
|
| 1140 |
+
for (const auto& entry : t) t2.insert(entry);
|
| 1141 |
+
}
|
| 1142 |
+
|
| 1143 |
+
TEST(Table, ClearBug) {
|
| 1144 |
+
if (SwisstableGenerationsEnabled()) {
|
| 1145 |
+
GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
|
| 1146 |
+
}
|
| 1147 |
+
|
| 1148 |
+
IntTable t;
|
| 1149 |
+
constexpr size_t capacity = container_internal::Group::kWidth - 1;
|
| 1150 |
+
constexpr size_t max_size = capacity / 2 + 1;
|
| 1151 |
+
for (size_t i = 0; i < max_size; ++i) {
|
| 1152 |
+
t.insert(i);
|
| 1153 |
+
}
|
| 1154 |
+
ASSERT_EQ(capacity, t.capacity());
|
| 1155 |
+
intptr_t original = reinterpret_cast<intptr_t>(&*t.find(2));
|
| 1156 |
+
t.clear();
|
| 1157 |
+
ASSERT_EQ(capacity, t.capacity());
|
| 1158 |
+
for (size_t i = 0; i < max_size; ++i) {
|
| 1159 |
+
t.insert(i);
|
| 1160 |
+
}
|
| 1161 |
+
ASSERT_EQ(capacity, t.capacity());
|
| 1162 |
+
intptr_t second = reinterpret_cast<intptr_t>(&*t.find(2));
|
| 1163 |
+
// We are checking that original and second are close enough to each other
|
| 1164 |
+
// that they are probably still in the same group. This is not strictly
|
| 1165 |
+
// guaranteed.
|
| 1166 |
+
EXPECT_LT(static_cast<size_t>(std::abs(original - second)),
|
| 1167 |
+
capacity * sizeof(IntTable::value_type));
|
| 1168 |
+
}
|
| 1169 |
+
|
| 1170 |
+
TEST(Table, Erase) {
|
| 1171 |
+
IntTable t;
|
| 1172 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1173 |
+
auto res = t.emplace(0);
|
| 1174 |
+
EXPECT_TRUE(res.second);
|
| 1175 |
+
EXPECT_EQ(1, t.size());
|
| 1176 |
+
t.erase(res.first);
|
| 1177 |
+
EXPECT_EQ(0, t.size());
|
| 1178 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1179 |
+
}
|
| 1180 |
+
|
| 1181 |
+
TEST(Table, EraseMaintainsValidIterator) {
|
| 1182 |
+
IntTable t;
|
| 1183 |
+
const int kNumElements = 100;
|
| 1184 |
+
for (int i = 0; i < kNumElements; i++) {
|
| 1185 |
+
EXPECT_TRUE(t.emplace(i).second);
|
| 1186 |
+
}
|
| 1187 |
+
EXPECT_EQ(t.size(), kNumElements);
|
| 1188 |
+
|
| 1189 |
+
int num_erase_calls = 0;
|
| 1190 |
+
auto it = t.begin();
|
| 1191 |
+
while (it != t.end()) {
|
| 1192 |
+
t.erase(it++);
|
| 1193 |
+
num_erase_calls++;
|
| 1194 |
+
}
|
| 1195 |
+
|
| 1196 |
+
EXPECT_TRUE(t.empty());
|
| 1197 |
+
EXPECT_EQ(num_erase_calls, kNumElements);
|
| 1198 |
+
}
|
| 1199 |
+
|
| 1200 |
+
TEST(Table, EraseBeginEnd) {
|
| 1201 |
+
IntTable t;
|
| 1202 |
+
for (int i = 0; i < 10; ++i) t.insert(i);
|
| 1203 |
+
EXPECT_EQ(t.size(), 10);
|
| 1204 |
+
t.erase(t.begin(), t.end());
|
| 1205 |
+
EXPECT_EQ(t.size(), 0);
|
| 1206 |
+
}
|
| 1207 |
+
|
| 1208 |
+
// Collect N bad keys by following algorithm:
|
| 1209 |
+
// 1. Create an empty table and reserve it to 2 * N.
|
| 1210 |
+
// 2. Insert N random elements.
|
| 1211 |
+
// 3. Take first Group::kWidth - 1 to bad_keys array.
|
| 1212 |
+
// 4. Clear the table without resize.
|
| 1213 |
+
// 5. Go to point 2 while N keys not collected
|
| 1214 |
+
std::vector<int64_t> CollectBadMergeKeys(size_t N) {
|
| 1215 |
+
static constexpr int kGroupSize = Group::kWidth - 1;
|
| 1216 |
+
|
| 1217 |
+
auto topk_range = [](size_t b, size_t e,
|
| 1218 |
+
IntTable* t) -> std::vector<int64_t> {
|
| 1219 |
+
for (size_t i = b; i != e; ++i) {
|
| 1220 |
+
t->emplace(i);
|
| 1221 |
+
}
|
| 1222 |
+
std::vector<int64_t> res;
|
| 1223 |
+
res.reserve(kGroupSize);
|
| 1224 |
+
auto it = t->begin();
|
| 1225 |
+
for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) {
|
| 1226 |
+
res.push_back(*it);
|
| 1227 |
+
}
|
| 1228 |
+
return res;
|
| 1229 |
+
};
|
| 1230 |
+
|
| 1231 |
+
std::vector<int64_t> bad_keys;
|
| 1232 |
+
bad_keys.reserve(N);
|
| 1233 |
+
IntTable t;
|
| 1234 |
+
t.reserve(N * 2);
|
| 1235 |
+
|
| 1236 |
+
for (size_t b = 0; bad_keys.size() < N; b += N) {
|
| 1237 |
+
auto keys = topk_range(b, b + N, &t);
|
| 1238 |
+
bad_keys.insert(bad_keys.end(), keys.begin(), keys.end());
|
| 1239 |
+
t.erase(t.begin(), t.end());
|
| 1240 |
+
EXPECT_TRUE(t.empty());
|
| 1241 |
+
}
|
| 1242 |
+
return bad_keys;
|
| 1243 |
+
}
|
| 1244 |
+
|
| 1245 |
+
struct ProbeStats {
|
| 1246 |
+
// Number of elements with specific probe length over all tested tables.
|
| 1247 |
+
std::vector<size_t> all_probes_histogram;
|
| 1248 |
+
// Ratios total_probe_length/size for every tested table.
|
| 1249 |
+
std::vector<double> single_table_ratios;
|
| 1250 |
+
|
| 1251 |
+
// Average ratio total_probe_length/size over tables.
|
| 1252 |
+
double AvgRatio() const {
|
| 1253 |
+
return std::accumulate(single_table_ratios.begin(),
|
| 1254 |
+
single_table_ratios.end(), 0.0) /
|
| 1255 |
+
single_table_ratios.size();
|
| 1256 |
+
}
|
| 1257 |
+
|
| 1258 |
+
// Maximum ratio total_probe_length/size over tables.
|
| 1259 |
+
double MaxRatio() const {
|
| 1260 |
+
return *std::max_element(single_table_ratios.begin(),
|
| 1261 |
+
single_table_ratios.end());
|
| 1262 |
+
}
|
| 1263 |
+
|
| 1264 |
+
// Percentile ratio total_probe_length/size over tables.
|
| 1265 |
+
double PercentileRatio(double Percentile = 0.95) const {
|
| 1266 |
+
auto r = single_table_ratios;
|
| 1267 |
+
auto mid = r.begin() + static_cast<size_t>(r.size() * Percentile);
|
| 1268 |
+
if (mid != r.end()) {
|
| 1269 |
+
std::nth_element(r.begin(), mid, r.end());
|
| 1270 |
+
return *mid;
|
| 1271 |
+
} else {
|
| 1272 |
+
return MaxRatio();
|
| 1273 |
+
}
|
| 1274 |
+
}
|
| 1275 |
+
|
| 1276 |
+
// Maximum probe length over all elements and all tables.
|
| 1277 |
+
size_t MaxProbe() const { return all_probes_histogram.size(); }
|
| 1278 |
+
|
| 1279 |
+
// Fraction of elements with specified probe length.
|
| 1280 |
+
std::vector<double> ProbeNormalizedHistogram() const {
|
| 1281 |
+
double total_elements = std::accumulate(all_probes_histogram.begin(),
|
| 1282 |
+
all_probes_histogram.end(), 0ull);
|
| 1283 |
+
std::vector<double> res;
|
| 1284 |
+
for (size_t p : all_probes_histogram) {
|
| 1285 |
+
res.push_back(p / total_elements);
|
| 1286 |
+
}
|
| 1287 |
+
return res;
|
| 1288 |
+
}
|
| 1289 |
+
|
| 1290 |
+
size_t PercentileProbe(double Percentile = 0.99) const {
|
| 1291 |
+
size_t idx = 0;
|
| 1292 |
+
for (double p : ProbeNormalizedHistogram()) {
|
| 1293 |
+
if (Percentile > p) {
|
| 1294 |
+
Percentile -= p;
|
| 1295 |
+
++idx;
|
| 1296 |
+
} else {
|
| 1297 |
+
return idx;
|
| 1298 |
+
}
|
| 1299 |
+
}
|
| 1300 |
+
return idx;
|
| 1301 |
+
}
|
| 1302 |
+
|
| 1303 |
+
friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) {
|
| 1304 |
+
out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio()
|
| 1305 |
+
<< ", PercentileRatio:" << s.PercentileRatio()
|
| 1306 |
+
<< ", MaxProbe:" << s.MaxProbe() << ", Probes=[";
|
| 1307 |
+
for (double p : s.ProbeNormalizedHistogram()) {
|
| 1308 |
+
out << p << ",";
|
| 1309 |
+
}
|
| 1310 |
+
out << "]}";
|
| 1311 |
+
|
| 1312 |
+
return out;
|
| 1313 |
+
}
|
| 1314 |
+
};
|
| 1315 |
+
|
| 1316 |
+
struct ExpectedStats {
|
| 1317 |
+
double avg_ratio;
|
| 1318 |
+
double max_ratio;
|
| 1319 |
+
std::vector<std::pair<double, double>> pecentile_ratios;
|
| 1320 |
+
std::vector<std::pair<double, double>> pecentile_probes;
|
| 1321 |
+
|
| 1322 |
+
friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) {
|
| 1323 |
+
out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio
|
| 1324 |
+
<< ", PercentileRatios: [";
|
| 1325 |
+
for (auto el : s.pecentile_ratios) {
|
| 1326 |
+
out << el.first << ":" << el.second << ", ";
|
| 1327 |
+
}
|
| 1328 |
+
out << "], PercentileProbes: [";
|
| 1329 |
+
for (auto el : s.pecentile_probes) {
|
| 1330 |
+
out << el.first << ":" << el.second << ", ";
|
| 1331 |
+
}
|
| 1332 |
+
out << "]}";
|
| 1333 |
+
|
| 1334 |
+
return out;
|
| 1335 |
+
}
|
| 1336 |
+
};
|
| 1337 |
+
|
| 1338 |
+
void VerifyStats(size_t size, const ExpectedStats& exp,
|
| 1339 |
+
const ProbeStats& stats) {
|
| 1340 |
+
EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats;
|
| 1341 |
+
EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats;
|
| 1342 |
+
for (auto pr : exp.pecentile_ratios) {
|
| 1343 |
+
EXPECT_LE(stats.PercentileRatio(pr.first), pr.second)
|
| 1344 |
+
<< size << " " << pr.first << " " << stats;
|
| 1345 |
+
}
|
| 1346 |
+
|
| 1347 |
+
for (auto pr : exp.pecentile_probes) {
|
| 1348 |
+
EXPECT_LE(stats.PercentileProbe(pr.first), pr.second)
|
| 1349 |
+
<< size << " " << pr.first << " " << stats;
|
| 1350 |
+
}
|
| 1351 |
+
}
|
| 1352 |
+
|
| 1353 |
+
using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
|
| 1354 |
+
|
| 1355 |
+
// Collect total ProbeStats on num_iters iterations of the following algorithm:
|
| 1356 |
+
// 1. Create new table and reserve it to keys.size() * 2
|
| 1357 |
+
// 2. Insert all keys xored with seed
|
| 1358 |
+
// 3. Collect ProbeStats from final table.
|
| 1359 |
+
ProbeStats CollectProbeStatsOnKeysXoredWithSeed(
|
| 1360 |
+
const std::vector<int64_t>& keys, size_t num_iters) {
|
| 1361 |
+
const size_t reserve_size = keys.size() * 2;
|
| 1362 |
+
|
| 1363 |
+
ProbeStats stats;
|
| 1364 |
+
|
| 1365 |
+
int64_t seed = 0x71b1a19b907d6e33;
|
| 1366 |
+
while (num_iters--) {
|
| 1367 |
+
seed = static_cast<int64_t>(static_cast<uint64_t>(seed) * 17 + 13);
|
| 1368 |
+
IntTable t1;
|
| 1369 |
+
t1.reserve(reserve_size);
|
| 1370 |
+
for (const auto& key : keys) {
|
| 1371 |
+
t1.emplace(key ^ seed);
|
| 1372 |
+
}
|
| 1373 |
+
|
| 1374 |
+
auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
|
| 1375 |
+
stats.all_probes_histogram.resize(
|
| 1376 |
+
std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
|
| 1377 |
+
std::transform(probe_histogram.begin(), probe_histogram.end(),
|
| 1378 |
+
stats.all_probes_histogram.begin(),
|
| 1379 |
+
stats.all_probes_histogram.begin(), std::plus<size_t>());
|
| 1380 |
+
|
| 1381 |
+
size_t total_probe_seq_length = 0;
|
| 1382 |
+
for (size_t i = 0; i < probe_histogram.size(); ++i) {
|
| 1383 |
+
total_probe_seq_length += i * probe_histogram[i];
|
| 1384 |
+
}
|
| 1385 |
+
stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
|
| 1386 |
+
keys.size());
|
| 1387 |
+
t1.erase(t1.begin(), t1.end());
|
| 1388 |
+
}
|
| 1389 |
+
return stats;
|
| 1390 |
+
}
|
| 1391 |
+
|
| 1392 |
+
ExpectedStats XorSeedExpectedStats() {
|
| 1393 |
+
constexpr bool kRandomizesInserts =
|
| 1394 |
+
#ifdef NDEBUG
|
| 1395 |
+
false;
|
| 1396 |
+
#else // NDEBUG
|
| 1397 |
+
true;
|
| 1398 |
+
#endif // NDEBUG
|
| 1399 |
+
|
| 1400 |
+
// The effective load factor is larger in non-opt mode because we insert
|
| 1401 |
+
// elements out of order.
|
| 1402 |
+
switch (container_internal::Group::kWidth) {
|
| 1403 |
+
case 8:
|
| 1404 |
+
if (kRandomizesInserts) {
|
| 1405 |
+
return {0.05,
|
| 1406 |
+
1.0,
|
| 1407 |
+
{{0.95, 0.5}},
|
| 1408 |
+
{{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
|
| 1409 |
+
} else {
|
| 1410 |
+
return {0.05,
|
| 1411 |
+
2.0,
|
| 1412 |
+
{{0.95, 0.1}},
|
| 1413 |
+
{{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
|
| 1414 |
+
}
|
| 1415 |
+
case 16:
|
| 1416 |
+
if (kRandomizesInserts) {
|
| 1417 |
+
return {0.1,
|
| 1418 |
+
2.0,
|
| 1419 |
+
{{0.95, 0.1}},
|
| 1420 |
+
{{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
|
| 1421 |
+
} else {
|
| 1422 |
+
return {0.05,
|
| 1423 |
+
1.0,
|
| 1424 |
+
{{0.95, 0.05}},
|
| 1425 |
+
{{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
|
| 1426 |
+
}
|
| 1427 |
+
}
|
| 1428 |
+
LOG(FATAL) << "Unknown Group width";
|
| 1429 |
+
return {};
|
| 1430 |
+
}
|
| 1431 |
+
|
| 1432 |
+
// TODO(b/80415403): Figure out why this test is so flaky, esp. on MSVC
|
| 1433 |
+
TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
|
| 1434 |
+
ProbeStatsPerSize stats;
|
| 1435 |
+
std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
|
| 1436 |
+
for (size_t size : sizes) {
|
| 1437 |
+
stats[size] =
|
| 1438 |
+
CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200);
|
| 1439 |
+
}
|
| 1440 |
+
auto expected = XorSeedExpectedStats();
|
| 1441 |
+
for (size_t size : sizes) {
|
| 1442 |
+
auto& stat = stats[size];
|
| 1443 |
+
VerifyStats(size, expected, stat);
|
| 1444 |
+
LOG(INFO) << size << " " << stat;
|
| 1445 |
+
}
|
| 1446 |
+
}
|
| 1447 |
+
|
| 1448 |
+
// Collect total ProbeStats on num_iters iterations of the following algorithm:
|
| 1449 |
+
// 1. Create new table
|
| 1450 |
+
// 2. Select 10% of keys and insert 10 elements key * 17 + j * 13
|
| 1451 |
+
// 3. Collect ProbeStats from final table
|
| 1452 |
+
ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
|
| 1453 |
+
const std::vector<int64_t>& keys, size_t num_iters) {
|
| 1454 |
+
ProbeStats stats;
|
| 1455 |
+
|
| 1456 |
+
std::random_device rd;
|
| 1457 |
+
std::mt19937 rng(rd());
|
| 1458 |
+
auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
|
| 1459 |
+
std::uniform_int_distribution<size_t> dist(0, keys.size() - 1);
|
| 1460 |
+
while (num_iters--) {
|
| 1461 |
+
IntTable t1;
|
| 1462 |
+
size_t num_keys = keys.size() / 10;
|
| 1463 |
+
size_t start = dist(rng);
|
| 1464 |
+
for (size_t i = 0; i != num_keys; ++i) {
|
| 1465 |
+
for (size_t j = 0; j != 10; ++j) {
|
| 1466 |
+
t1.emplace(linear_transform(keys[(i + start) % keys.size()], j));
|
| 1467 |
+
}
|
| 1468 |
+
}
|
| 1469 |
+
|
| 1470 |
+
auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
|
| 1471 |
+
stats.all_probes_histogram.resize(
|
| 1472 |
+
std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
|
| 1473 |
+
std::transform(probe_histogram.begin(), probe_histogram.end(),
|
| 1474 |
+
stats.all_probes_histogram.begin(),
|
| 1475 |
+
stats.all_probes_histogram.begin(), std::plus<size_t>());
|
| 1476 |
+
|
| 1477 |
+
size_t total_probe_seq_length = 0;
|
| 1478 |
+
for (size_t i = 0; i < probe_histogram.size(); ++i) {
|
| 1479 |
+
total_probe_seq_length += i * probe_histogram[i];
|
| 1480 |
+
}
|
| 1481 |
+
stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
|
| 1482 |
+
t1.size());
|
| 1483 |
+
t1.erase(t1.begin(), t1.end());
|
| 1484 |
+
}
|
| 1485 |
+
return stats;
|
| 1486 |
+
}
|
| 1487 |
+
|
| 1488 |
+
ExpectedStats LinearTransformExpectedStats() {
|
| 1489 |
+
constexpr bool kRandomizesInserts =
|
| 1490 |
+
#ifdef NDEBUG
|
| 1491 |
+
false;
|
| 1492 |
+
#else // NDEBUG
|
| 1493 |
+
true;
|
| 1494 |
+
#endif // NDEBUG
|
| 1495 |
+
|
| 1496 |
+
// The effective load factor is larger in non-opt mode because we insert
|
| 1497 |
+
// elements out of order.
|
| 1498 |
+
switch (container_internal::Group::kWidth) {
|
| 1499 |
+
case 8:
|
| 1500 |
+
if (kRandomizesInserts) {
|
| 1501 |
+
return {0.1,
|
| 1502 |
+
0.5,
|
| 1503 |
+
{{0.95, 0.3}},
|
| 1504 |
+
{{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
|
| 1505 |
+
} else {
|
| 1506 |
+
return {0.4,
|
| 1507 |
+
0.6,
|
| 1508 |
+
{{0.95, 0.5}},
|
| 1509 |
+
{{0.95, 1}, {0.99, 14}, {0.999, 23}, {0.9999, 26}}};
|
| 1510 |
+
}
|
| 1511 |
+
case 16:
|
| 1512 |
+
if (kRandomizesInserts) {
|
| 1513 |
+
return {0.1,
|
| 1514 |
+
0.4,
|
| 1515 |
+
{{0.95, 0.3}},
|
| 1516 |
+
{{0.95, 1}, {0.99, 2}, {0.999, 9}, {0.9999, 15}}};
|
| 1517 |
+
} else {
|
| 1518 |
+
return {0.05,
|
| 1519 |
+
0.2,
|
| 1520 |
+
{{0.95, 0.1}},
|
| 1521 |
+
{{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
|
| 1522 |
+
}
|
| 1523 |
+
}
|
| 1524 |
+
LOG(FATAL) << "Unknown Group width";
|
| 1525 |
+
return {};
|
| 1526 |
+
}
|
| 1527 |
+
|
| 1528 |
+
// TODO(b/80415403): Figure out why this test is so flaky.
|
| 1529 |
+
TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
|
| 1530 |
+
ProbeStatsPerSize stats;
|
| 1531 |
+
std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
|
| 1532 |
+
for (size_t size : sizes) {
|
| 1533 |
+
stats[size] = CollectProbeStatsOnLinearlyTransformedKeys(
|
| 1534 |
+
CollectBadMergeKeys(size), 300);
|
| 1535 |
+
}
|
| 1536 |
+
auto expected = LinearTransformExpectedStats();
|
| 1537 |
+
for (size_t size : sizes) {
|
| 1538 |
+
auto& stat = stats[size];
|
| 1539 |
+
VerifyStats(size, expected, stat);
|
| 1540 |
+
LOG(INFO) << size << " " << stat;
|
| 1541 |
+
}
|
| 1542 |
+
}
|
| 1543 |
+
|
| 1544 |
+
TEST(Table, EraseCollision) {
|
| 1545 |
+
BadTable t;
|
| 1546 |
+
|
| 1547 |
+
// 1 2 3
|
| 1548 |
+
t.emplace(1);
|
| 1549 |
+
t.emplace(2);
|
| 1550 |
+
t.emplace(3);
|
| 1551 |
+
EXPECT_THAT(*t.find(1), 1);
|
| 1552 |
+
EXPECT_THAT(*t.find(2), 2);
|
| 1553 |
+
EXPECT_THAT(*t.find(3), 3);
|
| 1554 |
+
EXPECT_EQ(3, t.size());
|
| 1555 |
+
|
| 1556 |
+
// 1 DELETED 3
|
| 1557 |
+
t.erase(t.find(2));
|
| 1558 |
+
EXPECT_THAT(*t.find(1), 1);
|
| 1559 |
+
EXPECT_TRUE(t.find(2) == t.end());
|
| 1560 |
+
EXPECT_THAT(*t.find(3), 3);
|
| 1561 |
+
EXPECT_EQ(2, t.size());
|
| 1562 |
+
|
| 1563 |
+
// DELETED DELETED 3
|
| 1564 |
+
t.erase(t.find(1));
|
| 1565 |
+
EXPECT_TRUE(t.find(1) == t.end());
|
| 1566 |
+
EXPECT_TRUE(t.find(2) == t.end());
|
| 1567 |
+
EXPECT_THAT(*t.find(3), 3);
|
| 1568 |
+
EXPECT_EQ(1, t.size());
|
| 1569 |
+
|
| 1570 |
+
// DELETED DELETED DELETED
|
| 1571 |
+
t.erase(t.find(3));
|
| 1572 |
+
EXPECT_TRUE(t.find(1) == t.end());
|
| 1573 |
+
EXPECT_TRUE(t.find(2) == t.end());
|
| 1574 |
+
EXPECT_TRUE(t.find(3) == t.end());
|
| 1575 |
+
EXPECT_EQ(0, t.size());
|
| 1576 |
+
}
|
| 1577 |
+
|
| 1578 |
+
TEST(Table, EraseInsertProbing) {
|
| 1579 |
+
BadTable t(100);
|
| 1580 |
+
|
| 1581 |
+
// 1 2 3 4
|
| 1582 |
+
t.emplace(1);
|
| 1583 |
+
t.emplace(2);
|
| 1584 |
+
t.emplace(3);
|
| 1585 |
+
t.emplace(4);
|
| 1586 |
+
|
| 1587 |
+
// 1 DELETED 3 DELETED
|
| 1588 |
+
t.erase(t.find(2));
|
| 1589 |
+
t.erase(t.find(4));
|
| 1590 |
+
|
| 1591 |
+
// 1 10 3 11 12
|
| 1592 |
+
t.emplace(10);
|
| 1593 |
+
t.emplace(11);
|
| 1594 |
+
t.emplace(12);
|
| 1595 |
+
|
| 1596 |
+
EXPECT_EQ(5, t.size());
|
| 1597 |
+
EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12));
|
| 1598 |
+
}
|
| 1599 |
+
|
| 1600 |
+
TEST(Table, Clear) {
|
| 1601 |
+
IntTable t;
|
| 1602 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1603 |
+
t.clear();
|
| 1604 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1605 |
+
auto res = t.emplace(0);
|
| 1606 |
+
EXPECT_TRUE(res.second);
|
| 1607 |
+
EXPECT_EQ(1, t.size());
|
| 1608 |
+
t.clear();
|
| 1609 |
+
EXPECT_EQ(0, t.size());
|
| 1610 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1611 |
+
}
|
| 1612 |
+
|
| 1613 |
+
TEST(Table, Swap) {
|
| 1614 |
+
IntTable t;
|
| 1615 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1616 |
+
auto res = t.emplace(0);
|
| 1617 |
+
EXPECT_TRUE(res.second);
|
| 1618 |
+
EXPECT_EQ(1, t.size());
|
| 1619 |
+
IntTable u;
|
| 1620 |
+
t.swap(u);
|
| 1621 |
+
EXPECT_EQ(0, t.size());
|
| 1622 |
+
EXPECT_EQ(1, u.size());
|
| 1623 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1624 |
+
EXPECT_THAT(*u.find(0), 0);
|
| 1625 |
+
}
|
| 1626 |
+
|
| 1627 |
+
TEST(Table, Rehash) {
|
| 1628 |
+
IntTable t;
|
| 1629 |
+
EXPECT_TRUE(t.find(0) == t.end());
|
| 1630 |
+
t.emplace(0);
|
| 1631 |
+
t.emplace(1);
|
| 1632 |
+
EXPECT_EQ(2, t.size());
|
| 1633 |
+
t.rehash(128);
|
| 1634 |
+
EXPECT_EQ(2, t.size());
|
| 1635 |
+
EXPECT_THAT(*t.find(0), 0);
|
| 1636 |
+
EXPECT_THAT(*t.find(1), 1);
|
| 1637 |
+
}
|
| 1638 |
+
|
| 1639 |
+
TEST(Table, RehashDoesNotRehashWhenNotNecessary) {
|
| 1640 |
+
IntTable t;
|
| 1641 |
+
t.emplace(0);
|
| 1642 |
+
t.emplace(1);
|
| 1643 |
+
auto* p = &*t.find(0);
|
| 1644 |
+
t.rehash(1);
|
| 1645 |
+
EXPECT_EQ(p, &*t.find(0));
|
| 1646 |
+
}
|
| 1647 |
+
|
| 1648 |
+
TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) {
|
| 1649 |
+
IntTable t;
|
| 1650 |
+
t.rehash(0);
|
| 1651 |
+
EXPECT_EQ(0, t.bucket_count());
|
| 1652 |
+
}
|
| 1653 |
+
|
| 1654 |
+
TEST(Table, RehashZeroDeallocatesEmptyTable) {
|
| 1655 |
+
IntTable t;
|
| 1656 |
+
t.emplace(0);
|
| 1657 |
+
t.clear();
|
| 1658 |
+
EXPECT_NE(0, t.bucket_count());
|
| 1659 |
+
t.rehash(0);
|
| 1660 |
+
EXPECT_EQ(0, t.bucket_count());
|
| 1661 |
+
}
|
| 1662 |
+
|
| 1663 |
+
TEST(Table, RehashZeroForcesRehash) {
|
| 1664 |
+
IntTable t;
|
| 1665 |
+
t.emplace(0);
|
| 1666 |
+
t.emplace(1);
|
| 1667 |
+
auto* p = &*t.find(0);
|
| 1668 |
+
t.rehash(0);
|
| 1669 |
+
EXPECT_NE(p, &*t.find(0));
|
| 1670 |
+
}
|
| 1671 |
+
|
| 1672 |
+
TEST(Table, ConstructFromInitList) {
|
| 1673 |
+
using P = std::pair<std::string, std::string>;
|
| 1674 |
+
struct Q {
|
| 1675 |
+
operator P() const { return {}; } // NOLINT
|
| 1676 |
+
};
|
| 1677 |
+
StringTable t = {P(), Q(), {}, {{}, {}}};
|
| 1678 |
+
}
|
| 1679 |
+
|
| 1680 |
+
TEST(Table, CopyConstruct) {
|
| 1681 |
+
IntTable t;
|
| 1682 |
+
t.emplace(0);
|
| 1683 |
+
EXPECT_EQ(1, t.size());
|
| 1684 |
+
{
|
| 1685 |
+
IntTable u(t);
|
| 1686 |
+
EXPECT_EQ(1, u.size());
|
| 1687 |
+
EXPECT_THAT(*u.find(0), 0);
|
| 1688 |
+
}
|
| 1689 |
+
{
|
| 1690 |
+
IntTable u{t};
|
| 1691 |
+
EXPECT_EQ(1, u.size());
|
| 1692 |
+
EXPECT_THAT(*u.find(0), 0);
|
| 1693 |
+
}
|
| 1694 |
+
{
|
| 1695 |
+
IntTable u = t;
|
| 1696 |
+
EXPECT_EQ(1, u.size());
|
| 1697 |
+
EXPECT_THAT(*u.find(0), 0);
|
| 1698 |
+
}
|
| 1699 |
+
}
|
| 1700 |
+
|
| 1701 |
+
TEST(Table, CopyConstructWithAlloc) {
|
| 1702 |
+
StringTable t;
|
| 1703 |
+
t.emplace("a", "b");
|
| 1704 |
+
EXPECT_EQ(1, t.size());
|
| 1705 |
+
StringTable u(t, Alloc<std::pair<std::string, std::string>>());
|
| 1706 |
+
EXPECT_EQ(1, u.size());
|
| 1707 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1708 |
+
}
|
| 1709 |
+
|
| 1710 |
+
struct ExplicitAllocIntTable
|
| 1711 |
+
: raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
|
| 1712 |
+
std::equal_to<int64_t>, Alloc<int64_t>> {
|
| 1713 |
+
ExplicitAllocIntTable() = default;
|
| 1714 |
+
};
|
| 1715 |
+
|
| 1716 |
+
TEST(Table, AllocWithExplicitCtor) {
|
| 1717 |
+
ExplicitAllocIntTable t;
|
| 1718 |
+
EXPECT_EQ(0, t.size());
|
| 1719 |
+
}
|
| 1720 |
+
|
| 1721 |
+
TEST(Table, MoveConstruct) {
|
| 1722 |
+
{
|
| 1723 |
+
StringTable t;
|
| 1724 |
+
t.emplace("a", "b");
|
| 1725 |
+
EXPECT_EQ(1, t.size());
|
| 1726 |
+
|
| 1727 |
+
StringTable u(std::move(t));
|
| 1728 |
+
EXPECT_EQ(1, u.size());
|
| 1729 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1730 |
+
}
|
| 1731 |
+
{
|
| 1732 |
+
StringTable t;
|
| 1733 |
+
t.emplace("a", "b");
|
| 1734 |
+
EXPECT_EQ(1, t.size());
|
| 1735 |
+
|
| 1736 |
+
StringTable u{std::move(t)};
|
| 1737 |
+
EXPECT_EQ(1, u.size());
|
| 1738 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1739 |
+
}
|
| 1740 |
+
{
|
| 1741 |
+
StringTable t;
|
| 1742 |
+
t.emplace("a", "b");
|
| 1743 |
+
EXPECT_EQ(1, t.size());
|
| 1744 |
+
|
| 1745 |
+
StringTable u = std::move(t);
|
| 1746 |
+
EXPECT_EQ(1, u.size());
|
| 1747 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1748 |
+
}
|
| 1749 |
+
}
|
| 1750 |
+
|
| 1751 |
+
TEST(Table, MoveConstructWithAlloc) {
|
| 1752 |
+
StringTable t;
|
| 1753 |
+
t.emplace("a", "b");
|
| 1754 |
+
EXPECT_EQ(1, t.size());
|
| 1755 |
+
StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>());
|
| 1756 |
+
EXPECT_EQ(1, u.size());
|
| 1757 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1758 |
+
}
|
| 1759 |
+
|
| 1760 |
+
TEST(Table, CopyAssign) {
|
| 1761 |
+
StringTable t;
|
| 1762 |
+
t.emplace("a", "b");
|
| 1763 |
+
EXPECT_EQ(1, t.size());
|
| 1764 |
+
StringTable u;
|
| 1765 |
+
u = t;
|
| 1766 |
+
EXPECT_EQ(1, u.size());
|
| 1767 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1768 |
+
}
|
| 1769 |
+
|
| 1770 |
+
TEST(Table, CopySelfAssign) {
|
| 1771 |
+
StringTable t;
|
| 1772 |
+
t.emplace("a", "b");
|
| 1773 |
+
EXPECT_EQ(1, t.size());
|
| 1774 |
+
t = *&t;
|
| 1775 |
+
EXPECT_EQ(1, t.size());
|
| 1776 |
+
EXPECT_THAT(*t.find("a"), Pair("a", "b"));
|
| 1777 |
+
}
|
| 1778 |
+
|
| 1779 |
+
TEST(Table, MoveAssign) {
|
| 1780 |
+
StringTable t;
|
| 1781 |
+
t.emplace("a", "b");
|
| 1782 |
+
EXPECT_EQ(1, t.size());
|
| 1783 |
+
StringTable u;
|
| 1784 |
+
u = std::move(t);
|
| 1785 |
+
EXPECT_EQ(1, u.size());
|
| 1786 |
+
EXPECT_THAT(*u.find("a"), Pair("a", "b"));
|
| 1787 |
+
}
|
| 1788 |
+
|
| 1789 |
+
TEST(Table, MoveSelfAssign) {
|
| 1790 |
+
StringTable t;
|
| 1791 |
+
t.emplace("a", "b");
|
| 1792 |
+
EXPECT_EQ(1, t.size());
|
| 1793 |
+
t = std::move(*&t);
|
| 1794 |
+
// As long as we don't crash, it's fine.
|
| 1795 |
+
}
|
| 1796 |
+
|
| 1797 |
+
TEST(Table, Equality) {
|
| 1798 |
+
StringTable t;
|
| 1799 |
+
std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
|
| 1800 |
+
{"aa", "bb"}};
|
| 1801 |
+
t.insert(std::begin(v), std::end(v));
|
| 1802 |
+
StringTable u = t;
|
| 1803 |
+
EXPECT_EQ(u, t);
|
| 1804 |
+
}
|
| 1805 |
+
|
| 1806 |
+
TEST(Table, Equality2) {
|
| 1807 |
+
StringTable t;
|
| 1808 |
+
std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"},
|
| 1809 |
+
{"aa", "bb"}};
|
| 1810 |
+
t.insert(std::begin(v1), std::end(v1));
|
| 1811 |
+
StringTable u;
|
| 1812 |
+
std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
|
| 1813 |
+
{"aa", "aa"}};
|
| 1814 |
+
u.insert(std::begin(v2), std::end(v2));
|
| 1815 |
+
EXPECT_NE(u, t);
|
| 1816 |
+
}
|
| 1817 |
+
|
| 1818 |
+
TEST(Table, Equality3) {
|
| 1819 |
+
StringTable t;
|
| 1820 |
+
std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"},
|
| 1821 |
+
{"bb", "bb"}};
|
| 1822 |
+
t.insert(std::begin(v1), std::end(v1));
|
| 1823 |
+
StringTable u;
|
| 1824 |
+
std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
|
| 1825 |
+
{"aa", "aa"}};
|
| 1826 |
+
u.insert(std::begin(v2), std::end(v2));
|
| 1827 |
+
EXPECT_NE(u, t);
|
| 1828 |
+
}
|
| 1829 |
+
|
| 1830 |
+
TEST(Table, NumDeletedRegression) {
|
| 1831 |
+
IntTable t;
|
| 1832 |
+
t.emplace(0);
|
| 1833 |
+
t.erase(t.find(0));
|
| 1834 |
+
// construct over a deleted slot.
|
| 1835 |
+
t.emplace(0);
|
| 1836 |
+
t.clear();
|
| 1837 |
+
}
|
| 1838 |
+
|
| 1839 |
+
TEST(Table, FindFullDeletedRegression) {
|
| 1840 |
+
IntTable t;
|
| 1841 |
+
for (int i = 0; i < 1000; ++i) {
|
| 1842 |
+
t.emplace(i);
|
| 1843 |
+
t.erase(t.find(i));
|
| 1844 |
+
}
|
| 1845 |
+
EXPECT_EQ(0, t.size());
|
| 1846 |
+
}
|
| 1847 |
+
|
| 1848 |
+
TEST(Table, ReplacingDeletedSlotDoesNotRehash) {
|
| 1849 |
+
size_t n;
|
| 1850 |
+
{
|
| 1851 |
+
// Compute n such that n is the maximum number of elements before rehash.
|
| 1852 |
+
IntTable t;
|
| 1853 |
+
t.emplace(0);
|
| 1854 |
+
size_t c = t.bucket_count();
|
| 1855 |
+
for (n = 1; c == t.bucket_count(); ++n) t.emplace(n);
|
| 1856 |
+
--n;
|
| 1857 |
+
}
|
| 1858 |
+
IntTable t;
|
| 1859 |
+
t.rehash(n);
|
| 1860 |
+
const size_t c = t.bucket_count();
|
| 1861 |
+
for (size_t i = 0; i != n; ++i) t.emplace(i);
|
| 1862 |
+
EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
|
| 1863 |
+
t.erase(0);
|
| 1864 |
+
t.emplace(0);
|
| 1865 |
+
EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
|
| 1866 |
+
}
|
| 1867 |
+
|
| 1868 |
+
TEST(Table, NoThrowMoveConstruct) {
|
| 1869 |
+
ASSERT_TRUE(
|
| 1870 |
+
std::is_nothrow_copy_constructible<absl::Hash<absl::string_view>>::value);
|
| 1871 |
+
ASSERT_TRUE(std::is_nothrow_copy_constructible<
|
| 1872 |
+
std::equal_to<absl::string_view>>::value);
|
| 1873 |
+
ASSERT_TRUE(std::is_nothrow_copy_constructible<std::allocator<int>>::value);
|
| 1874 |
+
EXPECT_TRUE(std::is_nothrow_move_constructible<StringTable>::value);
|
| 1875 |
+
}
|
| 1876 |
+
|
| 1877 |
+
TEST(Table, NoThrowMoveAssign) {
|
| 1878 |
+
ASSERT_TRUE(
|
| 1879 |
+
std::is_nothrow_move_assignable<absl::Hash<absl::string_view>>::value);
|
| 1880 |
+
ASSERT_TRUE(
|
| 1881 |
+
std::is_nothrow_move_assignable<std::equal_to<absl::string_view>>::value);
|
| 1882 |
+
ASSERT_TRUE(std::is_nothrow_move_assignable<std::allocator<int>>::value);
|
| 1883 |
+
ASSERT_TRUE(
|
| 1884 |
+
absl::allocator_traits<std::allocator<int>>::is_always_equal::value);
|
| 1885 |
+
EXPECT_TRUE(std::is_nothrow_move_assignable<StringTable>::value);
|
| 1886 |
+
}
|
| 1887 |
+
|
| 1888 |
+
TEST(Table, NoThrowSwappable) {
|
| 1889 |
+
ASSERT_TRUE(
|
| 1890 |
+
container_internal::IsNoThrowSwappable<absl::Hash<absl::string_view>>());
|
| 1891 |
+
ASSERT_TRUE(container_internal::IsNoThrowSwappable<
|
| 1892 |
+
std::equal_to<absl::string_view>>());
|
| 1893 |
+
ASSERT_TRUE(container_internal::IsNoThrowSwappable<std::allocator<int>>());
|
| 1894 |
+
EXPECT_TRUE(container_internal::IsNoThrowSwappable<StringTable>());
|
| 1895 |
+
}
|
| 1896 |
+
|
| 1897 |
+
TEST(Table, HeterogeneousLookup) {
|
| 1898 |
+
struct Hash {
|
| 1899 |
+
size_t operator()(int64_t i) const { return i; }
|
| 1900 |
+
size_t operator()(double i) const {
|
| 1901 |
+
ADD_FAILURE();
|
| 1902 |
+
return i;
|
| 1903 |
+
}
|
| 1904 |
+
};
|
| 1905 |
+
struct Eq {
|
| 1906 |
+
bool operator()(int64_t a, int64_t b) const { return a == b; }
|
| 1907 |
+
bool operator()(double a, int64_t b) const {
|
| 1908 |
+
ADD_FAILURE();
|
| 1909 |
+
return a == b;
|
| 1910 |
+
}
|
| 1911 |
+
bool operator()(int64_t a, double b) const {
|
| 1912 |
+
ADD_FAILURE();
|
| 1913 |
+
return a == b;
|
| 1914 |
+
}
|
| 1915 |
+
bool operator()(double a, double b) const {
|
| 1916 |
+
ADD_FAILURE();
|
| 1917 |
+
return a == b;
|
| 1918 |
+
}
|
| 1919 |
+
};
|
| 1920 |
+
|
| 1921 |
+
struct THash {
|
| 1922 |
+
using is_transparent = void;
|
| 1923 |
+
size_t operator()(int64_t i) const { return i; }
|
| 1924 |
+
size_t operator()(double i) const { return i; }
|
| 1925 |
+
};
|
| 1926 |
+
struct TEq {
|
| 1927 |
+
using is_transparent = void;
|
| 1928 |
+
bool operator()(int64_t a, int64_t b) const { return a == b; }
|
| 1929 |
+
bool operator()(double a, int64_t b) const { return a == b; }
|
| 1930 |
+
bool operator()(int64_t a, double b) const { return a == b; }
|
| 1931 |
+
bool operator()(double a, double b) const { return a == b; }
|
| 1932 |
+
};
|
| 1933 |
+
|
| 1934 |
+
raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
|
| 1935 |
+
// It will convert to int64_t before the query.
|
| 1936 |
+
EXPECT_EQ(1, *s.find(double{1.1}));
|
| 1937 |
+
|
| 1938 |
+
raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
|
| 1939 |
+
// It will try to use the double, and fail to find the object.
|
| 1940 |
+
EXPECT_TRUE(ts.find(1.1) == ts.end());
|
| 1941 |
+
}
|
| 1942 |
+
|
| 1943 |
+
template <class Table>
|
| 1944 |
+
using CallFind = decltype(std::declval<Table&>().find(17));
|
| 1945 |
+
|
| 1946 |
+
template <class Table>
|
| 1947 |
+
using CallErase = decltype(std::declval<Table&>().erase(17));
|
| 1948 |
+
|
| 1949 |
+
template <class Table>
|
| 1950 |
+
using CallExtract = decltype(std::declval<Table&>().extract(17));
|
| 1951 |
+
|
| 1952 |
+
template <class Table>
|
| 1953 |
+
using CallPrefetch = decltype(std::declval<Table&>().prefetch(17));
|
| 1954 |
+
|
| 1955 |
+
template <class Table>
|
| 1956 |
+
using CallCount = decltype(std::declval<Table&>().count(17));
|
| 1957 |
+
|
| 1958 |
+
template <template <typename> class C, class Table, class = void>
|
| 1959 |
+
struct VerifyResultOf : std::false_type {};
|
| 1960 |
+
|
| 1961 |
+
template <template <typename> class C, class Table>
|
| 1962 |
+
struct VerifyResultOf<C, Table, absl::void_t<C<Table>>> : std::true_type {};
|
| 1963 |
+
|
| 1964 |
+
TEST(Table, HeterogeneousLookupOverloads) {
|
| 1965 |
+
using NonTransparentTable =
|
| 1966 |
+
raw_hash_set<StringPolicy, absl::Hash<absl::string_view>,
|
| 1967 |
+
std::equal_to<absl::string_view>, std::allocator<int>>;
|
| 1968 |
+
|
| 1969 |
+
EXPECT_FALSE((VerifyResultOf<CallFind, NonTransparentTable>()));
|
| 1970 |
+
EXPECT_FALSE((VerifyResultOf<CallErase, NonTransparentTable>()));
|
| 1971 |
+
EXPECT_FALSE((VerifyResultOf<CallExtract, NonTransparentTable>()));
|
| 1972 |
+
EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
|
| 1973 |
+
EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
|
| 1974 |
+
|
| 1975 |
+
using TransparentTable =
|
| 1976 |
+
raw_hash_set<StringPolicy, hash_default_hash<absl::string_view>,
|
| 1977 |
+
hash_default_eq<absl::string_view>, std::allocator<int>>;
|
| 1978 |
+
|
| 1979 |
+
EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
|
| 1980 |
+
EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
|
| 1981 |
+
EXPECT_TRUE((VerifyResultOf<CallExtract, TransparentTable>()));
|
| 1982 |
+
EXPECT_TRUE((VerifyResultOf<CallPrefetch, TransparentTable>()));
|
| 1983 |
+
EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
|
| 1984 |
+
}
|
| 1985 |
+
|
| 1986 |
+
TEST(Iterator, IsDefaultConstructible) {
|
| 1987 |
+
StringTable::iterator i;
|
| 1988 |
+
EXPECT_TRUE(i == StringTable::iterator());
|
| 1989 |
+
}
|
| 1990 |
+
|
| 1991 |
+
TEST(ConstIterator, IsDefaultConstructible) {
|
| 1992 |
+
StringTable::const_iterator i;
|
| 1993 |
+
EXPECT_TRUE(i == StringTable::const_iterator());
|
| 1994 |
+
}
|
| 1995 |
+
|
| 1996 |
+
TEST(Iterator, ConvertsToConstIterator) {
|
| 1997 |
+
StringTable::iterator i;
|
| 1998 |
+
EXPECT_TRUE(i == StringTable::const_iterator());
|
| 1999 |
+
}
|
| 2000 |
+
|
| 2001 |
+
TEST(Iterator, Iterates) {
|
| 2002 |
+
IntTable t;
|
| 2003 |
+
for (size_t i = 3; i != 6; ++i) EXPECT_TRUE(t.emplace(i).second);
|
| 2004 |
+
EXPECT_THAT(t, UnorderedElementsAre(3, 4, 5));
|
| 2005 |
+
}
|
| 2006 |
+
|
| 2007 |
+
TEST(Table, Merge) {
|
| 2008 |
+
StringTable t1, t2;
|
| 2009 |
+
t1.emplace("0", "-0");
|
| 2010 |
+
t1.emplace("1", "-1");
|
| 2011 |
+
t2.emplace("0", "~0");
|
| 2012 |
+
t2.emplace("2", "~2");
|
| 2013 |
+
|
| 2014 |
+
EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1")));
|
| 2015 |
+
EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"), Pair("2", "~2")));
|
| 2016 |
+
|
| 2017 |
+
t1.merge(t2);
|
| 2018 |
+
EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1"),
|
| 2019 |
+
Pair("2", "~2")));
|
| 2020 |
+
EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
|
| 2021 |
+
}
|
| 2022 |
+
|
| 2023 |
+
TEST(Table, IteratorEmplaceConstructibleRequirement) {
|
| 2024 |
+
struct Value {
|
| 2025 |
+
explicit Value(absl::string_view view) : value(view) {}
|
| 2026 |
+
std::string value;
|
| 2027 |
+
|
| 2028 |
+
bool operator==(const Value& other) const { return value == other.value; }
|
| 2029 |
+
};
|
| 2030 |
+
struct H {
|
| 2031 |
+
size_t operator()(const Value& v) const {
|
| 2032 |
+
return absl::Hash<std::string>{}(v.value);
|
| 2033 |
+
}
|
| 2034 |
+
};
|
| 2035 |
+
|
| 2036 |
+
struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>,
|
| 2037 |
+
std::allocator<Value>> {
|
| 2038 |
+
using Base = typename Table::raw_hash_set;
|
| 2039 |
+
using Base::Base;
|
| 2040 |
+
};
|
| 2041 |
+
|
| 2042 |
+
std::string input[3]{"A", "B", "C"};
|
| 2043 |
+
|
| 2044 |
+
Table t(std::begin(input), std::end(input));
|
| 2045 |
+
EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"}));
|
| 2046 |
+
|
| 2047 |
+
input[0] = "D";
|
| 2048 |
+
input[1] = "E";
|
| 2049 |
+
input[2] = "F";
|
| 2050 |
+
t.insert(std::begin(input), std::end(input));
|
| 2051 |
+
EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"},
|
| 2052 |
+
Value{"D"}, Value{"E"}, Value{"F"}));
|
| 2053 |
+
}
|
| 2054 |
+
|
| 2055 |
+
TEST(Nodes, EmptyNodeType) {
|
| 2056 |
+
using node_type = StringTable::node_type;
|
| 2057 |
+
node_type n;
|
| 2058 |
+
EXPECT_FALSE(n);
|
| 2059 |
+
EXPECT_TRUE(n.empty());
|
| 2060 |
+
|
| 2061 |
+
EXPECT_TRUE((std::is_same<node_type::allocator_type,
|
| 2062 |
+
StringTable::allocator_type>::value));
|
| 2063 |
+
}
|
| 2064 |
+
|
| 2065 |
+
TEST(Nodes, ExtractInsert) {
|
| 2066 |
+
constexpr char k0[] = "Very long string zero.";
|
| 2067 |
+
constexpr char k1[] = "Very long string one.";
|
| 2068 |
+
constexpr char k2[] = "Very long string two.";
|
| 2069 |
+
StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}};
|
| 2070 |
+
EXPECT_THAT(t,
|
| 2071 |
+
UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, "")));
|
| 2072 |
+
|
| 2073 |
+
auto node = t.extract(k0);
|
| 2074 |
+
EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
|
| 2075 |
+
EXPECT_TRUE(node);
|
| 2076 |
+
EXPECT_FALSE(node.empty());
|
| 2077 |
+
|
| 2078 |
+
StringTable t2;
|
| 2079 |
+
StringTable::insert_return_type res = t2.insert(std::move(node));
|
| 2080 |
+
EXPECT_TRUE(res.inserted);
|
| 2081 |
+
EXPECT_THAT(*res.position, Pair(k0, ""));
|
| 2082 |
+
EXPECT_FALSE(res.node);
|
| 2083 |
+
EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
|
| 2084 |
+
|
| 2085 |
+
// Not there.
|
| 2086 |
+
EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
|
| 2087 |
+
node = t.extract("Not there!");
|
| 2088 |
+
EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
|
| 2089 |
+
EXPECT_FALSE(node);
|
| 2090 |
+
|
| 2091 |
+
// Inserting nothing.
|
| 2092 |
+
res = t2.insert(std::move(node));
|
| 2093 |
+
EXPECT_FALSE(res.inserted);
|
| 2094 |
+
EXPECT_EQ(res.position, t2.end());
|
| 2095 |
+
EXPECT_FALSE(res.node);
|
| 2096 |
+
EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
|
| 2097 |
+
|
| 2098 |
+
t.emplace(k0, "1");
|
| 2099 |
+
node = t.extract(k0);
|
| 2100 |
+
|
| 2101 |
+
// Insert duplicate.
|
| 2102 |
+
res = t2.insert(std::move(node));
|
| 2103 |
+
EXPECT_FALSE(res.inserted);
|
| 2104 |
+
EXPECT_THAT(*res.position, Pair(k0, ""));
|
| 2105 |
+
EXPECT_TRUE(res.node);
|
| 2106 |
+
EXPECT_FALSE(node); // NOLINT(bugprone-use-after-move)
|
| 2107 |
+
}
|
| 2108 |
+
|
| 2109 |
+
TEST(Nodes, HintInsert) {
|
| 2110 |
+
IntTable t = {1, 2, 3};
|
| 2111 |
+
auto node = t.extract(1);
|
| 2112 |
+
EXPECT_THAT(t, UnorderedElementsAre(2, 3));
|
| 2113 |
+
auto it = t.insert(t.begin(), std::move(node));
|
| 2114 |
+
EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
|
| 2115 |
+
EXPECT_EQ(*it, 1);
|
| 2116 |
+
EXPECT_FALSE(node); // NOLINT(bugprone-use-after-move)
|
| 2117 |
+
|
| 2118 |
+
node = t.extract(2);
|
| 2119 |
+
EXPECT_THAT(t, UnorderedElementsAre(1, 3));
|
| 2120 |
+
// reinsert 2 to make the next insert fail.
|
| 2121 |
+
t.insert(2);
|
| 2122 |
+
EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
|
| 2123 |
+
it = t.insert(t.begin(), std::move(node));
|
| 2124 |
+
EXPECT_EQ(*it, 2);
|
| 2125 |
+
// The node was not emptied by the insert call.
|
| 2126 |
+
EXPECT_TRUE(node); // NOLINT(bugprone-use-after-move)
|
| 2127 |
+
}
|
| 2128 |
+
|
| 2129 |
+
IntTable MakeSimpleTable(size_t size) {
|
| 2130 |
+
IntTable t;
|
| 2131 |
+
while (t.size() < size) t.insert(t.size());
|
| 2132 |
+
return t;
|
| 2133 |
+
}
|
| 2134 |
+
|
| 2135 |
+
std::vector<int> OrderOfIteration(const IntTable& t) {
|
| 2136 |
+
return {t.begin(), t.end()};
|
| 2137 |
+
}
|
| 2138 |
+
|
| 2139 |
+
// These IterationOrderChanges tests depend on non-deterministic behavior.
|
| 2140 |
+
// We are injecting non-determinism from the pointer of the table, but do so in
|
| 2141 |
+
// a way that only the page matters. We have to retry enough times to make sure
|
| 2142 |
+
// we are touching different memory pages to cause the ordering to change.
|
| 2143 |
+
// We also need to keep the old tables around to avoid getting the same memory
|
| 2144 |
+
// blocks over and over.
|
| 2145 |
+
TEST(Table, IterationOrderChangesByInstance) {
|
| 2146 |
+
for (size_t size : {2, 6, 12, 20}) {
|
| 2147 |
+
const auto reference_table = MakeSimpleTable(size);
|
| 2148 |
+
const auto reference = OrderOfIteration(reference_table);
|
| 2149 |
+
|
| 2150 |
+
std::vector<IntTable> tables;
|
| 2151 |
+
bool found_difference = false;
|
| 2152 |
+
for (int i = 0; !found_difference && i < 5000; ++i) {
|
| 2153 |
+
tables.push_back(MakeSimpleTable(size));
|
| 2154 |
+
found_difference = OrderOfIteration(tables.back()) != reference;
|
| 2155 |
+
}
|
| 2156 |
+
if (!found_difference) {
|
| 2157 |
+
FAIL()
|
| 2158 |
+
<< "Iteration order remained the same across many attempts with size "
|
| 2159 |
+
<< size;
|
| 2160 |
+
}
|
| 2161 |
+
}
|
| 2162 |
+
}
|
| 2163 |
+
|
| 2164 |
+
TEST(Table, IterationOrderChangesOnRehash) {
|
| 2165 |
+
std::vector<IntTable> garbage;
|
| 2166 |
+
for (int i = 0; i < 5000; ++i) {
|
| 2167 |
+
auto t = MakeSimpleTable(20);
|
| 2168 |
+
const auto reference = OrderOfIteration(t);
|
| 2169 |
+
// Force rehash to the same size.
|
| 2170 |
+
t.rehash(0);
|
| 2171 |
+
auto trial = OrderOfIteration(t);
|
| 2172 |
+
if (trial != reference) {
|
| 2173 |
+
// We are done.
|
| 2174 |
+
return;
|
| 2175 |
+
}
|
| 2176 |
+
garbage.push_back(std::move(t));
|
| 2177 |
+
}
|
| 2178 |
+
FAIL() << "Iteration order remained the same across many attempts.";
|
| 2179 |
+
}
|
| 2180 |
+
|
| 2181 |
+
// Verify that pointers are invalidated as soon as a second element is inserted.
|
| 2182 |
+
// This prevents dependency on pointer stability on small tables.
|
| 2183 |
+
TEST(Table, UnstablePointers) {
|
| 2184 |
+
IntTable table;
|
| 2185 |
+
|
| 2186 |
+
const auto addr = [&](int i) {
|
| 2187 |
+
return reinterpret_cast<uintptr_t>(&*table.find(i));
|
| 2188 |
+
};
|
| 2189 |
+
|
| 2190 |
+
table.insert(0);
|
| 2191 |
+
const uintptr_t old_ptr = addr(0);
|
| 2192 |
+
|
| 2193 |
+
// This causes a rehash.
|
| 2194 |
+
table.insert(1);
|
| 2195 |
+
|
| 2196 |
+
EXPECT_NE(old_ptr, addr(0));
|
| 2197 |
+
}
|
| 2198 |
+
|
| 2199 |
+
TEST(TableDeathTest, InvalidIteratorAsserts) {
|
| 2200 |
+
if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
|
| 2201 |
+
GTEST_SKIP() << "Assertions not enabled.";
|
| 2202 |
+
|
| 2203 |
+
IntTable t;
|
| 2204 |
+
// Extra simple "regexp" as regexp support is highly varied across platforms.
|
| 2205 |
+
EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()),
|
| 2206 |
+
"erase.* called on end.. iterator.");
|
| 2207 |
+
typename IntTable::iterator iter;
|
| 2208 |
+
EXPECT_DEATH_IF_SUPPORTED(
|
| 2209 |
+
++iter, "operator.* called on default-constructed iterator.");
|
| 2210 |
+
t.insert(0);
|
| 2211 |
+
iter = t.begin();
|
| 2212 |
+
t.erase(iter);
|
| 2213 |
+
const char* const kErasedDeathMessage =
|
| 2214 |
+
SwisstableGenerationsEnabled()
|
| 2215 |
+
? "operator.* called on invalid iterator.*was likely erased"
|
| 2216 |
+
: "operator.* called on invalid iterator.*might have been "
|
| 2217 |
+
"erased.*config=asan";
|
| 2218 |
+
EXPECT_DEATH_IF_SUPPORTED(++iter, kErasedDeathMessage);
|
| 2219 |
+
}
|
| 2220 |
+
|
| 2221 |
+
// Invalid iterator use can trigger use-after-free in asan/hwasan,
|
| 2222 |
+
// use-of-uninitialized-value in msan, or invalidated iterator assertions.
|
| 2223 |
+
constexpr const char* kInvalidIteratorDeathMessage =
|
| 2224 |
+
"use-after-free|use-of-uninitialized-value|invalidated "
|
| 2225 |
+
"iterator|Invalid iterator|invalid iterator";
|
| 2226 |
+
|
| 2227 |
+
// MSVC doesn't support | in regex.
|
| 2228 |
+
#if defined(_MSC_VER)
|
| 2229 |
+
constexpr bool kMsvc = true;
|
| 2230 |
+
#else
|
| 2231 |
+
constexpr bool kMsvc = false;
|
| 2232 |
+
#endif
|
| 2233 |
+
|
| 2234 |
+
TEST(TableDeathTest, IteratorInvalidAssertsEqualityOperator) {
|
| 2235 |
+
if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
|
| 2236 |
+
GTEST_SKIP() << "Assertions not enabled.";
|
| 2237 |
+
|
| 2238 |
+
IntTable t;
|
| 2239 |
+
t.insert(1);
|
| 2240 |
+
t.insert(2);
|
| 2241 |
+
t.insert(3);
|
| 2242 |
+
auto iter1 = t.begin();
|
| 2243 |
+
auto iter2 = std::next(iter1);
|
| 2244 |
+
ASSERT_NE(iter1, t.end());
|
| 2245 |
+
ASSERT_NE(iter2, t.end());
|
| 2246 |
+
t.erase(iter1);
|
| 2247 |
+
// Extra simple "regexp" as regexp support is highly varied across platforms.
|
| 2248 |
+
const char* const kErasedDeathMessage =
|
| 2249 |
+
SwisstableGenerationsEnabled()
|
| 2250 |
+
? "Invalid iterator comparison.*was likely erased"
|
| 2251 |
+
: "Invalid iterator comparison.*might have been erased.*config=asan";
|
| 2252 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
|
| 2253 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter2 != iter1), kErasedDeathMessage);
|
| 2254 |
+
t.erase(iter2);
|
| 2255 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
|
| 2256 |
+
|
| 2257 |
+
IntTable t1, t2;
|
| 2258 |
+
t1.insert(0);
|
| 2259 |
+
t2.insert(0);
|
| 2260 |
+
iter1 = t1.begin();
|
| 2261 |
+
iter2 = t2.begin();
|
| 2262 |
+
const char* const kContainerDiffDeathMessage =
|
| 2263 |
+
SwisstableGenerationsEnabled()
|
| 2264 |
+
? "Invalid iterator comparison.*iterators from different hashtables"
|
| 2265 |
+
: "Invalid iterator comparison.*may be from different "
|
| 2266 |
+
".*containers.*config=asan";
|
| 2267 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kContainerDiffDeathMessage);
|
| 2268 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter2 == iter1), kContainerDiffDeathMessage);
|
| 2269 |
+
|
| 2270 |
+
for (int i = 0; i < 10; ++i) t1.insert(i);
|
| 2271 |
+
// There should have been a rehash in t1.
|
| 2272 |
+
if (kMsvc) return; // MSVC doesn't support | in regex.
|
| 2273 |
+
|
| 2274 |
+
// NOTE(b/293887834): After rehashing, iterators will contain pointers to
|
| 2275 |
+
// freed memory, which may be detected by ThreadSanitizer.
|
| 2276 |
+
const char* const kRehashedDeathMessage =
|
| 2277 |
+
SwisstableGenerationsEnabled()
|
| 2278 |
+
? kInvalidIteratorDeathMessage
|
| 2279 |
+
: "Invalid iterator comparison.*might have rehashed.*config=asan"
|
| 2280 |
+
"|ThreadSanitizer: heap-use-after-free";
|
| 2281 |
+
EXPECT_DEATH_IF_SUPPORTED(void(iter1 == t1.begin()), kRehashedDeathMessage);
|
| 2282 |
+
}
|
| 2283 |
+
|
| 2284 |
+
#if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
|
| 2285 |
+
TEST(RawHashSamplerTest, Sample) {
|
| 2286 |
+
// Enable the feature even if the prod default is off.
|
| 2287 |
+
SetHashtablezEnabled(true);
|
| 2288 |
+
SetHashtablezSampleParameter(100);
|
| 2289 |
+
|
| 2290 |
+
auto& sampler = GlobalHashtablezSampler();
|
| 2291 |
+
size_t start_size = 0;
|
| 2292 |
+
absl::flat_hash_set<const HashtablezInfo*> preexisting_info;
|
| 2293 |
+
start_size += sampler.Iterate([&](const HashtablezInfo& info) {
|
| 2294 |
+
preexisting_info.insert(&info);
|
| 2295 |
+
++start_size;
|
| 2296 |
+
});
|
| 2297 |
+
|
| 2298 |
+
std::vector<IntTable> tables;
|
| 2299 |
+
for (int i = 0; i < 1000000; ++i) {
|
| 2300 |
+
tables.emplace_back();
|
| 2301 |
+
|
| 2302 |
+
const bool do_reserve = (i % 10 > 5);
|
| 2303 |
+
const bool do_rehash = !do_reserve && (i % 10 > 0);
|
| 2304 |
+
|
| 2305 |
+
if (do_reserve) {
|
| 2306 |
+
// Don't reserve on all tables.
|
| 2307 |
+
tables.back().reserve(10 * (i % 10));
|
| 2308 |
+
}
|
| 2309 |
+
|
| 2310 |
+
tables.back().insert(1);
|
| 2311 |
+
tables.back().insert(i % 5);
|
| 2312 |
+
|
| 2313 |
+
if (do_rehash) {
|
| 2314 |
+
// Rehash some other tables.
|
| 2315 |
+
tables.back().rehash(10 * (i % 10));
|
| 2316 |
+
}
|
| 2317 |
+
}
|
| 2318 |
+
size_t end_size = 0;
|
| 2319 |
+
absl::flat_hash_map<size_t, int> observed_checksums;
|
| 2320 |
+
absl::flat_hash_map<ssize_t, int> reservations;
|
| 2321 |
+
end_size += sampler.Iterate([&](const HashtablezInfo& info) {
|
| 2322 |
+
if (preexisting_info.count(&info) == 0) {
|
| 2323 |
+
observed_checksums[info.hashes_bitwise_xor.load(
|
| 2324 |
+
std::memory_order_relaxed)]++;
|
| 2325 |
+
reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
|
| 2326 |
+
}
|
| 2327 |
+
EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
|
| 2328 |
+
++end_size;
|
| 2329 |
+
});
|
| 2330 |
+
|
| 2331 |
+
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
|
| 2332 |
+
0.01, 0.005);
|
| 2333 |
+
EXPECT_EQ(observed_checksums.size(), 5);
|
| 2334 |
+
for (const auto& [_, count] : observed_checksums) {
|
| 2335 |
+
EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
|
| 2336 |
+
}
|
| 2337 |
+
|
| 2338 |
+
EXPECT_EQ(reservations.size(), 10);
|
| 2339 |
+
for (const auto& [reservation, count] : reservations) {
|
| 2340 |
+
EXPECT_GE(reservation, 0);
|
| 2341 |
+
EXPECT_LT(reservation, 100);
|
| 2342 |
+
|
| 2343 |
+
EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.1, 0.05)
|
| 2344 |
+
<< reservation;
|
| 2345 |
+
}
|
| 2346 |
+
}
|
| 2347 |
+
#endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
|
| 2348 |
+
|
| 2349 |
+
TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
|
| 2350 |
+
// Enable the feature even if the prod default is off.
|
| 2351 |
+
SetHashtablezEnabled(true);
|
| 2352 |
+
SetHashtablezSampleParameter(100);
|
| 2353 |
+
|
| 2354 |
+
auto& sampler = GlobalHashtablezSampler();
|
| 2355 |
+
size_t start_size = 0;
|
| 2356 |
+
start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
|
| 2357 |
+
|
| 2358 |
+
std::vector<CustomAllocIntTable> tables;
|
| 2359 |
+
for (int i = 0; i < 1000000; ++i) {
|
| 2360 |
+
tables.emplace_back();
|
| 2361 |
+
tables.back().insert(1);
|
| 2362 |
+
}
|
| 2363 |
+
size_t end_size = 0;
|
| 2364 |
+
end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
|
| 2365 |
+
|
| 2366 |
+
EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
|
| 2367 |
+
0.00, 0.001);
|
| 2368 |
+
}
|
| 2369 |
+
|
| 2370 |
+
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
|
| 2371 |
+
template <class TableType>
|
| 2372 |
+
class SanitizerTest : public testing::Test {};
|
| 2373 |
+
|
| 2374 |
+
TYPED_TEST_SUITE_P(SanitizerTest);
|
| 2375 |
+
|
| 2376 |
+
TYPED_TEST_P(SanitizerTest, PoisoningUnused) {
|
| 2377 |
+
TypeParam t;
|
| 2378 |
+
for (size_t reserve_size = 2; reserve_size < 1024;
|
| 2379 |
+
reserve_size = reserve_size * 3 / 2) {
|
| 2380 |
+
t.reserve(reserve_size);
|
| 2381 |
+
// Insert something to force an allocation.
|
| 2382 |
+
int64_t& v = *t.insert(0).first;
|
| 2383 |
+
|
| 2384 |
+
// Make sure there is something to test.
|
| 2385 |
+
ASSERT_GT(t.capacity(), 1);
|
| 2386 |
+
|
| 2387 |
+
int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
|
| 2388 |
+
for (size_t i = 0; i < t.capacity(); ++i) {
|
| 2389 |
+
EXPECT_EQ(slots + i != &v, __asan_address_is_poisoned(slots + i)) << i;
|
| 2390 |
+
}
|
| 2391 |
+
}
|
| 2392 |
+
}
|
| 2393 |
+
|
| 2394 |
+
REGISTER_TYPED_TEST_SUITE_P(SanitizerTest, PoisoningUnused);
|
| 2395 |
+
using SanitizerTableTypes = ::testing::Types<IntTable, TransferableIntTable>;
|
| 2396 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSanitizerTest, SanitizerTest,
|
| 2397 |
+
SanitizerTableTypes);
|
| 2398 |
+
|
| 2399 |
+
TEST(Sanitizer, PoisoningOnErase) {
|
| 2400 |
+
IntTable t;
|
| 2401 |
+
int64_t& v = *t.insert(0).first;
|
| 2402 |
+
|
| 2403 |
+
EXPECT_FALSE(__asan_address_is_poisoned(&v));
|
| 2404 |
+
t.erase(0);
|
| 2405 |
+
EXPECT_TRUE(__asan_address_is_poisoned(&v));
|
| 2406 |
+
}
|
| 2407 |
+
#endif // ABSL_HAVE_ADDRESS_SANITIZER
|
| 2408 |
+
|
| 2409 |
+
template <typename T>
|
| 2410 |
+
class AlignOneTest : public ::testing::Test {};
|
| 2411 |
+
using AlignOneTestTypes =
|
| 2412 |
+
::testing::Types<Uint8Table, MinimumAlignmentUint8Table>;
|
| 2413 |
+
TYPED_TEST_SUITE(AlignOneTest, AlignOneTestTypes);
|
| 2414 |
+
|
| 2415 |
+
TYPED_TEST(AlignOneTest, AlignOne) {
|
| 2416 |
+
// We previously had a bug in which we were copying a control byte over the
|
| 2417 |
+
// first slot when alignof(value_type) is 1. We test repeated
|
| 2418 |
+
// insertions/erases and verify that the behavior is correct.
|
| 2419 |
+
TypeParam t;
|
| 2420 |
+
std::unordered_set<uint8_t> verifier; // NOLINT
|
| 2421 |
+
|
| 2422 |
+
// Do repeated insertions/erases from the table.
|
| 2423 |
+
for (int64_t i = 0; i < 100000; ++i) {
|
| 2424 |
+
SCOPED_TRACE(i);
|
| 2425 |
+
const uint8_t u = (i * -i) & 0xFF;
|
| 2426 |
+
auto it = t.find(u);
|
| 2427 |
+
auto verifier_it = verifier.find(u);
|
| 2428 |
+
if (it == t.end()) {
|
| 2429 |
+
ASSERT_EQ(verifier_it, verifier.end());
|
| 2430 |
+
t.insert(u);
|
| 2431 |
+
verifier.insert(u);
|
| 2432 |
+
} else {
|
| 2433 |
+
ASSERT_NE(verifier_it, verifier.end());
|
| 2434 |
+
t.erase(it);
|
| 2435 |
+
verifier.erase(verifier_it);
|
| 2436 |
+
}
|
| 2437 |
+
}
|
| 2438 |
+
|
| 2439 |
+
EXPECT_EQ(t.size(), verifier.size());
|
| 2440 |
+
for (uint8_t u : t) {
|
| 2441 |
+
EXPECT_EQ(verifier.count(u), 1);
|
| 2442 |
+
}
|
| 2443 |
+
}
|
| 2444 |
+
|
| 2445 |
+
TEST(Iterator, InvalidUseCrashesWithSanitizers) {
|
| 2446 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2447 |
+
if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
|
| 2448 |
+
|
| 2449 |
+
IntTable t;
|
| 2450 |
+
// Start with 1 element so that `it` is never an end iterator.
|
| 2451 |
+
t.insert(-1);
|
| 2452 |
+
for (int i = 0; i < 10; ++i) {
|
| 2453 |
+
auto it = t.begin();
|
| 2454 |
+
t.insert(i);
|
| 2455 |
+
EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
|
| 2456 |
+
EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
|
| 2457 |
+
kInvalidIteratorDeathMessage);
|
| 2458 |
+
}
|
| 2459 |
+
}
|
| 2460 |
+
|
| 2461 |
+
TEST(Iterator, InvalidUseWithReserveCrashesWithSanitizers) {
|
| 2462 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2463 |
+
if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
|
| 2464 |
+
|
| 2465 |
+
IntTable t;
|
| 2466 |
+
t.reserve(10);
|
| 2467 |
+
t.insert(0);
|
| 2468 |
+
auto it = t.begin();
|
| 2469 |
+
// Reserved growth can't rehash.
|
| 2470 |
+
for (int i = 1; i < 10; ++i) {
|
| 2471 |
+
t.insert(i);
|
| 2472 |
+
EXPECT_EQ(*it, 0);
|
| 2473 |
+
}
|
| 2474 |
+
// ptr will become invalidated on rehash.
|
| 2475 |
+
const int64_t* ptr = &*it;
|
| 2476 |
+
(void)ptr;
|
| 2477 |
+
|
| 2478 |
+
// erase decreases size but does not decrease reserved growth so the next
|
| 2479 |
+
// insertion still invalidates iterators.
|
| 2480 |
+
t.erase(0);
|
| 2481 |
+
// The first insert after reserved growth is 0 is guaranteed to rehash when
|
| 2482 |
+
// generations are enabled.
|
| 2483 |
+
t.insert(10);
|
| 2484 |
+
EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
|
| 2485 |
+
EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
|
| 2486 |
+
kInvalidIteratorDeathMessage);
|
| 2487 |
+
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
|
| 2488 |
+
EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
|
| 2489 |
+
#endif
|
| 2490 |
+
}
|
| 2491 |
+
|
| 2492 |
+
TEST(Iterator, InvalidUseWithMoveCrashesWithSanitizers) {
|
| 2493 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2494 |
+
if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
|
| 2495 |
+
|
| 2496 |
+
IntTable t1, t2;
|
| 2497 |
+
t1.insert(1);
|
| 2498 |
+
auto it = t1.begin();
|
| 2499 |
+
// ptr will become invalidated on rehash.
|
| 2500 |
+
const int64_t* ptr = &*it;
|
| 2501 |
+
(void)ptr;
|
| 2502 |
+
|
| 2503 |
+
t2 = std::move(t1);
|
| 2504 |
+
EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
|
| 2505 |
+
EXPECT_DEATH_IF_SUPPORTED(void(it == t2.begin()),
|
| 2506 |
+
kInvalidIteratorDeathMessage);
|
| 2507 |
+
#ifdef ABSL_HAVE_ADDRESS_SANITIZER
|
| 2508 |
+
EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
|
| 2509 |
+
#endif
|
| 2510 |
+
}
|
| 2511 |
+
|
| 2512 |
+
TEST(Table, ReservedGrowthUpdatesWhenTableDoesntGrow) {
|
| 2513 |
+
IntTable t;
|
| 2514 |
+
for (int i = 0; i < 8; ++i) t.insert(i);
|
| 2515 |
+
// Want to insert twice without invalidating iterators so reserve.
|
| 2516 |
+
const size_t cap = t.capacity();
|
| 2517 |
+
t.reserve(t.size() + 2);
|
| 2518 |
+
// We want to be testing the case in which the reserve doesn't grow the table.
|
| 2519 |
+
ASSERT_EQ(cap, t.capacity());
|
| 2520 |
+
auto it = t.find(0);
|
| 2521 |
+
t.insert(100);
|
| 2522 |
+
t.insert(200);
|
| 2523 |
+
// `it` shouldn't have been invalidated.
|
| 2524 |
+
EXPECT_EQ(*it, 0);
|
| 2525 |
+
}
|
| 2526 |
+
|
| 2527 |
+
TEST(Table, EraseBeginEndResetsReservedGrowth) {
|
| 2528 |
+
bool frozen = false;
|
| 2529 |
+
BadHashFreezableIntTable t{FreezableAlloc<int64_t>(&frozen)};
|
| 2530 |
+
t.reserve(100);
|
| 2531 |
+
const size_t cap = t.capacity();
|
| 2532 |
+
frozen = true; // no further allocs allowed
|
| 2533 |
+
|
| 2534 |
+
for (int i = 0; i < 10; ++i) {
|
| 2535 |
+
// Create a long run (hash function returns constant).
|
| 2536 |
+
for (int j = 0; j < 100; ++j) t.insert(j);
|
| 2537 |
+
// Erase elements from the middle of the long run, which creates tombstones.
|
| 2538 |
+
for (int j = 30; j < 60; ++j) t.erase(j);
|
| 2539 |
+
EXPECT_EQ(t.size(), 70);
|
| 2540 |
+
EXPECT_EQ(t.capacity(), cap);
|
| 2541 |
+
ASSERT_EQ(RawHashSetTestOnlyAccess::CountTombstones(t), 30);
|
| 2542 |
+
|
| 2543 |
+
t.erase(t.begin(), t.end());
|
| 2544 |
+
|
| 2545 |
+
EXPECT_EQ(t.size(), 0);
|
| 2546 |
+
EXPECT_EQ(t.capacity(), cap);
|
| 2547 |
+
ASSERT_EQ(RawHashSetTestOnlyAccess::CountTombstones(t), 0);
|
| 2548 |
+
}
|
| 2549 |
+
}
|
| 2550 |
+
|
| 2551 |
+
TEST(Table, GenerationInfoResetsOnClear) {
|
| 2552 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2553 |
+
if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
|
| 2554 |
+
|
| 2555 |
+
IntTable t;
|
| 2556 |
+
for (int i = 0; i < 1000; ++i) t.insert(i);
|
| 2557 |
+
t.reserve(t.size() + 100);
|
| 2558 |
+
|
| 2559 |
+
t.clear();
|
| 2560 |
+
|
| 2561 |
+
t.insert(0);
|
| 2562 |
+
auto it = t.begin();
|
| 2563 |
+
t.insert(1);
|
| 2564 |
+
EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
|
| 2565 |
+
}
|
| 2566 |
+
|
| 2567 |
+
TEST(Table, InvalidReferenceUseCrashesWithSanitizers) {
|
| 2568 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2569 |
+
#ifdef ABSL_HAVE_MEMORY_SANITIZER
|
| 2570 |
+
GTEST_SKIP() << "MSan fails to detect some of these rehashes.";
|
| 2571 |
+
#endif
|
| 2572 |
+
|
| 2573 |
+
IntTable t;
|
| 2574 |
+
t.insert(0);
|
| 2575 |
+
// Rehashing is guaranteed on every insertion while capacity is less than
|
| 2576 |
+
// RehashProbabilityConstant().
|
| 2577 |
+
int64_t i = 0;
|
| 2578 |
+
while (t.capacity() <= RehashProbabilityConstant()) {
|
| 2579 |
+
// ptr will become invalidated on rehash.
|
| 2580 |
+
const int64_t* ptr = &*t.begin();
|
| 2581 |
+
t.insert(++i);
|
| 2582 |
+
EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "use-after-free") << i;
|
| 2583 |
+
}
|
| 2584 |
+
}
|
| 2585 |
+
|
| 2586 |
+
TEST(Iterator, InvalidComparisonDifferentTables) {
|
| 2587 |
+
if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
|
| 2588 |
+
|
| 2589 |
+
IntTable t1, t2;
|
| 2590 |
+
IntTable::iterator default_constructed_iter;
|
| 2591 |
+
// We randomly use one of N empty generations for generations from empty
|
| 2592 |
+
// hashtables. In general, we won't always detect when iterators from
|
| 2593 |
+
// different empty hashtables are compared, but in this test case, we
|
| 2594 |
+
// should deterministically detect the error due to our randomness yielding
|
| 2595 |
+
// consecutive random generations.
|
| 2596 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == t2.end()),
|
| 2597 |
+
"Invalid iterator comparison.*empty hashtables");
|
| 2598 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == default_constructed_iter),
|
| 2599 |
+
"Invalid iterator comparison.*default-constructed");
|
| 2600 |
+
t1.insert(0);
|
| 2601 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
|
| 2602 |
+
"Invalid iterator comparison.*empty hashtable");
|
| 2603 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == default_constructed_iter),
|
| 2604 |
+
"Invalid iterator comparison.*default-constructed");
|
| 2605 |
+
t2.insert(0);
|
| 2606 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
|
| 2607 |
+
"Invalid iterator comparison.*end.. iterator");
|
| 2608 |
+
EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.begin()),
|
| 2609 |
+
"Invalid iterator comparison.*non-end");
|
| 2610 |
+
}
|
| 2611 |
+
|
| 2612 |
+
template <typename Alloc>
|
| 2613 |
+
using RawHashSetAlloc = raw_hash_set<IntPolicy, hash_default_hash<int64_t>,
|
| 2614 |
+
std::equal_to<int64_t>, Alloc>;
|
| 2615 |
+
|
| 2616 |
+
TEST(Table, AllocatorPropagation) { TestAllocPropagation<RawHashSetAlloc>(); }
|
| 2617 |
+
|
| 2618 |
+
struct CountedHash {
|
| 2619 |
+
size_t operator()(int value) const {
|
| 2620 |
+
++count;
|
| 2621 |
+
return static_cast<size_t>(value);
|
| 2622 |
+
}
|
| 2623 |
+
mutable int count = 0;
|
| 2624 |
+
};
|
| 2625 |
+
|
| 2626 |
+
struct CountedHashIntTable
|
| 2627 |
+
: raw_hash_set<IntPolicy, CountedHash, std::equal_to<int>,
|
| 2628 |
+
std::allocator<int>> {
|
| 2629 |
+
using Base = typename CountedHashIntTable::raw_hash_set;
|
| 2630 |
+
using Base::Base;
|
| 2631 |
+
};
|
| 2632 |
+
|
| 2633 |
+
TEST(Table, CountedHash) {
|
| 2634 |
+
// Verify that raw_hash_set does not compute redundant hashes.
|
| 2635 |
+
#ifdef NDEBUG
|
| 2636 |
+
constexpr bool kExpectMinimumHashes = true;
|
| 2637 |
+
#else
|
| 2638 |
+
constexpr bool kExpectMinimumHashes = false;
|
| 2639 |
+
#endif
|
| 2640 |
+
if (!kExpectMinimumHashes) {
|
| 2641 |
+
GTEST_SKIP() << "Only run under NDEBUG: `assert` statements may cause "
|
| 2642 |
+
"redundant hashing.";
|
| 2643 |
+
}
|
| 2644 |
+
|
| 2645 |
+
using Table = CountedHashIntTable;
|
| 2646 |
+
auto HashCount = [](const Table& t) { return t.hash_function().count; };
|
| 2647 |
+
{
|
| 2648 |
+
Table t;
|
| 2649 |
+
EXPECT_EQ(HashCount(t), 0);
|
| 2650 |
+
}
|
| 2651 |
+
{
|
| 2652 |
+
Table t;
|
| 2653 |
+
t.insert(1);
|
| 2654 |
+
EXPECT_EQ(HashCount(t), 1);
|
| 2655 |
+
t.erase(1);
|
| 2656 |
+
EXPECT_EQ(HashCount(t), 2);
|
| 2657 |
+
}
|
| 2658 |
+
{
|
| 2659 |
+
Table t;
|
| 2660 |
+
t.insert(3);
|
| 2661 |
+
EXPECT_EQ(HashCount(t), 1);
|
| 2662 |
+
auto node = t.extract(3);
|
| 2663 |
+
EXPECT_EQ(HashCount(t), 2);
|
| 2664 |
+
t.insert(std::move(node));
|
| 2665 |
+
EXPECT_EQ(HashCount(t), 3);
|
| 2666 |
+
}
|
| 2667 |
+
{
|
| 2668 |
+
Table t;
|
| 2669 |
+
t.emplace(5);
|
| 2670 |
+
EXPECT_EQ(HashCount(t), 1);
|
| 2671 |
+
}
|
| 2672 |
+
{
|
| 2673 |
+
Table src;
|
| 2674 |
+
src.insert(7);
|
| 2675 |
+
Table dst;
|
| 2676 |
+
dst.merge(src);
|
| 2677 |
+
EXPECT_EQ(HashCount(dst), 1);
|
| 2678 |
+
}
|
| 2679 |
+
}
|
| 2680 |
+
|
| 2681 |
+
} // namespace
|
| 2682 |
+
} // namespace container_internal
|
| 2683 |
+
ABSL_NAMESPACE_END
|
| 2684 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_map_constructor_test.h
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
|
| 17 |
+
|
| 18 |
+
#include <algorithm>
|
| 19 |
+
#include <unordered_map>
|
| 20 |
+
#include <vector>
|
| 21 |
+
|
| 22 |
+
#include "gmock/gmock.h"
|
| 23 |
+
#include "gtest/gtest.h"
|
| 24 |
+
#include "absl/container/internal/hash_generator_testing.h"
|
| 25 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 26 |
+
|
| 27 |
+
namespace absl {
|
| 28 |
+
ABSL_NAMESPACE_BEGIN
|
| 29 |
+
namespace container_internal {
|
| 30 |
+
|
| 31 |
+
template <class UnordMap>
|
| 32 |
+
class ConstructorTest : public ::testing::Test {};
|
| 33 |
+
|
| 34 |
+
TYPED_TEST_SUITE_P(ConstructorTest);
|
| 35 |
+
|
| 36 |
+
TYPED_TEST_P(ConstructorTest, NoArgs) {
|
| 37 |
+
TypeParam m;
|
| 38 |
+
EXPECT_TRUE(m.empty());
|
| 39 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
TYPED_TEST_P(ConstructorTest, BucketCount) {
|
| 43 |
+
TypeParam m(123);
|
| 44 |
+
EXPECT_TRUE(m.empty());
|
| 45 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 46 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHash) {
|
| 50 |
+
using H = typename TypeParam::hasher;
|
| 51 |
+
H hasher;
|
| 52 |
+
TypeParam m(123, hasher);
|
| 53 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 54 |
+
EXPECT_TRUE(m.empty());
|
| 55 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 56 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) {
|
| 60 |
+
using H = typename TypeParam::hasher;
|
| 61 |
+
using E = typename TypeParam::key_equal;
|
| 62 |
+
H hasher;
|
| 63 |
+
E equal;
|
| 64 |
+
TypeParam m(123, hasher, equal);
|
| 65 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 66 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 67 |
+
EXPECT_TRUE(m.empty());
|
| 68 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 69 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) {
|
| 73 |
+
using H = typename TypeParam::hasher;
|
| 74 |
+
using E = typename TypeParam::key_equal;
|
| 75 |
+
using A = typename TypeParam::allocator_type;
|
| 76 |
+
H hasher;
|
| 77 |
+
E equal;
|
| 78 |
+
A alloc(0);
|
| 79 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 80 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 81 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 82 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 83 |
+
EXPECT_TRUE(m.empty());
|
| 84 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 85 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
template <typename T>
|
| 89 |
+
struct is_std_unordered_map : std::false_type {};
|
| 90 |
+
|
| 91 |
+
template <typename... T>
|
| 92 |
+
struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type {};
|
| 93 |
+
|
| 94 |
+
#if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17)
|
| 95 |
+
using has_cxx14_std_apis = std::true_type;
|
| 96 |
+
#else
|
| 97 |
+
using has_cxx14_std_apis = std::false_type;
|
| 98 |
+
#endif
|
| 99 |
+
|
| 100 |
+
template <typename T>
|
| 101 |
+
using expect_cxx14_apis =
|
| 102 |
+
absl::disjunction<absl::negation<is_std_unordered_map<T>>,
|
| 103 |
+
has_cxx14_std_apis>;
|
| 104 |
+
|
| 105 |
+
template <typename TypeParam>
|
| 106 |
+
void BucketCountAllocTest(std::false_type) {}
|
| 107 |
+
|
| 108 |
+
template <typename TypeParam>
|
| 109 |
+
void BucketCountAllocTest(std::true_type) {
|
| 110 |
+
using A = typename TypeParam::allocator_type;
|
| 111 |
+
A alloc(0);
|
| 112 |
+
TypeParam m(123, alloc);
|
| 113 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 114 |
+
EXPECT_TRUE(m.empty());
|
| 115 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 116 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
|
| 120 |
+
BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
template <typename TypeParam>
|
| 124 |
+
void BucketCountHashAllocTest(std::false_type) {}
|
| 125 |
+
|
| 126 |
+
template <typename TypeParam>
|
| 127 |
+
void BucketCountHashAllocTest(std::true_type) {
|
| 128 |
+
using H = typename TypeParam::hasher;
|
| 129 |
+
using A = typename TypeParam::allocator_type;
|
| 130 |
+
H hasher;
|
| 131 |
+
A alloc(0);
|
| 132 |
+
TypeParam m(123, hasher, alloc);
|
| 133 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 134 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 135 |
+
EXPECT_TRUE(m.empty());
|
| 136 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 137 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
|
| 141 |
+
BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
|
| 145 |
+
using has_alloc_std_constructors = std::true_type;
|
| 146 |
+
#else
|
| 147 |
+
using has_alloc_std_constructors = std::false_type;
|
| 148 |
+
#endif
|
| 149 |
+
|
| 150 |
+
template <typename T>
|
| 151 |
+
using expect_alloc_constructors =
|
| 152 |
+
absl::disjunction<absl::negation<is_std_unordered_map<T>>,
|
| 153 |
+
has_alloc_std_constructors>;
|
| 154 |
+
|
| 155 |
+
template <typename TypeParam>
|
| 156 |
+
void AllocTest(std::false_type) {}
|
| 157 |
+
|
| 158 |
+
template <typename TypeParam>
|
| 159 |
+
void AllocTest(std::true_type) {
|
| 160 |
+
using A = typename TypeParam::allocator_type;
|
| 161 |
+
A alloc(0);
|
| 162 |
+
TypeParam m(alloc);
|
| 163 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 164 |
+
EXPECT_TRUE(m.empty());
|
| 165 |
+
EXPECT_THAT(m, ::testing::UnorderedElementsAre());
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
TYPED_TEST_P(ConstructorTest, Alloc) {
|
| 169 |
+
AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
|
| 173 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 174 |
+
using H = typename TypeParam::hasher;
|
| 175 |
+
using E = typename TypeParam::key_equal;
|
| 176 |
+
using A = typename TypeParam::allocator_type;
|
| 177 |
+
H hasher;
|
| 178 |
+
E equal;
|
| 179 |
+
A alloc(0);
|
| 180 |
+
std::vector<T> values;
|
| 181 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 182 |
+
hash_internal::UniqueGenerator<T>());
|
| 183 |
+
TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
|
| 184 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 185 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 186 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 187 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 188 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
template <typename TypeParam>
|
| 192 |
+
void InputIteratorBucketAllocTest(std::false_type) {}
|
| 193 |
+
|
| 194 |
+
template <typename TypeParam>
|
| 195 |
+
void InputIteratorBucketAllocTest(std::true_type) {
|
| 196 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 197 |
+
using A = typename TypeParam::allocator_type;
|
| 198 |
+
A alloc(0);
|
| 199 |
+
std::vector<T> values;
|
| 200 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 201 |
+
hash_internal::UniqueGenerator<T>());
|
| 202 |
+
TypeParam m(values.begin(), values.end(), 123, alloc);
|
| 203 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 204 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 205 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
|
| 209 |
+
InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
template <typename TypeParam>
|
| 213 |
+
void InputIteratorBucketHashAllocTest(std::false_type) {}
|
| 214 |
+
|
| 215 |
+
template <typename TypeParam>
|
| 216 |
+
void InputIteratorBucketHashAllocTest(std::true_type) {
|
| 217 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 218 |
+
using H = typename TypeParam::hasher;
|
| 219 |
+
using A = typename TypeParam::allocator_type;
|
| 220 |
+
H hasher;
|
| 221 |
+
A alloc(0);
|
| 222 |
+
std::vector<T> values;
|
| 223 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 224 |
+
hash_internal::UniqueGenerator<T>());
|
| 225 |
+
TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
|
| 226 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 227 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 228 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 229 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
|
| 233 |
+
InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
TYPED_TEST_P(ConstructorTest, CopyConstructor) {
|
| 237 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 238 |
+
using H = typename TypeParam::hasher;
|
| 239 |
+
using E = typename TypeParam::key_equal;
|
| 240 |
+
using A = typename TypeParam::allocator_type;
|
| 241 |
+
H hasher;
|
| 242 |
+
E equal;
|
| 243 |
+
A alloc(0);
|
| 244 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 245 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 246 |
+
for (size_t i = 0; i != 10; ++i) m.insert(gen());
|
| 247 |
+
TypeParam n(m);
|
| 248 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 249 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 250 |
+
EXPECT_EQ(m.get_allocator(), n.get_allocator());
|
| 251 |
+
EXPECT_EQ(m, n);
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
template <typename TypeParam>
|
| 255 |
+
void CopyConstructorAllocTest(std::false_type) {}
|
| 256 |
+
|
| 257 |
+
template <typename TypeParam>
|
| 258 |
+
void CopyConstructorAllocTest(std::true_type) {
|
| 259 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 260 |
+
using H = typename TypeParam::hasher;
|
| 261 |
+
using E = typename TypeParam::key_equal;
|
| 262 |
+
using A = typename TypeParam::allocator_type;
|
| 263 |
+
H hasher;
|
| 264 |
+
E equal;
|
| 265 |
+
A alloc(0);
|
| 266 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 267 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 268 |
+
for (size_t i = 0; i != 10; ++i) m.insert(gen());
|
| 269 |
+
TypeParam n(m, A(11));
|
| 270 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 271 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 272 |
+
EXPECT_NE(m.get_allocator(), n.get_allocator());
|
| 273 |
+
EXPECT_EQ(m, n);
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
|
| 277 |
+
CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
// TODO(alkis): Test non-propagating allocators on copy constructors.
|
| 281 |
+
|
| 282 |
+
TYPED_TEST_P(ConstructorTest, MoveConstructor) {
|
| 283 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 284 |
+
using H = typename TypeParam::hasher;
|
| 285 |
+
using E = typename TypeParam::key_equal;
|
| 286 |
+
using A = typename TypeParam::allocator_type;
|
| 287 |
+
H hasher;
|
| 288 |
+
E equal;
|
| 289 |
+
A alloc(0);
|
| 290 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 291 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 292 |
+
for (size_t i = 0; i != 10; ++i) m.insert(gen());
|
| 293 |
+
TypeParam t(m);
|
| 294 |
+
TypeParam n(std::move(t));
|
| 295 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 296 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 297 |
+
EXPECT_EQ(m.get_allocator(), n.get_allocator());
|
| 298 |
+
EXPECT_EQ(m, n);
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
template <typename TypeParam>
|
| 302 |
+
void MoveConstructorAllocTest(std::false_type) {}
|
| 303 |
+
|
| 304 |
+
template <typename TypeParam>
|
| 305 |
+
void MoveConstructorAllocTest(std::true_type) {
|
| 306 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 307 |
+
using H = typename TypeParam::hasher;
|
| 308 |
+
using E = typename TypeParam::key_equal;
|
| 309 |
+
using A = typename TypeParam::allocator_type;
|
| 310 |
+
H hasher;
|
| 311 |
+
E equal;
|
| 312 |
+
A alloc(0);
|
| 313 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 314 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 315 |
+
for (size_t i = 0; i != 10; ++i) m.insert(gen());
|
| 316 |
+
TypeParam t(m);
|
| 317 |
+
TypeParam n(std::move(t), A(1));
|
| 318 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 319 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 320 |
+
EXPECT_NE(m.get_allocator(), n.get_allocator());
|
| 321 |
+
EXPECT_EQ(m, n);
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
|
| 325 |
+
MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
// TODO(alkis): Test non-propagating allocators on move constructors.
|
| 329 |
+
|
| 330 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
|
| 331 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 332 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 333 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 334 |
+
using H = typename TypeParam::hasher;
|
| 335 |
+
using E = typename TypeParam::key_equal;
|
| 336 |
+
using A = typename TypeParam::allocator_type;
|
| 337 |
+
H hasher;
|
| 338 |
+
E equal;
|
| 339 |
+
A alloc(0);
|
| 340 |
+
TypeParam m(values, 123, hasher, equal, alloc);
|
| 341 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 342 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 343 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 344 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 345 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
template <typename TypeParam>
|
| 349 |
+
void InitializerListBucketAllocTest(std::false_type) {}
|
| 350 |
+
|
| 351 |
+
template <typename TypeParam>
|
| 352 |
+
void InitializerListBucketAllocTest(std::true_type) {
|
| 353 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 354 |
+
using A = typename TypeParam::allocator_type;
|
| 355 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 356 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 357 |
+
A alloc(0);
|
| 358 |
+
TypeParam m(values, 123, alloc);
|
| 359 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 360 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 361 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
|
| 365 |
+
InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
template <typename TypeParam>
|
| 369 |
+
void InitializerListBucketHashAllocTest(std::false_type) {}
|
| 370 |
+
|
| 371 |
+
template <typename TypeParam>
|
| 372 |
+
void InitializerListBucketHashAllocTest(std::true_type) {
|
| 373 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 374 |
+
using H = typename TypeParam::hasher;
|
| 375 |
+
using A = typename TypeParam::allocator_type;
|
| 376 |
+
H hasher;
|
| 377 |
+
A alloc(0);
|
| 378 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 379 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 380 |
+
TypeParam m(values, 123, hasher, alloc);
|
| 381 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 382 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 383 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 384 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
|
| 388 |
+
InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
TYPED_TEST_P(ConstructorTest, Assignment) {
|
| 392 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 393 |
+
using H = typename TypeParam::hasher;
|
| 394 |
+
using E = typename TypeParam::key_equal;
|
| 395 |
+
using A = typename TypeParam::allocator_type;
|
| 396 |
+
H hasher;
|
| 397 |
+
E equal;
|
| 398 |
+
A alloc(0);
|
| 399 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 400 |
+
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
|
| 401 |
+
TypeParam n;
|
| 402 |
+
n = m;
|
| 403 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 404 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 405 |
+
EXPECT_EQ(m, n);
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
// TODO(alkis): Test [non-]propagating allocators on move/copy assignments
|
| 409 |
+
// (it depends on traits).
|
| 410 |
+
|
| 411 |
+
TYPED_TEST_P(ConstructorTest, MoveAssignment) {
|
| 412 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 413 |
+
using H = typename TypeParam::hasher;
|
| 414 |
+
using E = typename TypeParam::key_equal;
|
| 415 |
+
using A = typename TypeParam::allocator_type;
|
| 416 |
+
H hasher;
|
| 417 |
+
E equal;
|
| 418 |
+
A alloc(0);
|
| 419 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 420 |
+
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
|
| 421 |
+
TypeParam t(m);
|
| 422 |
+
TypeParam n;
|
| 423 |
+
n = std::move(t);
|
| 424 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 425 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 426 |
+
EXPECT_EQ(m, n);
|
| 427 |
+
}
|
| 428 |
+
|
| 429 |
+
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
|
| 430 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 431 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 432 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 433 |
+
TypeParam m;
|
| 434 |
+
m = values;
|
| 435 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
|
| 439 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 440 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 441 |
+
TypeParam m({gen(), gen(), gen()});
|
| 442 |
+
TypeParam n({gen()});
|
| 443 |
+
n = m;
|
| 444 |
+
EXPECT_EQ(m, n);
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
|
| 448 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 449 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 450 |
+
TypeParam m({gen(), gen(), gen()});
|
| 451 |
+
TypeParam t(m);
|
| 452 |
+
TypeParam n({gen()});
|
| 453 |
+
n = std::move(t);
|
| 454 |
+
EXPECT_EQ(m, n);
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
|
| 458 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 459 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 460 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 461 |
+
TypeParam m;
|
| 462 |
+
m = values;
|
| 463 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
|
| 467 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 468 |
+
hash_internal::UniqueGenerator<T> gen;
|
| 469 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 470 |
+
TypeParam m(values);
|
| 471 |
+
m = *&m; // Avoid -Wself-assign
|
| 472 |
+
EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values));
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
// We cannot test self move as standard states that it leaves standard
|
| 476 |
+
// containers in unspecified state (and in practice in causes memory-leak
|
| 477 |
+
// according to heap-checker!).
|
| 478 |
+
|
| 479 |
+
REGISTER_TYPED_TEST_SUITE_P(
|
| 480 |
+
ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
|
| 481 |
+
BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
|
| 482 |
+
InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
|
| 483 |
+
InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
|
| 484 |
+
MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
|
| 485 |
+
InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment,
|
| 486 |
+
MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
|
| 487 |
+
MoveAssignmentOverwritesExisting,
|
| 488 |
+
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
|
| 489 |
+
|
| 490 |
+
} // namespace container_internal
|
| 491 |
+
ABSL_NAMESPACE_END
|
| 492 |
+
} // namespace absl
|
| 493 |
+
|
| 494 |
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_map_members_test.h
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2019 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
|
| 17 |
+
|
| 18 |
+
#include <type_traits>
|
| 19 |
+
#include "gmock/gmock.h"
|
| 20 |
+
#include "gtest/gtest.h"
|
| 21 |
+
#include "absl/meta/type_traits.h"
|
| 22 |
+
|
| 23 |
+
namespace absl {
|
| 24 |
+
ABSL_NAMESPACE_BEGIN
|
| 25 |
+
namespace container_internal {
|
| 26 |
+
|
| 27 |
+
template <class UnordMap>
|
| 28 |
+
class MembersTest : public ::testing::Test {};
|
| 29 |
+
|
| 30 |
+
TYPED_TEST_SUITE_P(MembersTest);
|
| 31 |
+
|
| 32 |
+
template <typename T>
|
| 33 |
+
void UseType() {}
|
| 34 |
+
|
| 35 |
+
TYPED_TEST_P(MembersTest, Typedefs) {
|
| 36 |
+
EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type,
|
| 37 |
+
typename TypeParam::mapped_type>,
|
| 38 |
+
typename TypeParam::value_type>()));
|
| 39 |
+
EXPECT_TRUE((absl::conjunction<
|
| 40 |
+
absl::negation<std::is_signed<typename TypeParam::size_type>>,
|
| 41 |
+
std::is_integral<typename TypeParam::size_type>>()));
|
| 42 |
+
EXPECT_TRUE((absl::conjunction<
|
| 43 |
+
std::is_signed<typename TypeParam::difference_type>,
|
| 44 |
+
std::is_integral<typename TypeParam::difference_type>>()));
|
| 45 |
+
EXPECT_TRUE((std::is_convertible<
|
| 46 |
+
decltype(std::declval<const typename TypeParam::hasher&>()(
|
| 47 |
+
std::declval<const typename TypeParam::key_type&>())),
|
| 48 |
+
size_t>()));
|
| 49 |
+
EXPECT_TRUE((std::is_convertible<
|
| 50 |
+
decltype(std::declval<const typename TypeParam::key_equal&>()(
|
| 51 |
+
std::declval<const typename TypeParam::key_type&>(),
|
| 52 |
+
std::declval<const typename TypeParam::key_type&>())),
|
| 53 |
+
bool>()));
|
| 54 |
+
EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type,
|
| 55 |
+
typename TypeParam::value_type>()));
|
| 56 |
+
EXPECT_TRUE((std::is_same<typename TypeParam::value_type&,
|
| 57 |
+
typename TypeParam::reference>()));
|
| 58 |
+
EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&,
|
| 59 |
+
typename TypeParam::const_reference>()));
|
| 60 |
+
EXPECT_TRUE((std::is_same<typename std::allocator_traits<
|
| 61 |
+
typename TypeParam::allocator_type>::pointer,
|
| 62 |
+
typename TypeParam::pointer>()));
|
| 63 |
+
EXPECT_TRUE(
|
| 64 |
+
(std::is_same<typename std::allocator_traits<
|
| 65 |
+
typename TypeParam::allocator_type>::const_pointer,
|
| 66 |
+
typename TypeParam::const_pointer>()));
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
TYPED_TEST_P(MembersTest, SimpleFunctions) {
|
| 70 |
+
EXPECT_GT(TypeParam().max_size(), 0);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
TYPED_TEST_P(MembersTest, BeginEnd) {
|
| 74 |
+
TypeParam t = {typename TypeParam::value_type{}};
|
| 75 |
+
EXPECT_EQ(t.begin(), t.cbegin());
|
| 76 |
+
EXPECT_EQ(t.end(), t.cend());
|
| 77 |
+
EXPECT_NE(t.begin(), t.end());
|
| 78 |
+
EXPECT_NE(t.cbegin(), t.cend());
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd);
|
| 82 |
+
|
| 83 |
+
} // namespace container_internal
|
| 84 |
+
ABSL_NAMESPACE_END
|
| 85 |
+
} // namespace absl
|
| 86 |
+
|
| 87 |
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_map_test.cc
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include <memory>
|
| 16 |
+
#include <unordered_map>
|
| 17 |
+
|
| 18 |
+
#include "absl/container/internal/unordered_map_constructor_test.h"
|
| 19 |
+
#include "absl/container/internal/unordered_map_lookup_test.h"
|
| 20 |
+
#include "absl/container/internal/unordered_map_members_test.h"
|
| 21 |
+
#include "absl/container/internal/unordered_map_modifiers_test.h"
|
| 22 |
+
|
| 23 |
+
namespace absl {
|
| 24 |
+
ABSL_NAMESPACE_BEGIN
|
| 25 |
+
namespace container_internal {
|
| 26 |
+
namespace {
|
| 27 |
+
|
| 28 |
+
using MapTypes = ::testing::Types<
|
| 29 |
+
std::unordered_map<int, int, StatefulTestingHash, StatefulTestingEqual,
|
| 30 |
+
Alloc<std::pair<const int, int>>>,
|
| 31 |
+
std::unordered_map<std::string, std::string, StatefulTestingHash,
|
| 32 |
+
StatefulTestingEqual,
|
| 33 |
+
Alloc<std::pair<const std::string, std::string>>>>;
|
| 34 |
+
|
| 35 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ConstructorTest, MapTypes);
|
| 36 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, LookupTest, MapTypes);
|
| 37 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, MembersTest, MapTypes);
|
| 38 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, ModifiersTest, MapTypes);
|
| 39 |
+
|
| 40 |
+
using UniquePtrMapTypes = ::testing::Types<std::unordered_map<
|
| 41 |
+
int, std::unique_ptr<int>, StatefulTestingHash, StatefulTestingEqual,
|
| 42 |
+
Alloc<std::pair<const int, std::unique_ptr<int>>>>>;
|
| 43 |
+
|
| 44 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedMap, UniquePtrModifiersTest,
|
| 45 |
+
UniquePtrMapTypes);
|
| 46 |
+
|
| 47 |
+
} // namespace
|
| 48 |
+
} // namespace container_internal
|
| 49 |
+
ABSL_NAMESPACE_END
|
| 50 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_set_constructor_test.h
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
|
| 17 |
+
|
| 18 |
+
#include <algorithm>
|
| 19 |
+
#include <unordered_set>
|
| 20 |
+
#include <vector>
|
| 21 |
+
|
| 22 |
+
#include "gmock/gmock.h"
|
| 23 |
+
#include "gtest/gtest.h"
|
| 24 |
+
#include "absl/container/internal/hash_generator_testing.h"
|
| 25 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 26 |
+
#include "absl/meta/type_traits.h"
|
| 27 |
+
|
| 28 |
+
namespace absl {
|
| 29 |
+
ABSL_NAMESPACE_BEGIN
|
| 30 |
+
namespace container_internal {
|
| 31 |
+
|
| 32 |
+
template <class UnordMap>
|
| 33 |
+
class ConstructorTest : public ::testing::Test {};
|
| 34 |
+
|
| 35 |
+
TYPED_TEST_SUITE_P(ConstructorTest);
|
| 36 |
+
|
| 37 |
+
TYPED_TEST_P(ConstructorTest, NoArgs) {
|
| 38 |
+
TypeParam m;
|
| 39 |
+
EXPECT_TRUE(m.empty());
|
| 40 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
TYPED_TEST_P(ConstructorTest, BucketCount) {
|
| 44 |
+
TypeParam m(123);
|
| 45 |
+
EXPECT_TRUE(m.empty());
|
| 46 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 47 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHash) {
|
| 51 |
+
using H = typename TypeParam::hasher;
|
| 52 |
+
H hasher;
|
| 53 |
+
TypeParam m(123, hasher);
|
| 54 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 55 |
+
EXPECT_TRUE(m.empty());
|
| 56 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 57 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) {
|
| 61 |
+
using H = typename TypeParam::hasher;
|
| 62 |
+
using E = typename TypeParam::key_equal;
|
| 63 |
+
H hasher;
|
| 64 |
+
E equal;
|
| 65 |
+
TypeParam m(123, hasher, equal);
|
| 66 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 67 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 68 |
+
EXPECT_TRUE(m.empty());
|
| 69 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 70 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) {
|
| 74 |
+
using H = typename TypeParam::hasher;
|
| 75 |
+
using E = typename TypeParam::key_equal;
|
| 76 |
+
using A = typename TypeParam::allocator_type;
|
| 77 |
+
H hasher;
|
| 78 |
+
E equal;
|
| 79 |
+
A alloc(0);
|
| 80 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 81 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 82 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 83 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 84 |
+
EXPECT_TRUE(m.empty());
|
| 85 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 86 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 87 |
+
|
| 88 |
+
const auto& cm = m;
|
| 89 |
+
EXPECT_EQ(cm.hash_function(), hasher);
|
| 90 |
+
EXPECT_EQ(cm.key_eq(), equal);
|
| 91 |
+
EXPECT_EQ(cm.get_allocator(), alloc);
|
| 92 |
+
EXPECT_TRUE(cm.empty());
|
| 93 |
+
EXPECT_THAT(keys(cm), ::testing::UnorderedElementsAre());
|
| 94 |
+
EXPECT_GE(cm.bucket_count(), 123);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
template <typename T>
|
| 98 |
+
struct is_std_unordered_set : std::false_type {};
|
| 99 |
+
|
| 100 |
+
template <typename... T>
|
| 101 |
+
struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type {};
|
| 102 |
+
|
| 103 |
+
#if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17)
|
| 104 |
+
using has_cxx14_std_apis = std::true_type;
|
| 105 |
+
#else
|
| 106 |
+
using has_cxx14_std_apis = std::false_type;
|
| 107 |
+
#endif
|
| 108 |
+
|
| 109 |
+
template <typename T>
|
| 110 |
+
using expect_cxx14_apis =
|
| 111 |
+
absl::disjunction<absl::negation<is_std_unordered_set<T>>,
|
| 112 |
+
has_cxx14_std_apis>;
|
| 113 |
+
|
| 114 |
+
template <typename TypeParam>
|
| 115 |
+
void BucketCountAllocTest(std::false_type) {}
|
| 116 |
+
|
| 117 |
+
template <typename TypeParam>
|
| 118 |
+
void BucketCountAllocTest(std::true_type) {
|
| 119 |
+
using A = typename TypeParam::allocator_type;
|
| 120 |
+
A alloc(0);
|
| 121 |
+
TypeParam m(123, alloc);
|
| 122 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 123 |
+
EXPECT_TRUE(m.empty());
|
| 124 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 125 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
TYPED_TEST_P(ConstructorTest, BucketCountAlloc) {
|
| 129 |
+
BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
template <typename TypeParam>
|
| 133 |
+
void BucketCountHashAllocTest(std::false_type) {}
|
| 134 |
+
|
| 135 |
+
template <typename TypeParam>
|
| 136 |
+
void BucketCountHashAllocTest(std::true_type) {
|
| 137 |
+
using H = typename TypeParam::hasher;
|
| 138 |
+
using A = typename TypeParam::allocator_type;
|
| 139 |
+
H hasher;
|
| 140 |
+
A alloc(0);
|
| 141 |
+
TypeParam m(123, hasher, alloc);
|
| 142 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 143 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 144 |
+
EXPECT_TRUE(m.empty());
|
| 145 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 146 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) {
|
| 150 |
+
BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
#if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS
|
| 154 |
+
using has_alloc_std_constructors = std::true_type;
|
| 155 |
+
#else
|
| 156 |
+
using has_alloc_std_constructors = std::false_type;
|
| 157 |
+
#endif
|
| 158 |
+
|
| 159 |
+
template <typename T>
|
| 160 |
+
using expect_alloc_constructors =
|
| 161 |
+
absl::disjunction<absl::negation<is_std_unordered_set<T>>,
|
| 162 |
+
has_alloc_std_constructors>;
|
| 163 |
+
|
| 164 |
+
template <typename TypeParam>
|
| 165 |
+
void AllocTest(std::false_type) {}
|
| 166 |
+
|
| 167 |
+
template <typename TypeParam>
|
| 168 |
+
void AllocTest(std::true_type) {
|
| 169 |
+
using A = typename TypeParam::allocator_type;
|
| 170 |
+
A alloc(0);
|
| 171 |
+
TypeParam m(alloc);
|
| 172 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 173 |
+
EXPECT_TRUE(m.empty());
|
| 174 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
TYPED_TEST_P(ConstructorTest, Alloc) {
|
| 178 |
+
AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) {
|
| 182 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 183 |
+
using H = typename TypeParam::hasher;
|
| 184 |
+
using E = typename TypeParam::key_equal;
|
| 185 |
+
using A = typename TypeParam::allocator_type;
|
| 186 |
+
H hasher;
|
| 187 |
+
E equal;
|
| 188 |
+
A alloc(0);
|
| 189 |
+
std::vector<T> values;
|
| 190 |
+
for (size_t i = 0; i != 10; ++i)
|
| 191 |
+
values.push_back(hash_internal::Generator<T>()());
|
| 192 |
+
TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc);
|
| 193 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 194 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 195 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 196 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 197 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
template <typename TypeParam>
|
| 201 |
+
void InputIteratorBucketAllocTest(std::false_type) {}
|
| 202 |
+
|
| 203 |
+
template <typename TypeParam>
|
| 204 |
+
void InputIteratorBucketAllocTest(std::true_type) {
|
| 205 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 206 |
+
using A = typename TypeParam::allocator_type;
|
| 207 |
+
A alloc(0);
|
| 208 |
+
std::vector<T> values;
|
| 209 |
+
for (size_t i = 0; i != 10; ++i)
|
| 210 |
+
values.push_back(hash_internal::Generator<T>()());
|
| 211 |
+
TypeParam m(values.begin(), values.end(), 123, alloc);
|
| 212 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 213 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 214 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) {
|
| 218 |
+
InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
template <typename TypeParam>
|
| 222 |
+
void InputIteratorBucketHashAllocTest(std::false_type) {}
|
| 223 |
+
|
| 224 |
+
template <typename TypeParam>
|
| 225 |
+
void InputIteratorBucketHashAllocTest(std::true_type) {
|
| 226 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 227 |
+
using H = typename TypeParam::hasher;
|
| 228 |
+
using A = typename TypeParam::allocator_type;
|
| 229 |
+
H hasher;
|
| 230 |
+
A alloc(0);
|
| 231 |
+
std::vector<T> values;
|
| 232 |
+
for (size_t i = 0; i != 10; ++i)
|
| 233 |
+
values.push_back(hash_internal::Generator<T>()());
|
| 234 |
+
TypeParam m(values.begin(), values.end(), 123, hasher, alloc);
|
| 235 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 236 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 237 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 238 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) {
|
| 242 |
+
InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
TYPED_TEST_P(ConstructorTest, CopyConstructor) {
|
| 246 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 247 |
+
using H = typename TypeParam::hasher;
|
| 248 |
+
using E = typename TypeParam::key_equal;
|
| 249 |
+
using A = typename TypeParam::allocator_type;
|
| 250 |
+
H hasher;
|
| 251 |
+
E equal;
|
| 252 |
+
A alloc(0);
|
| 253 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 254 |
+
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
|
| 255 |
+
TypeParam n(m);
|
| 256 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 257 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 258 |
+
EXPECT_EQ(m.get_allocator(), n.get_allocator());
|
| 259 |
+
EXPECT_EQ(m, n);
|
| 260 |
+
EXPECT_NE(TypeParam(0, hasher, equal, alloc), n);
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
template <typename TypeParam>
|
| 264 |
+
void CopyConstructorAllocTest(std::false_type) {}
|
| 265 |
+
|
| 266 |
+
template <typename TypeParam>
|
| 267 |
+
void CopyConstructorAllocTest(std::true_type) {
|
| 268 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 269 |
+
using H = typename TypeParam::hasher;
|
| 270 |
+
using E = typename TypeParam::key_equal;
|
| 271 |
+
using A = typename TypeParam::allocator_type;
|
| 272 |
+
H hasher;
|
| 273 |
+
E equal;
|
| 274 |
+
A alloc(0);
|
| 275 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 276 |
+
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
|
| 277 |
+
TypeParam n(m, A(11));
|
| 278 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 279 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 280 |
+
EXPECT_NE(m.get_allocator(), n.get_allocator());
|
| 281 |
+
EXPECT_EQ(m, n);
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) {
|
| 285 |
+
CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
// TODO(alkis): Test non-propagating allocators on copy constructors.
|
| 289 |
+
|
| 290 |
+
TYPED_TEST_P(ConstructorTest, MoveConstructor) {
|
| 291 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 292 |
+
using H = typename TypeParam::hasher;
|
| 293 |
+
using E = typename TypeParam::key_equal;
|
| 294 |
+
using A = typename TypeParam::allocator_type;
|
| 295 |
+
H hasher;
|
| 296 |
+
E equal;
|
| 297 |
+
A alloc(0);
|
| 298 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 299 |
+
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
|
| 300 |
+
TypeParam t(m);
|
| 301 |
+
TypeParam n(std::move(t));
|
| 302 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 303 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 304 |
+
EXPECT_EQ(m.get_allocator(), n.get_allocator());
|
| 305 |
+
EXPECT_EQ(m, n);
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
template <typename TypeParam>
|
| 309 |
+
void MoveConstructorAllocTest(std::false_type) {}
|
| 310 |
+
|
| 311 |
+
template <typename TypeParam>
|
| 312 |
+
void MoveConstructorAllocTest(std::true_type) {
|
| 313 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 314 |
+
using H = typename TypeParam::hasher;
|
| 315 |
+
using E = typename TypeParam::key_equal;
|
| 316 |
+
using A = typename TypeParam::allocator_type;
|
| 317 |
+
H hasher;
|
| 318 |
+
E equal;
|
| 319 |
+
A alloc(0);
|
| 320 |
+
TypeParam m(123, hasher, equal, alloc);
|
| 321 |
+
for (size_t i = 0; i != 10; ++i) m.insert(hash_internal::Generator<T>()());
|
| 322 |
+
TypeParam t(m);
|
| 323 |
+
TypeParam n(std::move(t), A(1));
|
| 324 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 325 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 326 |
+
EXPECT_NE(m.get_allocator(), n.get_allocator());
|
| 327 |
+
EXPECT_EQ(m, n);
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) {
|
| 331 |
+
MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>());
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
// TODO(alkis): Test non-propagating allocators on move constructors.
|
| 335 |
+
|
| 336 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) {
|
| 337 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 338 |
+
hash_internal::Generator<T> gen;
|
| 339 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 340 |
+
using H = typename TypeParam::hasher;
|
| 341 |
+
using E = typename TypeParam::key_equal;
|
| 342 |
+
using A = typename TypeParam::allocator_type;
|
| 343 |
+
H hasher;
|
| 344 |
+
E equal;
|
| 345 |
+
A alloc(0);
|
| 346 |
+
TypeParam m(values, 123, hasher, equal, alloc);
|
| 347 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 348 |
+
EXPECT_EQ(m.key_eq(), equal);
|
| 349 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 350 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 351 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
template <typename TypeParam>
|
| 355 |
+
void InitializerListBucketAllocTest(std::false_type) {}
|
| 356 |
+
|
| 357 |
+
template <typename TypeParam>
|
| 358 |
+
void InitializerListBucketAllocTest(std::true_type) {
|
| 359 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 360 |
+
using A = typename TypeParam::allocator_type;
|
| 361 |
+
hash_internal::Generator<T> gen;
|
| 362 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 363 |
+
A alloc(0);
|
| 364 |
+
TypeParam m(values, 123, alloc);
|
| 365 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 366 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 367 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) {
|
| 371 |
+
InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
template <typename TypeParam>
|
| 375 |
+
void InitializerListBucketHashAllocTest(std::false_type) {}
|
| 376 |
+
|
| 377 |
+
template <typename TypeParam>
|
| 378 |
+
void InitializerListBucketHashAllocTest(std::true_type) {
|
| 379 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 380 |
+
using H = typename TypeParam::hasher;
|
| 381 |
+
using A = typename TypeParam::allocator_type;
|
| 382 |
+
H hasher;
|
| 383 |
+
A alloc(0);
|
| 384 |
+
hash_internal::Generator<T> gen;
|
| 385 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 386 |
+
TypeParam m(values, 123, hasher, alloc);
|
| 387 |
+
EXPECT_EQ(m.hash_function(), hasher);
|
| 388 |
+
EXPECT_EQ(m.get_allocator(), alloc);
|
| 389 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 390 |
+
EXPECT_GE(m.bucket_count(), 123);
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) {
|
| 394 |
+
InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>());
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
TYPED_TEST_P(ConstructorTest, CopyAssignment) {
|
| 398 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 399 |
+
using H = typename TypeParam::hasher;
|
| 400 |
+
using E = typename TypeParam::key_equal;
|
| 401 |
+
using A = typename TypeParam::allocator_type;
|
| 402 |
+
H hasher;
|
| 403 |
+
E equal;
|
| 404 |
+
A alloc(0);
|
| 405 |
+
hash_internal::Generator<T> gen;
|
| 406 |
+
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
|
| 407 |
+
TypeParam n;
|
| 408 |
+
n = m;
|
| 409 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 410 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 411 |
+
EXPECT_EQ(m, n);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
// TODO(alkis): Test [non-]propagating allocators on move/copy assignments
|
| 415 |
+
// (it depends on traits).
|
| 416 |
+
|
| 417 |
+
TYPED_TEST_P(ConstructorTest, MoveAssignment) {
|
| 418 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 419 |
+
using H = typename TypeParam::hasher;
|
| 420 |
+
using E = typename TypeParam::key_equal;
|
| 421 |
+
using A = typename TypeParam::allocator_type;
|
| 422 |
+
H hasher;
|
| 423 |
+
E equal;
|
| 424 |
+
A alloc(0);
|
| 425 |
+
hash_internal::Generator<T> gen;
|
| 426 |
+
TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc);
|
| 427 |
+
TypeParam t(m);
|
| 428 |
+
TypeParam n;
|
| 429 |
+
n = std::move(t);
|
| 430 |
+
EXPECT_EQ(m.hash_function(), n.hash_function());
|
| 431 |
+
EXPECT_EQ(m.key_eq(), n.key_eq());
|
| 432 |
+
EXPECT_EQ(m, n);
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) {
|
| 436 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 437 |
+
hash_internal::Generator<T> gen;
|
| 438 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 439 |
+
TypeParam m;
|
| 440 |
+
m = values;
|
| 441 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) {
|
| 445 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 446 |
+
hash_internal::Generator<T> gen;
|
| 447 |
+
TypeParam m({gen(), gen(), gen()});
|
| 448 |
+
TypeParam n({gen()});
|
| 449 |
+
n = m;
|
| 450 |
+
EXPECT_EQ(m, n);
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) {
|
| 454 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 455 |
+
hash_internal::Generator<T> gen;
|
| 456 |
+
TypeParam m({gen(), gen(), gen()});
|
| 457 |
+
TypeParam t(m);
|
| 458 |
+
TypeParam n({gen()});
|
| 459 |
+
n = std::move(t);
|
| 460 |
+
EXPECT_EQ(m, n);
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) {
|
| 464 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 465 |
+
hash_internal::Generator<T> gen;
|
| 466 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 467 |
+
TypeParam m;
|
| 468 |
+
m = values;
|
| 469 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) {
|
| 473 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 474 |
+
hash_internal::Generator<T> gen;
|
| 475 |
+
std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()};
|
| 476 |
+
TypeParam m(values);
|
| 477 |
+
m = *&m; // Avoid -Wself-assign.
|
| 478 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
REGISTER_TYPED_TEST_SUITE_P(
|
| 482 |
+
ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual,
|
| 483 |
+
BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc,
|
| 484 |
+
InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc,
|
| 485 |
+
InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc,
|
| 486 |
+
MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc,
|
| 487 |
+
InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment,
|
| 488 |
+
MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting,
|
| 489 |
+
MoveAssignmentOverwritesExisting,
|
| 490 |
+
AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf);
|
| 491 |
+
|
| 492 |
+
} // namespace container_internal
|
| 493 |
+
ABSL_NAMESPACE_END
|
| 494 |
+
} // namespace absl
|
| 495 |
+
|
| 496 |
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_set_lookup_test.h
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
|
| 17 |
+
|
| 18 |
+
#include "gmock/gmock.h"
|
| 19 |
+
#include "gtest/gtest.h"
|
| 20 |
+
#include "absl/container/internal/hash_generator_testing.h"
|
| 21 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 22 |
+
|
| 23 |
+
namespace absl {
|
| 24 |
+
ABSL_NAMESPACE_BEGIN
|
| 25 |
+
namespace container_internal {
|
| 26 |
+
|
| 27 |
+
template <class UnordSet>
|
| 28 |
+
class LookupTest : public ::testing::Test {};
|
| 29 |
+
|
| 30 |
+
TYPED_TEST_SUITE_P(LookupTest);
|
| 31 |
+
|
| 32 |
+
TYPED_TEST_P(LookupTest, Count) {
|
| 33 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 34 |
+
std::vector<T> values;
|
| 35 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 36 |
+
hash_internal::Generator<T>());
|
| 37 |
+
TypeParam m;
|
| 38 |
+
for (const auto& v : values)
|
| 39 |
+
EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v);
|
| 40 |
+
m.insert(values.begin(), values.end());
|
| 41 |
+
for (const auto& v : values)
|
| 42 |
+
EXPECT_EQ(1, m.count(v)) << ::testing::PrintToString(v);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
TYPED_TEST_P(LookupTest, Find) {
|
| 46 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 47 |
+
std::vector<T> values;
|
| 48 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 49 |
+
hash_internal::Generator<T>());
|
| 50 |
+
TypeParam m;
|
| 51 |
+
for (const auto& v : values)
|
| 52 |
+
EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v);
|
| 53 |
+
m.insert(values.begin(), values.end());
|
| 54 |
+
for (const auto& v : values) {
|
| 55 |
+
typename TypeParam::iterator it = m.find(v);
|
| 56 |
+
static_assert(std::is_same<const typename TypeParam::value_type&,
|
| 57 |
+
decltype(*it)>::value,
|
| 58 |
+
"");
|
| 59 |
+
static_assert(std::is_same<const typename TypeParam::value_type*,
|
| 60 |
+
decltype(it.operator->())>::value,
|
| 61 |
+
"");
|
| 62 |
+
EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(v);
|
| 63 |
+
EXPECT_EQ(v, *it) << ::testing::PrintToString(v);
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
TYPED_TEST_P(LookupTest, EqualRange) {
|
| 68 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 69 |
+
std::vector<T> values;
|
| 70 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 71 |
+
hash_internal::Generator<T>());
|
| 72 |
+
TypeParam m;
|
| 73 |
+
for (const auto& v : values) {
|
| 74 |
+
auto r = m.equal_range(v);
|
| 75 |
+
ASSERT_EQ(0, std::distance(r.first, r.second));
|
| 76 |
+
}
|
| 77 |
+
m.insert(values.begin(), values.end());
|
| 78 |
+
for (const auto& v : values) {
|
| 79 |
+
auto r = m.equal_range(v);
|
| 80 |
+
ASSERT_EQ(1, std::distance(r.first, r.second));
|
| 81 |
+
EXPECT_EQ(v, *r.first);
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
REGISTER_TYPED_TEST_SUITE_P(LookupTest, Count, Find, EqualRange);
|
| 86 |
+
|
| 87 |
+
} // namespace container_internal
|
| 88 |
+
ABSL_NAMESPACE_END
|
| 89 |
+
} // namespace absl
|
| 90 |
+
|
| 91 |
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_set_modifiers_test.h
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
|
| 16 |
+
#define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
|
| 17 |
+
|
| 18 |
+
#include "gmock/gmock.h"
|
| 19 |
+
#include "gtest/gtest.h"
|
| 20 |
+
#include "absl/container/internal/hash_generator_testing.h"
|
| 21 |
+
#include "absl/container/internal/hash_policy_testing.h"
|
| 22 |
+
|
| 23 |
+
namespace absl {
|
| 24 |
+
ABSL_NAMESPACE_BEGIN
|
| 25 |
+
namespace container_internal {
|
| 26 |
+
|
| 27 |
+
template <class UnordSet>
|
| 28 |
+
class ModifiersTest : public ::testing::Test {};
|
| 29 |
+
|
| 30 |
+
TYPED_TEST_SUITE_P(ModifiersTest);
|
| 31 |
+
|
| 32 |
+
TYPED_TEST_P(ModifiersTest, Clear) {
|
| 33 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 34 |
+
std::vector<T> values;
|
| 35 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 36 |
+
hash_internal::Generator<T>());
|
| 37 |
+
TypeParam m(values.begin(), values.end());
|
| 38 |
+
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 39 |
+
m.clear();
|
| 40 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 41 |
+
EXPECT_TRUE(m.empty());
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
TYPED_TEST_P(ModifiersTest, Insert) {
|
| 45 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 46 |
+
T val = hash_internal::Generator<T>()();
|
| 47 |
+
TypeParam m;
|
| 48 |
+
auto p = m.insert(val);
|
| 49 |
+
EXPECT_TRUE(p.second);
|
| 50 |
+
EXPECT_EQ(val, *p.first);
|
| 51 |
+
p = m.insert(val);
|
| 52 |
+
EXPECT_FALSE(p.second);
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
TYPED_TEST_P(ModifiersTest, InsertHint) {
|
| 56 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 57 |
+
T val = hash_internal::Generator<T>()();
|
| 58 |
+
TypeParam m;
|
| 59 |
+
auto it = m.insert(m.end(), val);
|
| 60 |
+
EXPECT_TRUE(it != m.end());
|
| 61 |
+
EXPECT_EQ(val, *it);
|
| 62 |
+
it = m.insert(it, val);
|
| 63 |
+
EXPECT_TRUE(it != m.end());
|
| 64 |
+
EXPECT_EQ(val, *it);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
TYPED_TEST_P(ModifiersTest, InsertRange) {
|
| 68 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 69 |
+
std::vector<T> values;
|
| 70 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 71 |
+
hash_internal::Generator<T>());
|
| 72 |
+
TypeParam m;
|
| 73 |
+
m.insert(values.begin(), values.end());
|
| 74 |
+
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) {
|
| 78 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 79 |
+
T val = hash_internal::Generator<T>()();
|
| 80 |
+
TypeParam m;
|
| 81 |
+
m.reserve(10);
|
| 82 |
+
const size_t original_capacity = m.bucket_count();
|
| 83 |
+
m.insert(val);
|
| 84 |
+
EXPECT_EQ(m.bucket_count(), original_capacity);
|
| 85 |
+
m.insert(val);
|
| 86 |
+
EXPECT_EQ(m.bucket_count(), original_capacity);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) {
|
| 90 |
+
#if !defined(__GLIBCXX__)
|
| 91 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 92 |
+
std::vector<T> base_values;
|
| 93 |
+
std::generate_n(std::back_inserter(base_values), 10,
|
| 94 |
+
hash_internal::Generator<T>());
|
| 95 |
+
std::vector<T> values;
|
| 96 |
+
while (values.size() != 100) {
|
| 97 |
+
values.insert(values.end(), base_values.begin(), base_values.end());
|
| 98 |
+
}
|
| 99 |
+
TypeParam m;
|
| 100 |
+
m.reserve(10);
|
| 101 |
+
const size_t original_capacity = m.bucket_count();
|
| 102 |
+
m.insert(values.begin(), values.end());
|
| 103 |
+
EXPECT_EQ(m.bucket_count(), original_capacity);
|
| 104 |
+
#endif
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
TYPED_TEST_P(ModifiersTest, Emplace) {
|
| 108 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 109 |
+
T val = hash_internal::Generator<T>()();
|
| 110 |
+
TypeParam m;
|
| 111 |
+
// TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
|
| 112 |
+
// with test traits/policy.
|
| 113 |
+
auto p = m.emplace(val);
|
| 114 |
+
EXPECT_TRUE(p.second);
|
| 115 |
+
EXPECT_EQ(val, *p.first);
|
| 116 |
+
p = m.emplace(val);
|
| 117 |
+
EXPECT_FALSE(p.second);
|
| 118 |
+
EXPECT_EQ(val, *p.first);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
TYPED_TEST_P(ModifiersTest, EmplaceHint) {
|
| 122 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 123 |
+
T val = hash_internal::Generator<T>()();
|
| 124 |
+
TypeParam m;
|
| 125 |
+
// TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps
|
| 126 |
+
// with test traits/policy.
|
| 127 |
+
auto it = m.emplace_hint(m.end(), val);
|
| 128 |
+
EXPECT_EQ(val, *it);
|
| 129 |
+
it = m.emplace_hint(it, val);
|
| 130 |
+
EXPECT_EQ(val, *it);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
template <class V>
|
| 134 |
+
using IfNotVoid = typename std::enable_if<!std::is_void<V>::value, V>::type;
|
| 135 |
+
|
| 136 |
+
// In openmap we chose not to return the iterator from erase because that's
|
| 137 |
+
// more expensive. As such we adapt erase to return an iterator here.
|
| 138 |
+
struct EraseFirst {
|
| 139 |
+
template <class Map>
|
| 140 |
+
auto operator()(Map* m, int) const
|
| 141 |
+
-> IfNotVoid<decltype(m->erase(m->begin()))> {
|
| 142 |
+
return m->erase(m->begin());
|
| 143 |
+
}
|
| 144 |
+
template <class Map>
|
| 145 |
+
typename Map::iterator operator()(Map* m, ...) const {
|
| 146 |
+
auto it = m->begin();
|
| 147 |
+
m->erase(it++);
|
| 148 |
+
return it;
|
| 149 |
+
}
|
| 150 |
+
};
|
| 151 |
+
|
| 152 |
+
TYPED_TEST_P(ModifiersTest, Erase) {
|
| 153 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 154 |
+
std::vector<T> values;
|
| 155 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 156 |
+
hash_internal::Generator<T>());
|
| 157 |
+
TypeParam m(values.begin(), values.end());
|
| 158 |
+
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 159 |
+
std::vector<T> values2;
|
| 160 |
+
for (const auto& val : values)
|
| 161 |
+
if (val != *m.begin()) values2.push_back(val);
|
| 162 |
+
auto it = EraseFirst()(&m, 0);
|
| 163 |
+
ASSERT_TRUE(it != m.end());
|
| 164 |
+
EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it));
|
| 165 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values2.begin(),
|
| 166 |
+
values2.end()));
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
TYPED_TEST_P(ModifiersTest, EraseRange) {
|
| 170 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 171 |
+
std::vector<T> values;
|
| 172 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 173 |
+
hash_internal::Generator<T>());
|
| 174 |
+
TypeParam m(values.begin(), values.end());
|
| 175 |
+
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 176 |
+
auto it = m.erase(m.begin(), m.end());
|
| 177 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre());
|
| 178 |
+
EXPECT_TRUE(it == m.end());
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
TYPED_TEST_P(ModifiersTest, EraseKey) {
|
| 182 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 183 |
+
std::vector<T> values;
|
| 184 |
+
std::generate_n(std::back_inserter(values), 10,
|
| 185 |
+
hash_internal::Generator<T>());
|
| 186 |
+
TypeParam m(values.begin(), values.end());
|
| 187 |
+
ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values));
|
| 188 |
+
EXPECT_EQ(1, m.erase(values[0]));
|
| 189 |
+
EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0]));
|
| 190 |
+
EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values.begin() + 1,
|
| 191 |
+
values.end()));
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
TYPED_TEST_P(ModifiersTest, Swap) {
|
| 195 |
+
using T = hash_internal::GeneratedType<TypeParam>;
|
| 196 |
+
std::vector<T> v1;
|
| 197 |
+
std::vector<T> v2;
|
| 198 |
+
std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>());
|
| 199 |
+
std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>());
|
| 200 |
+
TypeParam m1(v1.begin(), v1.end());
|
| 201 |
+
TypeParam m2(v2.begin(), v2.end());
|
| 202 |
+
EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v1));
|
| 203 |
+
EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v2));
|
| 204 |
+
m1.swap(m2);
|
| 205 |
+
EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v2));
|
| 206 |
+
EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v1));
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
// TODO(alkis): Write tests for extract.
|
| 210 |
+
// TODO(alkis): Write tests for merge.
|
| 211 |
+
|
| 212 |
+
REGISTER_TYPED_TEST_SUITE_P(ModifiersTest, Clear, Insert, InsertHint,
|
| 213 |
+
InsertRange, InsertWithinCapacity,
|
| 214 |
+
InsertRangeWithinCapacity, Emplace, EmplaceHint,
|
| 215 |
+
Erase, EraseRange, EraseKey, Swap);
|
| 216 |
+
|
| 217 |
+
} // namespace container_internal
|
| 218 |
+
ABSL_NAMESPACE_END
|
| 219 |
+
} // namespace absl
|
| 220 |
+
|
| 221 |
+
#endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_
|
weight/_dep/abseil-cpp/absl/container/internal/unordered_set_test.cc
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include <unordered_set>
|
| 16 |
+
|
| 17 |
+
#include "absl/container/internal/unordered_set_constructor_test.h"
|
| 18 |
+
#include "absl/container/internal/unordered_set_lookup_test.h"
|
| 19 |
+
#include "absl/container/internal/unordered_set_members_test.h"
|
| 20 |
+
#include "absl/container/internal/unordered_set_modifiers_test.h"
|
| 21 |
+
|
| 22 |
+
namespace absl {
|
| 23 |
+
ABSL_NAMESPACE_BEGIN
|
| 24 |
+
namespace container_internal {
|
| 25 |
+
namespace {
|
| 26 |
+
|
| 27 |
+
using SetTypes = ::testing::Types<
|
| 28 |
+
std::unordered_set<int, StatefulTestingHash, StatefulTestingEqual,
|
| 29 |
+
Alloc<int>>,
|
| 30 |
+
std::unordered_set<std::string, StatefulTestingHash, StatefulTestingEqual,
|
| 31 |
+
Alloc<std::string>>>;
|
| 32 |
+
|
| 33 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ConstructorTest, SetTypes);
|
| 34 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, LookupTest, SetTypes);
|
| 35 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, MembersTest, SetTypes);
|
| 36 |
+
INSTANTIATE_TYPED_TEST_SUITE_P(UnorderedSet, ModifiersTest, SetTypes);
|
| 37 |
+
|
| 38 |
+
} // namespace
|
| 39 |
+
} // namespace container_internal
|
| 40 |
+
ABSL_NAMESPACE_END
|
| 41 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/crc/crc32c.h
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// -----------------------------------------------------------------------------
|
| 16 |
+
// File: crc32c.h
|
| 17 |
+
// -----------------------------------------------------------------------------
|
| 18 |
+
//
|
| 19 |
+
// This header file defines the API for computing CRC32C values as checksums
|
| 20 |
+
// for arbitrary sequences of bytes provided as a string buffer.
|
| 21 |
+
//
|
| 22 |
+
// The API includes the basic functions for computing such CRC32C values and
|
| 23 |
+
// some utility functions for performing more efficient mathematical
|
| 24 |
+
// computations using an existing checksum.
|
| 25 |
+
#ifndef ABSL_CRC_CRC32C_H_
|
| 26 |
+
#define ABSL_CRC_CRC32C_H_
|
| 27 |
+
|
| 28 |
+
#include <cstdint>
|
| 29 |
+
#include <ostream>
|
| 30 |
+
|
| 31 |
+
#include "absl/crc/internal/crc32c_inline.h"
|
| 32 |
+
#include "absl/strings/str_format.h"
|
| 33 |
+
#include "absl/strings/string_view.h"
|
| 34 |
+
|
| 35 |
+
namespace absl {
|
| 36 |
+
ABSL_NAMESPACE_BEGIN
|
| 37 |
+
|
| 38 |
+
//-----------------------------------------------------------------------------
|
| 39 |
+
// crc32c_t
|
| 40 |
+
//-----------------------------------------------------------------------------
|
| 41 |
+
|
| 42 |
+
// `crc32c_t` defines a strongly-typed integer for holding a CRC32C value.
|
| 43 |
+
//
|
| 44 |
+
// Some operators are intentionally omitted. Only equality operators are defined
|
| 45 |
+
// so that `crc32c_t` can be directly compared. Methods for putting `crc32c_t`
|
| 46 |
+
// directly into a set are omitted because this is bug-prone due to checksum
|
| 47 |
+
// collisions. Use an explicit conversion to the `uint32_t` space for operations
|
| 48 |
+
// that treat `crc32c_t` as an integer.
|
| 49 |
+
class crc32c_t final {
|
| 50 |
+
public:
|
| 51 |
+
crc32c_t() = default;
|
| 52 |
+
constexpr explicit crc32c_t(uint32_t crc) : crc_(crc) {}
|
| 53 |
+
|
| 54 |
+
crc32c_t(const crc32c_t&) = default;
|
| 55 |
+
crc32c_t& operator=(const crc32c_t&) = default;
|
| 56 |
+
|
| 57 |
+
explicit operator uint32_t() const { return crc_; }
|
| 58 |
+
|
| 59 |
+
friend bool operator==(crc32c_t lhs, crc32c_t rhs) {
|
| 60 |
+
return static_cast<uint32_t>(lhs) == static_cast<uint32_t>(rhs);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
friend bool operator!=(crc32c_t lhs, crc32c_t rhs) { return !(lhs == rhs); }
|
| 64 |
+
|
| 65 |
+
template <typename Sink>
|
| 66 |
+
friend void AbslStringify(Sink& sink, crc32c_t crc) {
|
| 67 |
+
absl::Format(&sink, "%08x", static_cast<uint32_t>(crc));
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
private:
|
| 71 |
+
uint32_t crc_;
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
namespace crc_internal {
|
| 76 |
+
// Non-inline code path for `absl::ExtendCrc32c()`. Do not call directly.
|
| 77 |
+
// Call `absl::ExtendCrc32c()` (defined below) instead.
|
| 78 |
+
crc32c_t ExtendCrc32cInternal(crc32c_t initial_crc,
|
| 79 |
+
absl::string_view buf_to_add);
|
| 80 |
+
} // namespace crc_internal
|
| 81 |
+
|
| 82 |
+
// -----------------------------------------------------------------------------
|
| 83 |
+
// CRC32C Computation Functions
|
| 84 |
+
// -----------------------------------------------------------------------------
|
| 85 |
+
|
| 86 |
+
// ComputeCrc32c()
|
| 87 |
+
//
|
| 88 |
+
// Returns the CRC32C value of the provided string.
|
| 89 |
+
crc32c_t ComputeCrc32c(absl::string_view buf);
|
| 90 |
+
|
| 91 |
+
// ExtendCrc32c()
|
| 92 |
+
//
|
| 93 |
+
// Computes a CRC32C value from an `initial_crc` CRC32C value including the
|
| 94 |
+
// `buf_to_add` bytes of an additional buffer. Using this function is more
|
| 95 |
+
// efficient than computing a CRC32C value for the combined buffer from
|
| 96 |
+
// scratch.
|
| 97 |
+
//
|
| 98 |
+
// Note: `ExtendCrc32c` with an initial_crc of 0 is equivalent to
|
| 99 |
+
// `ComputeCrc32c`.
|
| 100 |
+
//
|
| 101 |
+
// This operation has a runtime cost of O(`buf_to_add.size()`)
|
| 102 |
+
inline crc32c_t ExtendCrc32c(crc32c_t initial_crc,
|
| 103 |
+
absl::string_view buf_to_add) {
|
| 104 |
+
// Approximately 75% of calls have size <= 64.
|
| 105 |
+
if (buf_to_add.size() <= 64) {
|
| 106 |
+
uint32_t crc = static_cast<uint32_t>(initial_crc);
|
| 107 |
+
if (crc_internal::ExtendCrc32cInline(&crc, buf_to_add.data(),
|
| 108 |
+
buf_to_add.size())) {
|
| 109 |
+
return crc32c_t{crc};
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
return crc_internal::ExtendCrc32cInternal(initial_crc, buf_to_add);
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// ExtendCrc32cByZeroes()
|
| 116 |
+
//
|
| 117 |
+
// Computes a CRC32C value for a buffer with an `initial_crc` CRC32C value,
|
| 118 |
+
// where `length` bytes with a value of 0 are appended to the buffer. Using this
|
| 119 |
+
// function is more efficient than computing a CRC32C value for the combined
|
| 120 |
+
// buffer from scratch.
|
| 121 |
+
//
|
| 122 |
+
// This operation has a runtime cost of O(log(`length`))
|
| 123 |
+
crc32c_t ExtendCrc32cByZeroes(crc32c_t initial_crc, size_t length);
|
| 124 |
+
|
| 125 |
+
// MemcpyCrc32c()
|
| 126 |
+
//
|
| 127 |
+
// Copies `src` to `dest` using `memcpy()` semantics, returning the CRC32C
|
| 128 |
+
// value of the copied buffer.
|
| 129 |
+
//
|
| 130 |
+
// Using `MemcpyCrc32c()` is potentially faster than performing the `memcpy()`
|
| 131 |
+
// and `ComputeCrc32c()` operations separately.
|
| 132 |
+
crc32c_t MemcpyCrc32c(void* dest, const void* src, size_t count,
|
| 133 |
+
crc32c_t initial_crc = crc32c_t{0});
|
| 134 |
+
|
| 135 |
+
// -----------------------------------------------------------------------------
|
| 136 |
+
// CRC32C Arithmetic Functions
|
| 137 |
+
// -----------------------------------------------------------------------------
|
| 138 |
+
|
| 139 |
+
// The following functions perform arithmetic on CRC32C values, which are
|
| 140 |
+
// generally more efficient than recalculating any given result's CRC32C value.
|
| 141 |
+
|
| 142 |
+
// ConcatCrc32c()
|
| 143 |
+
//
|
| 144 |
+
// Calculates the CRC32C value of two buffers with known CRC32C values
|
| 145 |
+
// concatenated together.
|
| 146 |
+
//
|
| 147 |
+
// Given a buffer with CRC32C value `crc1` and a buffer with
|
| 148 |
+
// CRC32C value `crc2` and length, `crc2_length`, returns the CRC32C value of
|
| 149 |
+
// the concatenation of these two buffers.
|
| 150 |
+
//
|
| 151 |
+
// This operation has a runtime cost of O(log(`crc2_length`)).
|
| 152 |
+
crc32c_t ConcatCrc32c(crc32c_t crc1, crc32c_t crc2, size_t crc2_length);
|
| 153 |
+
|
| 154 |
+
// RemoveCrc32cPrefix()
|
| 155 |
+
//
|
| 156 |
+
// Calculates the CRC32C value of an existing buffer with a series of bytes
|
| 157 |
+
// (the prefix) removed from the beginning of that buffer.
|
| 158 |
+
//
|
| 159 |
+
// Given the CRC32C value of an existing buffer, `full_string_crc`; The CRC32C
|
| 160 |
+
// value of a prefix of that buffer, `prefix_crc`; and the length of the buffer
|
| 161 |
+
// with the prefix removed, `remaining_string_length` , return the CRC32C
|
| 162 |
+
// value of the buffer with the prefix removed.
|
| 163 |
+
//
|
| 164 |
+
// This operation has a runtime cost of O(log(`remaining_string_length`)).
|
| 165 |
+
crc32c_t RemoveCrc32cPrefix(crc32c_t prefix_crc, crc32c_t full_string_crc,
|
| 166 |
+
size_t remaining_string_length);
|
| 167 |
+
// RemoveCrc32cSuffix()
|
| 168 |
+
//
|
| 169 |
+
// Calculates the CRC32C value of an existing buffer with a series of bytes
|
| 170 |
+
// (the suffix) removed from the end of that buffer.
|
| 171 |
+
//
|
| 172 |
+
// Given a CRC32C value of an existing buffer `full_string_crc`, the CRC32C
|
| 173 |
+
// value of the suffix to remove `suffix_crc`, and the length of that suffix
|
| 174 |
+
// `suffix_len`, returns the CRC32C value of the buffer with suffix removed.
|
| 175 |
+
//
|
| 176 |
+
// This operation has a runtime cost of O(log(`suffix_len`))
|
| 177 |
+
crc32c_t RemoveCrc32cSuffix(crc32c_t full_string_crc, crc32c_t suffix_crc,
|
| 178 |
+
size_t suffix_length);
|
| 179 |
+
|
| 180 |
+
// operator<<
|
| 181 |
+
//
|
| 182 |
+
// Streams the CRC32C value `crc` to the stream `os`.
|
| 183 |
+
inline std::ostream& operator<<(std::ostream& os, crc32c_t crc) {
|
| 184 |
+
return os << absl::StreamFormat("%08x", static_cast<uint32_t>(crc));
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
ABSL_NAMESPACE_END
|
| 188 |
+
} // namespace absl
|
| 189 |
+
|
| 190 |
+
#endif // ABSL_CRC_CRC32C_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/crc32c.h
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CRC_INTERNAL_CRC32C_H_
|
| 16 |
+
#define ABSL_CRC_INTERNAL_CRC32C_H_
|
| 17 |
+
|
| 18 |
+
#include "absl/base/config.h"
|
| 19 |
+
#include "absl/crc/crc32c.h"
|
| 20 |
+
|
| 21 |
+
namespace absl {
|
| 22 |
+
ABSL_NAMESPACE_BEGIN
|
| 23 |
+
namespace crc_internal {
|
| 24 |
+
|
| 25 |
+
// Modifies a CRC32 value by removing `length` bytes with a value of 0 from
|
| 26 |
+
// the end of the string.
|
| 27 |
+
//
|
| 28 |
+
// This is the inverse operation of ExtendCrc32cByZeroes().
|
| 29 |
+
//
|
| 30 |
+
// This operation has a runtime cost of O(log(`length`))
|
| 31 |
+
//
|
| 32 |
+
// Internal implementation detail, exposed for testing only.
|
| 33 |
+
crc32c_t UnextendCrc32cByZeroes(crc32c_t initial_crc, size_t length);
|
| 34 |
+
|
| 35 |
+
} // namespace crc_internal
|
| 36 |
+
ABSL_NAMESPACE_END
|
| 37 |
+
} // namespace absl
|
| 38 |
+
|
| 39 |
+
#endif // ABSL_CRC_INTERNAL_CRC32C_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_cord_state.h
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_
|
| 16 |
+
#define ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_
|
| 17 |
+
|
| 18 |
+
#include <atomic>
|
| 19 |
+
#include <cstddef>
|
| 20 |
+
#include <deque>
|
| 21 |
+
|
| 22 |
+
#include "absl/base/config.h"
|
| 23 |
+
#include "absl/crc/crc32c.h"
|
| 24 |
+
|
| 25 |
+
namespace absl {
|
| 26 |
+
ABSL_NAMESPACE_BEGIN
|
| 27 |
+
namespace crc_internal {
|
| 28 |
+
|
| 29 |
+
// CrcCordState is a copy-on-write class that holds the chunked CRC32C data
|
| 30 |
+
// that allows CrcCord to perform efficient substring operations. CrcCordState
|
| 31 |
+
// is used as a member variable in CrcCord. When a CrcCord is converted to a
|
| 32 |
+
// Cord, the CrcCordState is shallow-copied into the root node of the Cord. If
|
| 33 |
+
// the converted Cord is modified outside of CrcCord, the CrcCordState is
|
| 34 |
+
// discarded from the Cord. If the Cord is converted back to a CrcCord, and the
|
| 35 |
+
// Cord is still carrying the CrcCordState in its root node, the CrcCord can
|
| 36 |
+
// re-use the CrcCordState, making the construction of the CrcCord cheap.
|
| 37 |
+
//
|
| 38 |
+
// CrcCordState does not try to encapsulate the CRC32C state (CrcCord requires
|
| 39 |
+
// knowledge of how CrcCordState represents the CRC32C state). It does
|
| 40 |
+
// encapsulate the copy-on-write nature of the state.
|
| 41 |
+
class CrcCordState {
|
| 42 |
+
public:
|
| 43 |
+
// Constructors.
|
| 44 |
+
CrcCordState();
|
| 45 |
+
CrcCordState(const CrcCordState&);
|
| 46 |
+
CrcCordState(CrcCordState&&);
|
| 47 |
+
|
| 48 |
+
// Destructor. Atomically unreferences the data.
|
| 49 |
+
~CrcCordState();
|
| 50 |
+
|
| 51 |
+
// Copy and move operators.
|
| 52 |
+
CrcCordState& operator=(const CrcCordState&);
|
| 53 |
+
CrcCordState& operator=(CrcCordState&&);
|
| 54 |
+
|
| 55 |
+
// A (length, crc) pair.
|
| 56 |
+
struct PrefixCrc {
|
| 57 |
+
PrefixCrc() = default;
|
| 58 |
+
PrefixCrc(size_t length_arg, absl::crc32c_t crc_arg)
|
| 59 |
+
: length(length_arg), crc(crc_arg) {}
|
| 60 |
+
|
| 61 |
+
size_t length = 0;
|
| 62 |
+
|
| 63 |
+
// TODO(absl-team): Memory stomping often zeros out memory. If this struct
|
| 64 |
+
// gets overwritten, we could end up with {0, 0}, which is the correct CRC
|
| 65 |
+
// for a string of length 0. Consider storing a scrambled value and
|
| 66 |
+
// unscrambling it before verifying it.
|
| 67 |
+
absl::crc32c_t crc = absl::crc32c_t{0};
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
+
// The representation of the chunked CRC32C data.
|
| 71 |
+
struct Rep {
|
| 72 |
+
// `removed_prefix` is the crc and length of any prefix that has been
|
| 73 |
+
// removed from the Cord (for example, by calling
|
| 74 |
+
// `CrcCord::RemovePrefix()`). To get the checksum of any prefix of the
|
| 75 |
+
// cord, this value must be subtracted from `prefix_crc`. See `Checksum()`
|
| 76 |
+
// for an example.
|
| 77 |
+
//
|
| 78 |
+
// CrcCordState is said to be "normalized" if removed_prefix.length == 0.
|
| 79 |
+
PrefixCrc removed_prefix;
|
| 80 |
+
|
| 81 |
+
// A deque of (length, crc) pairs, representing length and crc of a prefix
|
| 82 |
+
// of the Cord, before removed_prefix has been subtracted. The lengths of
|
| 83 |
+
// the prefixes are stored in increasing order. If the Cord is not empty,
|
| 84 |
+
// the last value in deque is the contains the CRC32C of the entire Cord
|
| 85 |
+
// when removed_prefix is subtracted from it.
|
| 86 |
+
std::deque<PrefixCrc> prefix_crc;
|
| 87 |
+
};
|
| 88 |
+
|
| 89 |
+
// Returns a reference to the representation of the chunked CRC32C data.
|
| 90 |
+
const Rep& rep() const { return refcounted_rep_->rep; }
|
| 91 |
+
|
| 92 |
+
// Returns a mutable reference to the representation of the chunked CRC32C
|
| 93 |
+
// data. Calling this function will copy the data if another instance also
|
| 94 |
+
// holds a reference to the data, so it is important to call rep() instead if
|
| 95 |
+
// the data may not be mutated.
|
| 96 |
+
Rep* mutable_rep() {
|
| 97 |
+
if (refcounted_rep_->count.load(std::memory_order_acquire) != 1) {
|
| 98 |
+
RefcountedRep* copy = new RefcountedRep;
|
| 99 |
+
copy->rep = refcounted_rep_->rep;
|
| 100 |
+
Unref(refcounted_rep_);
|
| 101 |
+
refcounted_rep_ = copy;
|
| 102 |
+
}
|
| 103 |
+
return &refcounted_rep_->rep;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// Returns the CRC32C of the entire Cord.
|
| 107 |
+
absl::crc32c_t Checksum() const;
|
| 108 |
+
|
| 109 |
+
// Returns true if the chunked CRC32C cached is normalized.
|
| 110 |
+
bool IsNormalized() const { return rep().removed_prefix.length == 0; }
|
| 111 |
+
|
| 112 |
+
// Normalizes the chunked CRC32C checksum cache by subtracting any removed
|
| 113 |
+
// prefix from the chunks.
|
| 114 |
+
void Normalize();
|
| 115 |
+
|
| 116 |
+
// Returns the number of cached chunks.
|
| 117 |
+
size_t NumChunks() const { return rep().prefix_crc.size(); }
|
| 118 |
+
|
| 119 |
+
// Helper that returns the (length, crc) of the `n`-th cached chunked.
|
| 120 |
+
PrefixCrc NormalizedPrefixCrcAtNthChunk(size_t n) const;
|
| 121 |
+
|
| 122 |
+
// Poisons all chunks to so that Checksum() will likely be incorrect with high
|
| 123 |
+
// probability.
|
| 124 |
+
void Poison();
|
| 125 |
+
|
| 126 |
+
private:
|
| 127 |
+
struct RefcountedRep {
|
| 128 |
+
std::atomic<int32_t> count{1};
|
| 129 |
+
Rep rep;
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
// Adds a reference to the shared global empty `RefcountedRep`, and returns a
|
| 133 |
+
// pointer to the `RefcountedRep`. This is an optimization to avoid unneeded
|
| 134 |
+
// allocations when the allocation is unlikely to ever be used. The returned
|
| 135 |
+
// pointer can be `Unref()`ed when it is no longer needed. Since the returned
|
| 136 |
+
// instance will always have a reference counter greater than 1, attempts to
|
| 137 |
+
// modify it (by calling `mutable_rep()`) will create a new unshared copy.
|
| 138 |
+
static RefcountedRep* RefSharedEmptyRep();
|
| 139 |
+
|
| 140 |
+
static void Ref(RefcountedRep* r) {
|
| 141 |
+
assert(r != nullptr);
|
| 142 |
+
r->count.fetch_add(1, std::memory_order_relaxed);
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
static void Unref(RefcountedRep* r) {
|
| 146 |
+
assert(r != nullptr);
|
| 147 |
+
if (r->count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
| 148 |
+
delete r;
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
RefcountedRep* refcounted_rep_;
|
| 153 |
+
};
|
| 154 |
+
|
| 155 |
+
} // namespace crc_internal
|
| 156 |
+
ABSL_NAMESPACE_END
|
| 157 |
+
} // namespace absl
|
| 158 |
+
|
| 159 |
+
#endif // ABSL_CRC_INTERNAL_CRC_CORD_STATE_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_cord_state_test.cc
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/crc/internal/crc_cord_state.h"
|
| 16 |
+
|
| 17 |
+
#include <algorithm>
|
| 18 |
+
#include <cstdint>
|
| 19 |
+
#include <string>
|
| 20 |
+
#include <utility>
|
| 21 |
+
|
| 22 |
+
#include "gtest/gtest.h"
|
| 23 |
+
#include "absl/crc/crc32c.h"
|
| 24 |
+
|
| 25 |
+
namespace {
|
| 26 |
+
|
| 27 |
+
TEST(CrcCordState, Default) {
|
| 28 |
+
absl::crc_internal::CrcCordState state;
|
| 29 |
+
EXPECT_TRUE(state.IsNormalized());
|
| 30 |
+
EXPECT_EQ(state.Checksum(), absl::crc32c_t{0});
|
| 31 |
+
state.Normalize();
|
| 32 |
+
EXPECT_EQ(state.Checksum(), absl::crc32c_t{0});
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
TEST(CrcCordState, Normalize) {
|
| 36 |
+
absl::crc_internal::CrcCordState state;
|
| 37 |
+
auto* rep = state.mutable_rep();
|
| 38 |
+
rep->prefix_crc.push_back(
|
| 39 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 40 |
+
rep->prefix_crc.push_back(
|
| 41 |
+
absl::crc_internal::CrcCordState::PrefixCrc(2000, absl::crc32c_t{2000}));
|
| 42 |
+
rep->removed_prefix =
|
| 43 |
+
absl::crc_internal::CrcCordState::PrefixCrc(500, absl::crc32c_t{500});
|
| 44 |
+
|
| 45 |
+
// The removed_prefix means state is not normalized.
|
| 46 |
+
EXPECT_FALSE(state.IsNormalized());
|
| 47 |
+
|
| 48 |
+
absl::crc32c_t crc = state.Checksum();
|
| 49 |
+
state.Normalize();
|
| 50 |
+
EXPECT_TRUE(state.IsNormalized());
|
| 51 |
+
|
| 52 |
+
// The checksum should not change as a result of calling Normalize().
|
| 53 |
+
EXPECT_EQ(state.Checksum(), crc);
|
| 54 |
+
EXPECT_EQ(rep->removed_prefix.length, 0);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
TEST(CrcCordState, Copy) {
|
| 58 |
+
absl::crc_internal::CrcCordState state;
|
| 59 |
+
auto* rep = state.mutable_rep();
|
| 60 |
+
rep->prefix_crc.push_back(
|
| 61 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 62 |
+
|
| 63 |
+
absl::crc_internal::CrcCordState copy = state;
|
| 64 |
+
|
| 65 |
+
EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
|
| 66 |
+
EXPECT_EQ(copy.Checksum(), absl::crc32c_t{1000});
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
TEST(CrcCordState, UnsharedSelfCopy) {
|
| 70 |
+
absl::crc_internal::CrcCordState state;
|
| 71 |
+
auto* rep = state.mutable_rep();
|
| 72 |
+
rep->prefix_crc.push_back(
|
| 73 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 74 |
+
|
| 75 |
+
const absl::crc_internal::CrcCordState& ref = state;
|
| 76 |
+
state = ref;
|
| 77 |
+
|
| 78 |
+
EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
TEST(CrcCordState, Move) {
|
| 82 |
+
absl::crc_internal::CrcCordState state;
|
| 83 |
+
auto* rep = state.mutable_rep();
|
| 84 |
+
rep->prefix_crc.push_back(
|
| 85 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 86 |
+
|
| 87 |
+
absl::crc_internal::CrcCordState moved = std::move(state);
|
| 88 |
+
EXPECT_EQ(moved.Checksum(), absl::crc32c_t{1000});
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
TEST(CrcCordState, UnsharedSelfMove) {
|
| 92 |
+
absl::crc_internal::CrcCordState state;
|
| 93 |
+
auto* rep = state.mutable_rep();
|
| 94 |
+
rep->prefix_crc.push_back(
|
| 95 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 96 |
+
|
| 97 |
+
absl::crc_internal::CrcCordState& ref = state;
|
| 98 |
+
state = std::move(ref);
|
| 99 |
+
|
| 100 |
+
EXPECT_EQ(state.Checksum(), absl::crc32c_t{1000});
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
TEST(CrcCordState, PoisonDefault) {
|
| 104 |
+
absl::crc_internal::CrcCordState state;
|
| 105 |
+
state.Poison();
|
| 106 |
+
EXPECT_NE(state.Checksum(), absl::crc32c_t{0});
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
TEST(CrcCordState, PoisonData) {
|
| 110 |
+
absl::crc_internal::CrcCordState state;
|
| 111 |
+
auto* rep = state.mutable_rep();
|
| 112 |
+
rep->prefix_crc.push_back(
|
| 113 |
+
absl::crc_internal::CrcCordState::PrefixCrc(1000, absl::crc32c_t{1000}));
|
| 114 |
+
rep->prefix_crc.push_back(
|
| 115 |
+
absl::crc_internal::CrcCordState::PrefixCrc(2000, absl::crc32c_t{2000}));
|
| 116 |
+
rep->removed_prefix =
|
| 117 |
+
absl::crc_internal::CrcCordState::PrefixCrc(500, absl::crc32c_t{500});
|
| 118 |
+
|
| 119 |
+
absl::crc32c_t crc = state.Checksum();
|
| 120 |
+
state.Poison();
|
| 121 |
+
EXPECT_NE(state.Checksum(), crc);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
} // namespace
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy.h
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CRC_INTERNAL_CRC_MEMCPY_H_
|
| 16 |
+
#define ABSL_CRC_INTERNAL_CRC_MEMCPY_H_
|
| 17 |
+
|
| 18 |
+
#include <cstddef>
|
| 19 |
+
#include <memory>
|
| 20 |
+
|
| 21 |
+
#include "absl/base/config.h"
|
| 22 |
+
#include "absl/crc/crc32c.h"
|
| 23 |
+
#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
|
| 24 |
+
|
| 25 |
+
// Defined if the class AcceleratedCrcMemcpyEngine exists.
|
| 26 |
+
// TODO(b/299127771): Consider relaxing the pclmul requirement once the other
|
| 27 |
+
// intrinsics are conditionally compiled without it.
|
| 28 |
+
#if defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
|
| 29 |
+
#define ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE 1
|
| 30 |
+
#elif defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD)
|
| 31 |
+
#define ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE 1
|
| 32 |
+
#endif
|
| 33 |
+
|
| 34 |
+
namespace absl {
|
| 35 |
+
ABSL_NAMESPACE_BEGIN
|
| 36 |
+
namespace crc_internal {
|
| 37 |
+
|
| 38 |
+
class CrcMemcpyEngine {
|
| 39 |
+
public:
|
| 40 |
+
virtual ~CrcMemcpyEngine() = default;
|
| 41 |
+
|
| 42 |
+
virtual crc32c_t Compute(void* __restrict dst, const void* __restrict src,
|
| 43 |
+
std::size_t length, crc32c_t initial_crc) const = 0;
|
| 44 |
+
|
| 45 |
+
protected:
|
| 46 |
+
CrcMemcpyEngine() = default;
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
class CrcMemcpy {
|
| 50 |
+
public:
|
| 51 |
+
static crc32c_t CrcAndCopy(void* __restrict dst, const void* __restrict src,
|
| 52 |
+
std::size_t length,
|
| 53 |
+
crc32c_t initial_crc = crc32c_t{0},
|
| 54 |
+
bool non_temporal = false) {
|
| 55 |
+
static const ArchSpecificEngines engines = GetArchSpecificEngines();
|
| 56 |
+
auto* engine = non_temporal ? engines.non_temporal : engines.temporal;
|
| 57 |
+
return engine->Compute(dst, src, length, initial_crc);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
// For testing only: get an architecture-specific engine for tests.
|
| 61 |
+
static std::unique_ptr<CrcMemcpyEngine> GetTestEngine(int vector,
|
| 62 |
+
int integer);
|
| 63 |
+
|
| 64 |
+
private:
|
| 65 |
+
struct ArchSpecificEngines {
|
| 66 |
+
CrcMemcpyEngine* temporal;
|
| 67 |
+
CrcMemcpyEngine* non_temporal;
|
| 68 |
+
};
|
| 69 |
+
|
| 70 |
+
static ArchSpecificEngines GetArchSpecificEngines();
|
| 71 |
+
};
|
| 72 |
+
|
| 73 |
+
// Fallback CRC-memcpy engine.
|
| 74 |
+
class FallbackCrcMemcpyEngine : public CrcMemcpyEngine {
|
| 75 |
+
public:
|
| 76 |
+
FallbackCrcMemcpyEngine() = default;
|
| 77 |
+
FallbackCrcMemcpyEngine(const FallbackCrcMemcpyEngine&) = delete;
|
| 78 |
+
FallbackCrcMemcpyEngine operator=(const FallbackCrcMemcpyEngine&) = delete;
|
| 79 |
+
|
| 80 |
+
crc32c_t Compute(void* __restrict dst, const void* __restrict src,
|
| 81 |
+
std::size_t length, crc32c_t initial_crc) const override;
|
| 82 |
+
};
|
| 83 |
+
|
| 84 |
+
// CRC Non-Temporal-Memcpy engine.
|
| 85 |
+
class CrcNonTemporalMemcpyEngine : public CrcMemcpyEngine {
|
| 86 |
+
public:
|
| 87 |
+
CrcNonTemporalMemcpyEngine() = default;
|
| 88 |
+
CrcNonTemporalMemcpyEngine(const CrcNonTemporalMemcpyEngine&) = delete;
|
| 89 |
+
CrcNonTemporalMemcpyEngine operator=(const CrcNonTemporalMemcpyEngine&) =
|
| 90 |
+
delete;
|
| 91 |
+
|
| 92 |
+
crc32c_t Compute(void* __restrict dst, const void* __restrict src,
|
| 93 |
+
std::size_t length, crc32c_t initial_crc) const override;
|
| 94 |
+
};
|
| 95 |
+
|
| 96 |
+
// CRC Non-Temporal-Memcpy AVX engine.
|
| 97 |
+
class CrcNonTemporalMemcpyAVXEngine : public CrcMemcpyEngine {
|
| 98 |
+
public:
|
| 99 |
+
CrcNonTemporalMemcpyAVXEngine() = default;
|
| 100 |
+
CrcNonTemporalMemcpyAVXEngine(const CrcNonTemporalMemcpyAVXEngine&) = delete;
|
| 101 |
+
CrcNonTemporalMemcpyAVXEngine operator=(
|
| 102 |
+
const CrcNonTemporalMemcpyAVXEngine&) = delete;
|
| 103 |
+
|
| 104 |
+
crc32c_t Compute(void* __restrict dst, const void* __restrict src,
|
| 105 |
+
std::size_t length, crc32c_t initial_crc) const override;
|
| 106 |
+
};
|
| 107 |
+
|
| 108 |
+
// Copy source to destination and return the CRC32C of the data copied. If an
|
| 109 |
+
// accelerated version is available, use the accelerated version, otherwise use
|
| 110 |
+
// the generic fallback version.
|
| 111 |
+
inline crc32c_t Crc32CAndCopy(void* __restrict dst, const void* __restrict src,
|
| 112 |
+
std::size_t length,
|
| 113 |
+
crc32c_t initial_crc = crc32c_t{0},
|
| 114 |
+
bool non_temporal = false) {
|
| 115 |
+
return CrcMemcpy::CrcAndCopy(dst, src, length, initial_crc, non_temporal);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
} // namespace crc_internal
|
| 119 |
+
ABSL_NAMESPACE_END
|
| 120 |
+
} // namespace absl
|
| 121 |
+
|
| 122 |
+
#endif // ABSL_CRC_INTERNAL_CRC_MEMCPY_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_fallback.cc
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include <cstdint>
|
| 16 |
+
#include <memory>
|
| 17 |
+
|
| 18 |
+
#include "absl/base/config.h"
|
| 19 |
+
#include "absl/crc/crc32c.h"
|
| 20 |
+
#include "absl/crc/internal/crc_memcpy.h"
|
| 21 |
+
|
| 22 |
+
namespace absl {
|
| 23 |
+
ABSL_NAMESPACE_BEGIN
|
| 24 |
+
namespace crc_internal {
|
| 25 |
+
|
| 26 |
+
absl::crc32c_t FallbackCrcMemcpyEngine::Compute(void* __restrict dst,
|
| 27 |
+
const void* __restrict src,
|
| 28 |
+
std::size_t length,
|
| 29 |
+
crc32c_t initial_crc) const {
|
| 30 |
+
constexpr size_t kBlockSize = 8192;
|
| 31 |
+
absl::crc32c_t crc = initial_crc;
|
| 32 |
+
|
| 33 |
+
const char* src_bytes = reinterpret_cast<const char*>(src);
|
| 34 |
+
char* dst_bytes = reinterpret_cast<char*>(dst);
|
| 35 |
+
|
| 36 |
+
// Copy + CRC loop - run 8k chunks until we are out of full chunks. CRC
|
| 37 |
+
// then copy was found to be slightly more efficient in our test cases.
|
| 38 |
+
std::size_t offset = 0;
|
| 39 |
+
for (; offset + kBlockSize < length; offset += kBlockSize) {
|
| 40 |
+
crc = absl::ExtendCrc32c(crc,
|
| 41 |
+
absl::string_view(src_bytes + offset, kBlockSize));
|
| 42 |
+
memcpy(dst_bytes + offset, src_bytes + offset, kBlockSize);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
// Save some work if length is 0.
|
| 46 |
+
if (offset < length) {
|
| 47 |
+
std::size_t final_copy_size = length - offset;
|
| 48 |
+
crc = absl::ExtendCrc32c(
|
| 49 |
+
crc, absl::string_view(src_bytes + offset, final_copy_size));
|
| 50 |
+
memcpy(dst_bytes + offset, src_bytes + offset, final_copy_size);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
return crc;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
// Compile the following only if we don't have
|
| 57 |
+
#if !defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) && \
|
| 58 |
+
!defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
|
| 59 |
+
|
| 60 |
+
CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
|
| 61 |
+
CrcMemcpy::ArchSpecificEngines engines;
|
| 62 |
+
engines.temporal = new FallbackCrcMemcpyEngine();
|
| 63 |
+
engines.non_temporal = new FallbackCrcMemcpyEngine();
|
| 64 |
+
return engines;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int /*vector*/,
|
| 68 |
+
int /*integer*/) {
|
| 69 |
+
return std::make_unique<FallbackCrcMemcpyEngine>();
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
#endif // !ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE &&
|
| 73 |
+
// !ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
|
| 74 |
+
|
| 75 |
+
} // namespace crc_internal
|
| 76 |
+
ABSL_NAMESPACE_END
|
| 77 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_test.cc
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/crc/internal/crc_memcpy.h"
|
| 16 |
+
|
| 17 |
+
#include <cstddef>
|
| 18 |
+
#include <cstdint>
|
| 19 |
+
#include <cstring>
|
| 20 |
+
#include <limits>
|
| 21 |
+
#include <memory>
|
| 22 |
+
#include <string>
|
| 23 |
+
#include <utility>
|
| 24 |
+
|
| 25 |
+
#include "gtest/gtest.h"
|
| 26 |
+
#include "absl/crc/crc32c.h"
|
| 27 |
+
#include "absl/memory/memory.h"
|
| 28 |
+
#include "absl/random/distributions.h"
|
| 29 |
+
#include "absl/random/random.h"
|
| 30 |
+
#include "absl/strings/str_cat.h"
|
| 31 |
+
#include "absl/strings/string_view.h"
|
| 32 |
+
|
| 33 |
+
namespace {
|
| 34 |
+
|
| 35 |
+
enum CrcEngine {
|
| 36 |
+
ACCELERATED = 0,
|
| 37 |
+
NONTEMPORAL = 1,
|
| 38 |
+
FALLBACK = 2,
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
// Correctness tests:
|
| 42 |
+
// - Every source/destination byte alignment 0-15, every size 0-511 bytes
|
| 43 |
+
// - Arbitrarily aligned source, large size
|
| 44 |
+
template <size_t max_size>
|
| 45 |
+
class CrcMemcpyTest : public testing::Test {
|
| 46 |
+
protected:
|
| 47 |
+
CrcMemcpyTest() {
|
| 48 |
+
source_ = std::make_unique<char[]>(kSize);
|
| 49 |
+
destination_ = std::make_unique<char[]>(kSize);
|
| 50 |
+
}
|
| 51 |
+
static constexpr size_t kAlignment = 16;
|
| 52 |
+
static constexpr size_t kMaxCopySize = max_size;
|
| 53 |
+
static constexpr size_t kSize = kAlignment + kMaxCopySize;
|
| 54 |
+
std::unique_ptr<char[]> source_;
|
| 55 |
+
std::unique_ptr<char[]> destination_;
|
| 56 |
+
|
| 57 |
+
absl::BitGen gen_;
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
// Small test is slightly larger 4096 bytes to allow coverage of the "large"
|
| 61 |
+
// copy function. The minimum size to exercise all code paths in that function
|
| 62 |
+
// would be around 256 consecutive tests (getting every possible tail value
|
| 63 |
+
// and 0-2 small copy loops after the main block), so testing from 4096-4500
|
| 64 |
+
// will cover all of those code paths multiple times.
|
| 65 |
+
typedef CrcMemcpyTest<4500> CrcSmallTest;
|
| 66 |
+
typedef CrcMemcpyTest<(1 << 24)> CrcLargeTest;
|
| 67 |
+
// Parametrize the small test so that it can be done with all configurations.
|
| 68 |
+
template <typename ParamsT>
|
| 69 |
+
class EngineParamTestTemplate : public CrcSmallTest,
|
| 70 |
+
public ::testing::WithParamInterface<ParamsT> {
|
| 71 |
+
protected:
|
| 72 |
+
EngineParamTestTemplate() {
|
| 73 |
+
if (GetParam().crc_engine_selector == FALLBACK) {
|
| 74 |
+
engine_ = std::make_unique<absl::crc_internal::FallbackCrcMemcpyEngine>();
|
| 75 |
+
} else if (GetParam().crc_engine_selector == NONTEMPORAL) {
|
| 76 |
+
engine_ =
|
| 77 |
+
std::make_unique<absl::crc_internal::CrcNonTemporalMemcpyEngine>();
|
| 78 |
+
} else {
|
| 79 |
+
engine_ = absl::crc_internal::CrcMemcpy::GetTestEngine(
|
| 80 |
+
GetParam().vector_lanes, GetParam().integer_lanes);
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// Convenience method.
|
| 85 |
+
ParamsT GetParam() const {
|
| 86 |
+
return ::testing::WithParamInterface<ParamsT>::GetParam();
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
std::unique_ptr<absl::crc_internal::CrcMemcpyEngine> engine_;
|
| 90 |
+
};
|
| 91 |
+
struct TestParams {
|
| 92 |
+
CrcEngine crc_engine_selector = ACCELERATED;
|
| 93 |
+
int vector_lanes = 0;
|
| 94 |
+
int integer_lanes = 0;
|
| 95 |
+
};
|
| 96 |
+
using EngineParamTest = EngineParamTestTemplate<TestParams>;
|
| 97 |
+
// SmallCorrectness is designed to exercise every possible set of code paths
|
| 98 |
+
// in the memcpy code, not including the loop.
|
| 99 |
+
TEST_P(EngineParamTest, SmallCorrectnessCheckSourceAlignment) {
|
| 100 |
+
constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
|
| 101 |
+
|
| 102 |
+
for (size_t source_alignment = 0; source_alignment < kAlignment;
|
| 103 |
+
source_alignment++) {
|
| 104 |
+
for (auto size : kTestSizes) {
|
| 105 |
+
char* base_data = static_cast<char*>(source_.get()) + source_alignment;
|
| 106 |
+
for (size_t i = 0; i < size; i++) {
|
| 107 |
+
*(base_data + i) =
|
| 108 |
+
static_cast<char>(absl::Uniform<unsigned char>(gen_));
|
| 109 |
+
}
|
| 110 |
+
SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
|
| 111 |
+
GetParam().integer_lanes, ">, ", "size=", size,
|
| 112 |
+
", source_alignment=", source_alignment));
|
| 113 |
+
absl::crc32c_t initial_crc =
|
| 114 |
+
absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
|
| 115 |
+
absl::crc32c_t experiment_crc =
|
| 116 |
+
engine_->Compute(destination_.get(), source_.get() + source_alignment,
|
| 117 |
+
size, initial_crc);
|
| 118 |
+
// Check the memory region to make sure it is the same
|
| 119 |
+
int mem_comparison =
|
| 120 |
+
memcmp(destination_.get(), source_.get() + source_alignment, size);
|
| 121 |
+
SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size,
|
| 122 |
+
" with source alignment: ", source_alignment));
|
| 123 |
+
ASSERT_EQ(mem_comparison, 0);
|
| 124 |
+
absl::crc32c_t baseline_crc = absl::ExtendCrc32c(
|
| 125 |
+
initial_crc,
|
| 126 |
+
absl::string_view(
|
| 127 |
+
static_cast<char*>(source_.get()) + source_alignment, size));
|
| 128 |
+
ASSERT_EQ(baseline_crc, experiment_crc);
|
| 129 |
+
}
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
TEST_P(EngineParamTest, SmallCorrectnessCheckDestAlignment) {
|
| 134 |
+
constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize};
|
| 135 |
+
|
| 136 |
+
for (size_t dest_alignment = 0; dest_alignment < kAlignment;
|
| 137 |
+
dest_alignment++) {
|
| 138 |
+
for (auto size : kTestSizes) {
|
| 139 |
+
char* base_data = static_cast<char*>(source_.get());
|
| 140 |
+
for (size_t i = 0; i < size; i++) {
|
| 141 |
+
*(base_data + i) =
|
| 142 |
+
static_cast<char>(absl::Uniform<unsigned char>(gen_));
|
| 143 |
+
}
|
| 144 |
+
SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",",
|
| 145 |
+
GetParam().integer_lanes, ">, ", "size=", size,
|
| 146 |
+
", destination_alignment=", dest_alignment));
|
| 147 |
+
absl::crc32c_t initial_crc =
|
| 148 |
+
absl::crc32c_t{absl::Uniform<uint32_t>(gen_)};
|
| 149 |
+
absl::crc32c_t experiment_crc =
|
| 150 |
+
engine_->Compute(destination_.get() + dest_alignment, source_.get(),
|
| 151 |
+
size, initial_crc);
|
| 152 |
+
// Check the memory region to make sure it is the same
|
| 153 |
+
int mem_comparison =
|
| 154 |
+
memcmp(destination_.get() + dest_alignment, source_.get(), size);
|
| 155 |
+
SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size,
|
| 156 |
+
" with dest alignment: ", dest_alignment));
|
| 157 |
+
ASSERT_EQ(mem_comparison, 0);
|
| 158 |
+
absl::crc32c_t baseline_crc = absl::ExtendCrc32c(
|
| 159 |
+
initial_crc,
|
| 160 |
+
absl::string_view(static_cast<char*>(source_.get()), size));
|
| 161 |
+
ASSERT_EQ(baseline_crc, experiment_crc);
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
INSTANTIATE_TEST_SUITE_P(EngineParamTest, EngineParamTest,
|
| 167 |
+
::testing::Values(
|
| 168 |
+
// Tests for configurations that may occur in prod.
|
| 169 |
+
TestParams{ACCELERATED, 3, 0},
|
| 170 |
+
TestParams{ACCELERATED, 1, 2},
|
| 171 |
+
TestParams{ACCELERATED, 1, 0},
|
| 172 |
+
// Fallback test.
|
| 173 |
+
TestParams{FALLBACK, 0, 0},
|
| 174 |
+
// Non Temporal
|
| 175 |
+
TestParams{NONTEMPORAL, 0, 0}));
|
| 176 |
+
|
| 177 |
+
} // namespace
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_memcpy_x86_arm_combined.cc
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
// Simultaneous memcopy and CRC-32C for x86-64 and ARM 64. Uses integer
|
| 16 |
+
// registers because XMM registers do not support the CRC instruction (yet).
|
| 17 |
+
// While copying, compute the running CRC of the data being copied.
|
| 18 |
+
//
|
| 19 |
+
// It is assumed that any CPU running this code has SSE4.2 instructions
|
| 20 |
+
// available (for CRC32C). This file will do nothing if that is not true.
|
| 21 |
+
//
|
| 22 |
+
// The CRC instruction has a 3-byte latency, and we are stressing the ALU ports
|
| 23 |
+
// here (unlike a traditional memcopy, which has almost no ALU use), so we will
|
| 24 |
+
// need to copy in such a way that the CRC unit is used efficiently. We have two
|
| 25 |
+
// regimes in this code:
|
| 26 |
+
// 1. For operations of size < kCrcSmallSize, do the CRC then the memcpy
|
| 27 |
+
// 2. For operations of size > kCrcSmallSize:
|
| 28 |
+
// a) compute an initial CRC + copy on a small amount of data to align the
|
| 29 |
+
// destination pointer on a 16-byte boundary.
|
| 30 |
+
// b) Split the data into 3 main regions and a tail (smaller than 48 bytes)
|
| 31 |
+
// c) Do the copy and CRC of the 3 main regions, interleaving (start with
|
| 32 |
+
// full cache line copies for each region, then move to single 16 byte
|
| 33 |
+
// pieces per region).
|
| 34 |
+
// d) Combine the CRCs with CRC32C::Concat.
|
| 35 |
+
// e) Copy the tail and extend the CRC with the CRC of the tail.
|
| 36 |
+
// This method is not ideal for op sizes between ~1k and ~8k because CRC::Concat
|
| 37 |
+
// takes a significant amount of time. A medium-sized approach could be added
|
| 38 |
+
// using 3 CRCs over fixed-size blocks where the zero-extensions required for
|
| 39 |
+
// CRC32C::Concat can be precomputed.
|
| 40 |
+
|
| 41 |
+
#ifdef __SSE4_2__
|
| 42 |
+
#include <immintrin.h>
|
| 43 |
+
#endif
|
| 44 |
+
|
| 45 |
+
#ifdef _MSC_VER
|
| 46 |
+
#include <intrin.h>
|
| 47 |
+
#endif
|
| 48 |
+
|
| 49 |
+
#include <array>
|
| 50 |
+
#include <cstddef>
|
| 51 |
+
#include <cstdint>
|
| 52 |
+
#include <cstring>
|
| 53 |
+
#include <memory>
|
| 54 |
+
|
| 55 |
+
#include "absl/base/config.h"
|
| 56 |
+
#include "absl/base/optimization.h"
|
| 57 |
+
#include "absl/base/prefetch.h"
|
| 58 |
+
#include "absl/crc/crc32c.h"
|
| 59 |
+
#include "absl/crc/internal/cpu_detect.h"
|
| 60 |
+
#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
|
| 61 |
+
#include "absl/crc/internal/crc_memcpy.h"
|
| 62 |
+
#include "absl/strings/string_view.h"
|
| 63 |
+
|
| 64 |
+
#if defined(ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE) || \
|
| 65 |
+
defined(ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE)
|
| 66 |
+
|
| 67 |
+
namespace absl {
|
| 68 |
+
ABSL_NAMESPACE_BEGIN
|
| 69 |
+
namespace crc_internal {
|
| 70 |
+
|
| 71 |
+
namespace {
|
| 72 |
+
|
| 73 |
+
inline crc32c_t ShortCrcCopy(char* dst, const char* src, std::size_t length,
|
| 74 |
+
crc32c_t crc) {
|
| 75 |
+
// Small copy: just go 1 byte at a time: being nice to the branch predictor
|
| 76 |
+
// is more important here than anything else
|
| 77 |
+
uint32_t crc_uint32 = static_cast<uint32_t>(crc);
|
| 78 |
+
for (std::size_t i = 0; i < length; i++) {
|
| 79 |
+
uint8_t data = *reinterpret_cast<const uint8_t*>(src);
|
| 80 |
+
crc_uint32 = CRC32_u8(crc_uint32, data);
|
| 81 |
+
*reinterpret_cast<uint8_t*>(dst) = data;
|
| 82 |
+
++src;
|
| 83 |
+
++dst;
|
| 84 |
+
}
|
| 85 |
+
return crc32c_t{crc_uint32};
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
constexpr size_t kIntLoadsPerVec = sizeof(V128) / sizeof(uint64_t);
|
| 89 |
+
|
| 90 |
+
// Common function for copying the tails of multiple large regions.
|
| 91 |
+
template <size_t vec_regions, size_t int_regions>
|
| 92 |
+
inline void LargeTailCopy(crc32c_t* crcs, char** dst, const char** src,
|
| 93 |
+
size_t region_size, size_t copy_rounds) {
|
| 94 |
+
std::array<V128, vec_regions> data;
|
| 95 |
+
std::array<uint64_t, kIntLoadsPerVec * int_regions> int_data;
|
| 96 |
+
|
| 97 |
+
while (copy_rounds > 0) {
|
| 98 |
+
for (size_t i = 0; i < vec_regions; i++) {
|
| 99 |
+
size_t region = i;
|
| 100 |
+
|
| 101 |
+
auto* vsrc = reinterpret_cast<const V128*>(*src + region_size * region);
|
| 102 |
+
auto* vdst = reinterpret_cast<V128*>(*dst + region_size * region);
|
| 103 |
+
|
| 104 |
+
// Load the blocks, unaligned
|
| 105 |
+
data[i] = V128_LoadU(vsrc);
|
| 106 |
+
|
| 107 |
+
// Store the blocks, aligned
|
| 108 |
+
V128_Store(vdst, data[i]);
|
| 109 |
+
|
| 110 |
+
// Compute the running CRC
|
| 111 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(
|
| 112 |
+
CRC32_u64(static_cast<uint32_t>(crcs[region]),
|
| 113 |
+
static_cast<uint64_t>(V128_Extract64<0>(data[i]))))};
|
| 114 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(
|
| 115 |
+
CRC32_u64(static_cast<uint32_t>(crcs[region]),
|
| 116 |
+
static_cast<uint64_t>(V128_Extract64<1>(data[i]))))};
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
for (size_t i = 0; i < int_regions; i++) {
|
| 120 |
+
size_t region = vec_regions + i;
|
| 121 |
+
|
| 122 |
+
auto* usrc =
|
| 123 |
+
reinterpret_cast<const uint64_t*>(*src + region_size * region);
|
| 124 |
+
auto* udst = reinterpret_cast<uint64_t*>(*dst + region_size * region);
|
| 125 |
+
|
| 126 |
+
for (size_t j = 0; j < kIntLoadsPerVec; j++) {
|
| 127 |
+
size_t data_index = i * kIntLoadsPerVec + j;
|
| 128 |
+
|
| 129 |
+
int_data[data_index] = *(usrc + j);
|
| 130 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
|
| 131 |
+
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
|
| 132 |
+
|
| 133 |
+
*(udst + j) = int_data[data_index];
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
// Increment pointers
|
| 138 |
+
*src += sizeof(V128);
|
| 139 |
+
*dst += sizeof(V128);
|
| 140 |
+
--copy_rounds;
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
} // namespace
|
| 145 |
+
|
| 146 |
+
template <size_t vec_regions, size_t int_regions>
|
| 147 |
+
class AcceleratedCrcMemcpyEngine : public CrcMemcpyEngine {
|
| 148 |
+
public:
|
| 149 |
+
AcceleratedCrcMemcpyEngine() = default;
|
| 150 |
+
AcceleratedCrcMemcpyEngine(const AcceleratedCrcMemcpyEngine&) = delete;
|
| 151 |
+
AcceleratedCrcMemcpyEngine operator=(const AcceleratedCrcMemcpyEngine&) =
|
| 152 |
+
delete;
|
| 153 |
+
|
| 154 |
+
crc32c_t Compute(void* __restrict dst, const void* __restrict src,
|
| 155 |
+
std::size_t length, crc32c_t initial_crc) const override;
|
| 156 |
+
};
|
| 157 |
+
|
| 158 |
+
template <size_t vec_regions, size_t int_regions>
|
| 159 |
+
crc32c_t AcceleratedCrcMemcpyEngine<vec_regions, int_regions>::Compute(
|
| 160 |
+
void* __restrict dst, const void* __restrict src, std::size_t length,
|
| 161 |
+
crc32c_t initial_crc) const {
|
| 162 |
+
constexpr std::size_t kRegions = vec_regions + int_regions;
|
| 163 |
+
static_assert(kRegions > 0, "Must specify at least one region.");
|
| 164 |
+
constexpr uint32_t kCrcDataXor = uint32_t{0xffffffff};
|
| 165 |
+
constexpr std::size_t kBlockSize = sizeof(V128);
|
| 166 |
+
constexpr std::size_t kCopyRoundSize = kRegions * kBlockSize;
|
| 167 |
+
|
| 168 |
+
// Number of blocks per cacheline.
|
| 169 |
+
constexpr std::size_t kBlocksPerCacheLine = ABSL_CACHELINE_SIZE / kBlockSize;
|
| 170 |
+
|
| 171 |
+
char* dst_bytes = static_cast<char*>(dst);
|
| 172 |
+
const char* src_bytes = static_cast<const char*>(src);
|
| 173 |
+
|
| 174 |
+
// Make sure that one prefetch per big block is enough to cover the whole
|
| 175 |
+
// dataset, and we don't prefetch too much.
|
| 176 |
+
static_assert(ABSL_CACHELINE_SIZE % kBlockSize == 0,
|
| 177 |
+
"Cache lines are not divided evenly into blocks, may have "
|
| 178 |
+
"unintended behavior!");
|
| 179 |
+
|
| 180 |
+
// Experimentally-determined boundary between a small and large copy.
|
| 181 |
+
// Below this number, spin-up and concatenation of CRCs takes enough time that
|
| 182 |
+
// it kills the throughput gains of using 3 regions and wide vectors.
|
| 183 |
+
constexpr size_t kCrcSmallSize = 256;
|
| 184 |
+
|
| 185 |
+
// Experimentally-determined prefetch distance. Main loop copies will
|
| 186 |
+
// prefeth data 2 cache lines ahead.
|
| 187 |
+
constexpr std::size_t kPrefetchAhead = 2 * ABSL_CACHELINE_SIZE;
|
| 188 |
+
|
| 189 |
+
// Small-size CRC-memcpy : just do CRC + memcpy
|
| 190 |
+
if (length < kCrcSmallSize) {
|
| 191 |
+
crc32c_t crc =
|
| 192 |
+
ExtendCrc32c(initial_crc, absl::string_view(src_bytes, length));
|
| 193 |
+
memcpy(dst, src, length);
|
| 194 |
+
return crc;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
// Start work on the CRC: undo the XOR from the previous calculation or set up
|
| 198 |
+
// the initial value of the CRC.
|
| 199 |
+
// initial_crc ^= kCrcDataXor;
|
| 200 |
+
initial_crc = crc32c_t{static_cast<uint32_t>(initial_crc) ^ kCrcDataXor};
|
| 201 |
+
|
| 202 |
+
// Do an initial alignment copy, so we can use aligned store instructions to
|
| 203 |
+
// the destination pointer. We align the destination pointer because the
|
| 204 |
+
// penalty for an unaligned load is small compared to the penalty of an
|
| 205 |
+
// unaligned store on modern CPUs.
|
| 206 |
+
std::size_t bytes_from_last_aligned =
|
| 207 |
+
reinterpret_cast<uintptr_t>(dst) & (kBlockSize - 1);
|
| 208 |
+
if (bytes_from_last_aligned != 0) {
|
| 209 |
+
std::size_t bytes_for_alignment = kBlockSize - bytes_from_last_aligned;
|
| 210 |
+
|
| 211 |
+
// Do the short-sized copy and CRC.
|
| 212 |
+
initial_crc =
|
| 213 |
+
ShortCrcCopy(dst_bytes, src_bytes, bytes_for_alignment, initial_crc);
|
| 214 |
+
src_bytes += bytes_for_alignment;
|
| 215 |
+
dst_bytes += bytes_for_alignment;
|
| 216 |
+
length -= bytes_for_alignment;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
// We are going to do the copy and CRC in kRegions regions to make sure that
|
| 220 |
+
// we can saturate the CRC unit. The CRCs will be combined at the end of the
|
| 221 |
+
// run. Copying will use the SSE registers, and we will extract words from
|
| 222 |
+
// the SSE registers to add to the CRC. Initially, we run the loop one full
|
| 223 |
+
// cache line per region at a time, in order to insert prefetches.
|
| 224 |
+
|
| 225 |
+
// Initialize CRCs for kRegions regions.
|
| 226 |
+
crc32c_t crcs[kRegions];
|
| 227 |
+
crcs[0] = initial_crc;
|
| 228 |
+
for (size_t i = 1; i < kRegions; i++) {
|
| 229 |
+
crcs[i] = crc32c_t{kCrcDataXor};
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
// Find the number of rounds to copy and the region size. Also compute the
|
| 233 |
+
// tail size here.
|
| 234 |
+
size_t copy_rounds = length / kCopyRoundSize;
|
| 235 |
+
|
| 236 |
+
// Find the size of each region and the size of the tail.
|
| 237 |
+
const std::size_t region_size = copy_rounds * kBlockSize;
|
| 238 |
+
const std::size_t tail_size = length - (kRegions * region_size);
|
| 239 |
+
|
| 240 |
+
// Holding registers for data in each region.
|
| 241 |
+
std::array<V128, vec_regions> vec_data;
|
| 242 |
+
std::array<uint64_t, int_regions * kIntLoadsPerVec> int_data;
|
| 243 |
+
|
| 244 |
+
// Main loop.
|
| 245 |
+
while (copy_rounds > kBlocksPerCacheLine) {
|
| 246 |
+
// Prefetch kPrefetchAhead bytes ahead of each pointer.
|
| 247 |
+
for (size_t i = 0; i < kRegions; i++) {
|
| 248 |
+
absl::PrefetchToLocalCache(src_bytes + kPrefetchAhead + region_size * i);
|
| 249 |
+
#ifdef ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE
|
| 250 |
+
// TODO(b/297082454): investigate dropping prefetch on x86.
|
| 251 |
+
absl::PrefetchToLocalCache(dst_bytes + kPrefetchAhead + region_size * i);
|
| 252 |
+
#endif
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
// Load and store data, computing CRC on the way.
|
| 256 |
+
for (size_t i = 0; i < kBlocksPerCacheLine; i++) {
|
| 257 |
+
// Copy and CRC the data for the CRC regions.
|
| 258 |
+
for (size_t j = 0; j < vec_regions; j++) {
|
| 259 |
+
// Cycle which regions get vector load/store and integer load/store, to
|
| 260 |
+
// engage prefetching logic around vector load/stores and save issue
|
| 261 |
+
// slots by using the integer registers.
|
| 262 |
+
size_t region = (j + i) % kRegions;
|
| 263 |
+
|
| 264 |
+
auto* vsrc =
|
| 265 |
+
reinterpret_cast<const V128*>(src_bytes + region_size * region);
|
| 266 |
+
auto* vdst = reinterpret_cast<V128*>(dst_bytes + region_size * region);
|
| 267 |
+
|
| 268 |
+
// Load and CRC data.
|
| 269 |
+
vec_data[j] = V128_LoadU(vsrc + i);
|
| 270 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(
|
| 271 |
+
CRC32_u64(static_cast<uint32_t>(crcs[region]),
|
| 272 |
+
static_cast<uint64_t>(V128_Extract64<0>(vec_data[j]))))};
|
| 273 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(
|
| 274 |
+
CRC32_u64(static_cast<uint32_t>(crcs[region]),
|
| 275 |
+
static_cast<uint64_t>(V128_Extract64<1>(vec_data[j]))))};
|
| 276 |
+
|
| 277 |
+
// Store the data.
|
| 278 |
+
V128_Store(vdst + i, vec_data[j]);
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
// Preload the partial CRCs for the CLMUL subregions.
|
| 282 |
+
for (size_t j = 0; j < int_regions; j++) {
|
| 283 |
+
// Cycle which regions get vector load/store and integer load/store, to
|
| 284 |
+
// engage prefetching logic around vector load/stores and save issue
|
| 285 |
+
// slots by using the integer registers.
|
| 286 |
+
size_t region = (j + vec_regions + i) % kRegions;
|
| 287 |
+
|
| 288 |
+
auto* usrc =
|
| 289 |
+
reinterpret_cast<const uint64_t*>(src_bytes + region_size * region);
|
| 290 |
+
auto* udst =
|
| 291 |
+
reinterpret_cast<uint64_t*>(dst_bytes + region_size * region);
|
| 292 |
+
|
| 293 |
+
for (size_t k = 0; k < kIntLoadsPerVec; k++) {
|
| 294 |
+
size_t data_index = j * kIntLoadsPerVec + k;
|
| 295 |
+
|
| 296 |
+
// Load and CRC the data.
|
| 297 |
+
int_data[data_index] = *(usrc + i * kIntLoadsPerVec + k);
|
| 298 |
+
crcs[region] = crc32c_t{static_cast<uint32_t>(CRC32_u64(
|
| 299 |
+
static_cast<uint32_t>(crcs[region]), int_data[data_index]))};
|
| 300 |
+
|
| 301 |
+
// Store the data.
|
| 302 |
+
*(udst + i * kIntLoadsPerVec + k) = int_data[data_index];
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
// Increment pointers
|
| 308 |
+
src_bytes += kBlockSize * kBlocksPerCacheLine;
|
| 309 |
+
dst_bytes += kBlockSize * kBlocksPerCacheLine;
|
| 310 |
+
copy_rounds -= kBlocksPerCacheLine;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
// Copy and CRC the tails of each region.
|
| 314 |
+
LargeTailCopy<vec_regions, int_regions>(crcs, &dst_bytes, &src_bytes,
|
| 315 |
+
region_size, copy_rounds);
|
| 316 |
+
|
| 317 |
+
// Move the source and destination pointers to the end of the region
|
| 318 |
+
src_bytes += region_size * (kRegions - 1);
|
| 319 |
+
dst_bytes += region_size * (kRegions - 1);
|
| 320 |
+
|
| 321 |
+
// Copy and CRC the tail through the XMM registers.
|
| 322 |
+
std::size_t tail_blocks = tail_size / kBlockSize;
|
| 323 |
+
LargeTailCopy<0, 1>(&crcs[kRegions - 1], &dst_bytes, &src_bytes, 0,
|
| 324 |
+
tail_blocks);
|
| 325 |
+
|
| 326 |
+
// Final tail copy for under 16 bytes.
|
| 327 |
+
crcs[kRegions - 1] =
|
| 328 |
+
ShortCrcCopy(dst_bytes, src_bytes, tail_size - tail_blocks * kBlockSize,
|
| 329 |
+
crcs[kRegions - 1]);
|
| 330 |
+
|
| 331 |
+
if (kRegions == 1) {
|
| 332 |
+
// If there is only one region, finalize and return its CRC.
|
| 333 |
+
return crc32c_t{static_cast<uint32_t>(crcs[0]) ^ kCrcDataXor};
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
// Finalize the first CRCs: XOR the internal CRCs by the XOR mask to undo the
|
| 337 |
+
// XOR done before doing block copy + CRCs.
|
| 338 |
+
for (size_t i = 0; i + 1 < kRegions; i++) {
|
| 339 |
+
crcs[i] = crc32c_t{static_cast<uint32_t>(crcs[i]) ^ kCrcDataXor};
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
// Build a CRC of the first kRegions - 1 regions.
|
| 343 |
+
crc32c_t full_crc = crcs[0];
|
| 344 |
+
for (size_t i = 1; i + 1 < kRegions; i++) {
|
| 345 |
+
full_crc = ConcatCrc32c(full_crc, crcs[i], region_size);
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
// Finalize and concatenate the final CRC, then return.
|
| 349 |
+
crcs[kRegions - 1] =
|
| 350 |
+
crc32c_t{static_cast<uint32_t>(crcs[kRegions - 1]) ^ kCrcDataXor};
|
| 351 |
+
return ConcatCrc32c(full_crc, crcs[kRegions - 1], region_size + tail_size);
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
CrcMemcpy::ArchSpecificEngines CrcMemcpy::GetArchSpecificEngines() {
|
| 355 |
+
#ifdef UNDEFINED_BEHAVIOR_SANITIZER
|
| 356 |
+
// UBSAN does not play nicely with unaligned loads (which we use a lot).
|
| 357 |
+
// Get the underlying architecture.
|
| 358 |
+
CpuType cpu_type = GetCpuType();
|
| 359 |
+
switch (cpu_type) {
|
| 360 |
+
case CpuType::kAmdRome:
|
| 361 |
+
case CpuType::kAmdNaples:
|
| 362 |
+
case CpuType::kAmdMilan:
|
| 363 |
+
case CpuType::kAmdGenoa:
|
| 364 |
+
case CpuType::kAmdRyzenV3000:
|
| 365 |
+
case CpuType::kIntelCascadelakeXeon:
|
| 366 |
+
case CpuType::kIntelSkylakeXeon:
|
| 367 |
+
case CpuType::kIntelSkylake:
|
| 368 |
+
case CpuType::kIntelBroadwell:
|
| 369 |
+
case CpuType::kIntelHaswell:
|
| 370 |
+
case CpuType::kIntelIvybridge:
|
| 371 |
+
return {
|
| 372 |
+
/*.temporal=*/new FallbackCrcMemcpyEngine(),
|
| 373 |
+
/*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
|
| 374 |
+
};
|
| 375 |
+
// INTEL_SANDYBRIDGE performs better with SSE than AVX.
|
| 376 |
+
case CpuType::kIntelSandybridge:
|
| 377 |
+
return {
|
| 378 |
+
/*.temporal=*/new FallbackCrcMemcpyEngine(),
|
| 379 |
+
/*.non_temporal=*/new CrcNonTemporalMemcpyEngine(),
|
| 380 |
+
};
|
| 381 |
+
default:
|
| 382 |
+
return {/*.temporal=*/new FallbackCrcMemcpyEngine(),
|
| 383 |
+
/*.non_temporal=*/new FallbackCrcMemcpyEngine()};
|
| 384 |
+
}
|
| 385 |
+
#else
|
| 386 |
+
// Get the underlying architecture.
|
| 387 |
+
CpuType cpu_type = GetCpuType();
|
| 388 |
+
switch (cpu_type) {
|
| 389 |
+
// On Zen 2, PEXTRQ uses 2 micro-ops, including one on the vector store port
|
| 390 |
+
// which data movement from the vector registers to the integer registers
|
| 391 |
+
// (where CRC32C happens) to crowd the same units as vector stores. As a
|
| 392 |
+
// result, using that path exclusively causes bottlenecking on this port.
|
| 393 |
+
// We can avoid this bottleneck by using the integer side of the CPU for
|
| 394 |
+
// most operations rather than the vector side. We keep a vector region to
|
| 395 |
+
// engage some of the prefetching logic in the cache hierarchy which seems
|
| 396 |
+
// to give vector instructions special treatment. These prefetch units see
|
| 397 |
+
// strided access to each region, and do the right thing.
|
| 398 |
+
case CpuType::kAmdRome:
|
| 399 |
+
case CpuType::kAmdNaples:
|
| 400 |
+
case CpuType::kAmdMilan:
|
| 401 |
+
case CpuType::kAmdGenoa:
|
| 402 |
+
case CpuType::kAmdRyzenV3000:
|
| 403 |
+
return {
|
| 404 |
+
/*.temporal=*/new AcceleratedCrcMemcpyEngine<1, 2>(),
|
| 405 |
+
/*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
|
| 406 |
+
};
|
| 407 |
+
// PCLMULQDQ is slow and we don't have wide enough issue width to take
|
| 408 |
+
// advantage of it. For an unknown architecture, don't risk using CLMULs.
|
| 409 |
+
case CpuType::kIntelCascadelakeXeon:
|
| 410 |
+
case CpuType::kIntelSkylakeXeon:
|
| 411 |
+
case CpuType::kIntelSkylake:
|
| 412 |
+
case CpuType::kIntelBroadwell:
|
| 413 |
+
case CpuType::kIntelHaswell:
|
| 414 |
+
case CpuType::kIntelIvybridge:
|
| 415 |
+
return {
|
| 416 |
+
/*.temporal=*/new AcceleratedCrcMemcpyEngine<3, 0>(),
|
| 417 |
+
/*.non_temporal=*/new CrcNonTemporalMemcpyAVXEngine(),
|
| 418 |
+
};
|
| 419 |
+
// INTEL_SANDYBRIDGE performs better with SSE than AVX.
|
| 420 |
+
case CpuType::kIntelSandybridge:
|
| 421 |
+
return {
|
| 422 |
+
/*.temporal=*/new AcceleratedCrcMemcpyEngine<3, 0>(),
|
| 423 |
+
/*.non_temporal=*/new CrcNonTemporalMemcpyEngine(),
|
| 424 |
+
};
|
| 425 |
+
default:
|
| 426 |
+
return {/*.temporal=*/new FallbackCrcMemcpyEngine(),
|
| 427 |
+
/*.non_temporal=*/new FallbackCrcMemcpyEngine()};
|
| 428 |
+
}
|
| 429 |
+
#endif // UNDEFINED_BEHAVIOR_SANITIZER
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
// For testing, allow the user to specify which engine they want.
|
| 433 |
+
std::unique_ptr<CrcMemcpyEngine> CrcMemcpy::GetTestEngine(int vector,
|
| 434 |
+
int integer) {
|
| 435 |
+
if (vector == 3 && integer == 0) {
|
| 436 |
+
return std::make_unique<AcceleratedCrcMemcpyEngine<3, 0>>();
|
| 437 |
+
} else if (vector == 1 && integer == 2) {
|
| 438 |
+
return std::make_unique<AcceleratedCrcMemcpyEngine<1, 2>>();
|
| 439 |
+
} else if (vector == 1 && integer == 0) {
|
| 440 |
+
return std::make_unique<AcceleratedCrcMemcpyEngine<1, 0>>();
|
| 441 |
+
}
|
| 442 |
+
return nullptr;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
} // namespace crc_internal
|
| 446 |
+
ABSL_NAMESPACE_END
|
| 447 |
+
} // namespace absl
|
| 448 |
+
|
| 449 |
+
#endif // ABSL_INTERNAL_HAVE_X86_64_ACCELERATED_CRC_MEMCPY_ENGINE ||
|
| 450 |
+
// ABSL_INTERNAL_HAVE_ARM_ACCELERATED_CRC_MEMCPY_ENGINE
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_non_temporal_memcpy.cc
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include <cstdint>
|
| 16 |
+
|
| 17 |
+
#include "absl/base/config.h"
|
| 18 |
+
#include "absl/crc/crc32c.h"
|
| 19 |
+
#include "absl/crc/internal/crc_memcpy.h"
|
| 20 |
+
#include "absl/crc/internal/non_temporal_memcpy.h"
|
| 21 |
+
#include "absl/strings/string_view.h"
|
| 22 |
+
|
| 23 |
+
namespace absl {
|
| 24 |
+
ABSL_NAMESPACE_BEGIN
|
| 25 |
+
namespace crc_internal {
|
| 26 |
+
|
| 27 |
+
crc32c_t CrcNonTemporalMemcpyEngine::Compute(void* __restrict dst,
|
| 28 |
+
const void* __restrict src,
|
| 29 |
+
std::size_t length,
|
| 30 |
+
crc32c_t initial_crc) const {
|
| 31 |
+
constexpr size_t kBlockSize = 8192;
|
| 32 |
+
crc32c_t crc = initial_crc;
|
| 33 |
+
|
| 34 |
+
const char* src_bytes = reinterpret_cast<const char*>(src);
|
| 35 |
+
char* dst_bytes = reinterpret_cast<char*>(dst);
|
| 36 |
+
|
| 37 |
+
// Copy + CRC loop - run 8k chunks until we are out of full chunks.
|
| 38 |
+
std::size_t offset = 0;
|
| 39 |
+
for (; offset + kBlockSize < length; offset += kBlockSize) {
|
| 40 |
+
crc = absl::ExtendCrc32c(crc,
|
| 41 |
+
absl::string_view(src_bytes + offset, kBlockSize));
|
| 42 |
+
non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset,
|
| 43 |
+
kBlockSize);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
// Save some work if length is 0.
|
| 47 |
+
if (offset < length) {
|
| 48 |
+
std::size_t final_copy_size = length - offset;
|
| 49 |
+
crc = ExtendCrc32c(crc,
|
| 50 |
+
absl::string_view(src_bytes + offset, final_copy_size));
|
| 51 |
+
|
| 52 |
+
non_temporal_store_memcpy(dst_bytes + offset, src_bytes + offset,
|
| 53 |
+
final_copy_size);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
return crc;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
crc32c_t CrcNonTemporalMemcpyAVXEngine::Compute(void* __restrict dst,
|
| 60 |
+
const void* __restrict src,
|
| 61 |
+
std::size_t length,
|
| 62 |
+
crc32c_t initial_crc) const {
|
| 63 |
+
constexpr size_t kBlockSize = 8192;
|
| 64 |
+
crc32c_t crc = initial_crc;
|
| 65 |
+
|
| 66 |
+
const char* src_bytes = reinterpret_cast<const char*>(src);
|
| 67 |
+
char* dst_bytes = reinterpret_cast<char*>(dst);
|
| 68 |
+
|
| 69 |
+
// Copy + CRC loop - run 8k chunks until we are out of full chunks.
|
| 70 |
+
std::size_t offset = 0;
|
| 71 |
+
for (; offset + kBlockSize < length; offset += kBlockSize) {
|
| 72 |
+
crc = ExtendCrc32c(crc, absl::string_view(src_bytes + offset, kBlockSize));
|
| 73 |
+
|
| 74 |
+
non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset,
|
| 75 |
+
kBlockSize);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
// Save some work if length is 0.
|
| 79 |
+
if (offset < length) {
|
| 80 |
+
std::size_t final_copy_size = length - offset;
|
| 81 |
+
crc = ExtendCrc32c(crc,
|
| 82 |
+
absl::string_view(src_bytes + offset, final_copy_size));
|
| 83 |
+
|
| 84 |
+
non_temporal_store_memcpy_avx(dst_bytes + offset, src_bytes + offset,
|
| 85 |
+
final_copy_size);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
return crc;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
} // namespace crc_internal
|
| 92 |
+
ABSL_NAMESPACE_END
|
| 93 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/crc/internal/crc_x86_arm_combined.cc
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
// Hardware accelerated CRC32 computation on Intel and ARM architecture.
|
| 16 |
+
|
| 17 |
+
#include <cstddef>
|
| 18 |
+
#include <cstdint>
|
| 19 |
+
#include <memory>
|
| 20 |
+
#include <vector>
|
| 21 |
+
|
| 22 |
+
#include "absl/base/attributes.h"
|
| 23 |
+
#include "absl/base/config.h"
|
| 24 |
+
#include "absl/base/internal/endian.h"
|
| 25 |
+
#include "absl/base/prefetch.h"
|
| 26 |
+
#include "absl/crc/internal/cpu_detect.h"
|
| 27 |
+
#include "absl/crc/internal/crc32_x86_arm_combined_simd.h"
|
| 28 |
+
#include "absl/crc/internal/crc_internal.h"
|
| 29 |
+
#include "absl/memory/memory.h"
|
| 30 |
+
#include "absl/numeric/bits.h"
|
| 31 |
+
|
| 32 |
+
#if defined(ABSL_CRC_INTERNAL_HAVE_ARM_SIMD) || \
|
| 33 |
+
defined(ABSL_CRC_INTERNAL_HAVE_X86_SIMD)
|
| 34 |
+
#define ABSL_INTERNAL_CAN_USE_SIMD_CRC32C
|
| 35 |
+
#endif
|
| 36 |
+
|
| 37 |
+
namespace absl {
|
| 38 |
+
ABSL_NAMESPACE_BEGIN
|
| 39 |
+
namespace crc_internal {
|
| 40 |
+
|
| 41 |
+
#if defined(ABSL_INTERNAL_CAN_USE_SIMD_CRC32C)
|
| 42 |
+
|
| 43 |
+
// Implementation details not exported outside of file
|
| 44 |
+
namespace {
|
| 45 |
+
|
| 46 |
+
// Some machines have CRC acceleration hardware.
|
| 47 |
+
// We can do a faster version of Extend() on such machines.
|
| 48 |
+
class CRC32AcceleratedX86ARMCombined : public CRC32 {
|
| 49 |
+
public:
|
| 50 |
+
CRC32AcceleratedX86ARMCombined() {}
|
| 51 |
+
~CRC32AcceleratedX86ARMCombined() override {}
|
| 52 |
+
void ExtendByZeroes(uint32_t* crc, size_t length) const override;
|
| 53 |
+
uint32_t ComputeZeroConstant(size_t length) const;
|
| 54 |
+
|
| 55 |
+
private:
|
| 56 |
+
CRC32AcceleratedX86ARMCombined(const CRC32AcceleratedX86ARMCombined&) =
|
| 57 |
+
delete;
|
| 58 |
+
CRC32AcceleratedX86ARMCombined& operator=(
|
| 59 |
+
const CRC32AcceleratedX86ARMCombined&) = delete;
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
// Constants for switching between algorithms.
|
| 63 |
+
// Chosen by comparing speed at different powers of 2.
|
| 64 |
+
constexpr size_t kSmallCutoff = 256;
|
| 65 |
+
constexpr size_t kMediumCutoff = 2048;
|
| 66 |
+
|
| 67 |
+
#define ABSL_INTERNAL_STEP1(crc) \
|
| 68 |
+
do { \
|
| 69 |
+
crc = CRC32_u8(static_cast<uint32_t>(crc), *p++); \
|
| 70 |
+
} while (0)
|
| 71 |
+
#define ABSL_INTERNAL_STEP2(crc) \
|
| 72 |
+
do { \
|
| 73 |
+
crc = \
|
| 74 |
+
CRC32_u16(static_cast<uint32_t>(crc), absl::little_endian::Load16(p)); \
|
| 75 |
+
p += 2; \
|
| 76 |
+
} while (0)
|
| 77 |
+
#define ABSL_INTERNAL_STEP4(crc) \
|
| 78 |
+
do { \
|
| 79 |
+
crc = \
|
| 80 |
+
CRC32_u32(static_cast<uint32_t>(crc), absl::little_endian::Load32(p)); \
|
| 81 |
+
p += 4; \
|
| 82 |
+
} while (0)
|
| 83 |
+
#define ABSL_INTERNAL_STEP8(crc, data) \
|
| 84 |
+
do { \
|
| 85 |
+
crc = CRC32_u64(static_cast<uint32_t>(crc), \
|
| 86 |
+
absl::little_endian::Load64(data)); \
|
| 87 |
+
data += 8; \
|
| 88 |
+
} while (0)
|
| 89 |
+
#define ABSL_INTERNAL_STEP8BY2(crc0, crc1, p0, p1) \
|
| 90 |
+
do { \
|
| 91 |
+
ABSL_INTERNAL_STEP8(crc0, p0); \
|
| 92 |
+
ABSL_INTERNAL_STEP8(crc1, p1); \
|
| 93 |
+
} while (0)
|
| 94 |
+
#define ABSL_INTERNAL_STEP8BY3(crc0, crc1, crc2, p0, p1, p2) \
|
| 95 |
+
do { \
|
| 96 |
+
ABSL_INTERNAL_STEP8(crc0, p0); \
|
| 97 |
+
ABSL_INTERNAL_STEP8(crc1, p1); \
|
| 98 |
+
ABSL_INTERNAL_STEP8(crc2, p2); \
|
| 99 |
+
} while (0)
|
| 100 |
+
|
| 101 |
+
namespace {
|
| 102 |
+
|
| 103 |
+
uint32_t multiply(uint32_t a, uint32_t b) {
|
| 104 |
+
V128 shifts = V128_From2x64(0, 1);
|
| 105 |
+
V128 power = V128_From2x64(0, a);
|
| 106 |
+
V128 crc = V128_From2x64(0, b);
|
| 107 |
+
V128 res = V128_PMulLow(power, crc);
|
| 108 |
+
|
| 109 |
+
// Combine crc values
|
| 110 |
+
res = V128_ShiftLeft64(res, shifts);
|
| 111 |
+
return static_cast<uint32_t>(V128_Extract32<1>(res)) ^
|
| 112 |
+
CRC32_u32(0, static_cast<uint32_t>(V128_Low64(res)));
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// Powers of crc32c polynomial, for faster ExtendByZeros.
|
| 116 |
+
// Verified against folly:
|
| 117 |
+
// folly/hash/detail/Crc32CombineDetail.cpp
|
| 118 |
+
constexpr uint32_t kCRC32CPowers[] = {
|
| 119 |
+
0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955, 0xb8fdb1e7,
|
| 120 |
+
0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62, 0x28461564,
|
| 121 |
+
0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f, 0x538586e3,
|
| 122 |
+
0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe, 0xe94ca9bc,
|
| 123 |
+
0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000, 0x00800000,
|
| 124 |
+
0x00008000, 0x82f63b78, 0x6ea2d55c, 0x18b8ea18, 0x510ac59a, 0xb82be955,
|
| 125 |
+
0xb8fdb1e7, 0x88e56f72, 0x74c360a4, 0xe4172b16, 0x0d65762a, 0x35d73a62,
|
| 126 |
+
0x28461564, 0xbf455269, 0xe2ea32dc, 0xfe7740e6, 0xf946610b, 0x3c204f8f,
|
| 127 |
+
0x538586e3, 0x59726915, 0x734d5309, 0xbc1ac763, 0x7d0722cc, 0xd289cabe,
|
| 128 |
+
0xe94ca9bc, 0x05b74f3f, 0xa51e1f42, 0x40000000, 0x20000000, 0x08000000,
|
| 129 |
+
0x00800000, 0x00008000,
|
| 130 |
+
};
|
| 131 |
+
|
| 132 |
+
} // namespace
|
| 133 |
+
|
| 134 |
+
// Compute a magic constant, so that multiplying by it is the same as
|
| 135 |
+
// extending crc by length zeros.
|
| 136 |
+
uint32_t CRC32AcceleratedX86ARMCombined::ComputeZeroConstant(
|
| 137 |
+
size_t length) const {
|
| 138 |
+
// Lowest 2 bits are handled separately in ExtendByZeroes
|
| 139 |
+
length >>= 2;
|
| 140 |
+
|
| 141 |
+
int index = absl::countr_zero(length);
|
| 142 |
+
uint32_t prev = kCRC32CPowers[index];
|
| 143 |
+
length &= length - 1;
|
| 144 |
+
|
| 145 |
+
while (length) {
|
| 146 |
+
// For each bit of length, extend by 2**n zeros.
|
| 147 |
+
index = absl::countr_zero(length);
|
| 148 |
+
prev = multiply(prev, kCRC32CPowers[index]);
|
| 149 |
+
length &= length - 1;
|
| 150 |
+
}
|
| 151 |
+
return prev;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
void CRC32AcceleratedX86ARMCombined::ExtendByZeroes(uint32_t* crc,
|
| 155 |
+
size_t length) const {
|
| 156 |
+
uint32_t val = *crc;
|
| 157 |
+
// Don't bother with multiplication for small length.
|
| 158 |
+
switch (length & 3) {
|
| 159 |
+
case 0:
|
| 160 |
+
break;
|
| 161 |
+
case 1:
|
| 162 |
+
val = CRC32_u8(val, 0);
|
| 163 |
+
break;
|
| 164 |
+
case 2:
|
| 165 |
+
val = CRC32_u16(val, 0);
|
| 166 |
+
break;
|
| 167 |
+
case 3:
|
| 168 |
+
val = CRC32_u8(val, 0);
|
| 169 |
+
val = CRC32_u16(val, 0);
|
| 170 |
+
break;
|
| 171 |
+
}
|
| 172 |
+
if (length > 3) {
|
| 173 |
+
val = multiply(val, ComputeZeroConstant(length));
|
| 174 |
+
}
|
| 175 |
+
*crc = val;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
// Taken from Intel paper "Fast CRC Computation for iSCSI Polynomial Using CRC32
|
| 179 |
+
// Instruction"
|
| 180 |
+
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/crc-iscsi-polynomial-crc32-instruction-paper.pdf
|
| 181 |
+
// We only need every 4th value, because we unroll loop by 4.
|
| 182 |
+
constexpr uint64_t kClmulConstants[] = {
|
| 183 |
+
0x09e4addf8, 0x0ba4fc28e, 0x00d3b6092, 0x09e4addf8, 0x0ab7aff2a,
|
| 184 |
+
0x102f9b8a2, 0x0b9e02b86, 0x00d3b6092, 0x1bf2e8b8a, 0x18266e456,
|
| 185 |
+
0x0d270f1a2, 0x0ab7aff2a, 0x11eef4f8e, 0x083348832, 0x0dd7e3b0c,
|
| 186 |
+
0x0b9e02b86, 0x0271d9844, 0x1b331e26a, 0x06b749fb2, 0x1bf2e8b8a,
|
| 187 |
+
0x0e6fc4e6a, 0x0ce7f39f4, 0x0d7a4825c, 0x0d270f1a2, 0x026f6a60a,
|
| 188 |
+
0x12ed0daac, 0x068bce87a, 0x11eef4f8e, 0x1329d9f7e, 0x0b3e32c28,
|
| 189 |
+
0x0170076fa, 0x0dd7e3b0c, 0x1fae1cc66, 0x010746f3c, 0x086d8e4d2,
|
| 190 |
+
0x0271d9844, 0x0b3af077a, 0x093a5f730, 0x1d88abd4a, 0x06b749fb2,
|
| 191 |
+
0x0c9c8b782, 0x0cec3662e, 0x1ddffc5d4, 0x0e6fc4e6a, 0x168763fa6,
|
| 192 |
+
0x0b0cd4768, 0x19b1afbc4, 0x0d7a4825c, 0x123888b7a, 0x00167d312,
|
| 193 |
+
0x133d7a042, 0x026f6a60a, 0x000bcf5f6, 0x19d34af3a, 0x1af900c24,
|
| 194 |
+
0x068bce87a, 0x06d390dec, 0x16cba8aca, 0x1f16a3418, 0x1329d9f7e,
|
| 195 |
+
0x19fb2a8b0, 0x02178513a, 0x1a0f717c4, 0x0170076fa,
|
| 196 |
+
};
|
| 197 |
+
|
| 198 |
+
enum class CutoffStrategy {
|
| 199 |
+
// Use 3 CRC streams to fold into 1.
|
| 200 |
+
Fold3,
|
| 201 |
+
// Unroll CRC instructions for 64 bytes.
|
| 202 |
+
Unroll64CRC,
|
| 203 |
+
};
|
| 204 |
+
|
| 205 |
+
// Base class for CRC32AcceleratedX86ARMCombinedMultipleStreams containing the
|
| 206 |
+
// methods and data that don't need the template arguments.
|
| 207 |
+
class CRC32AcceleratedX86ARMCombinedMultipleStreamsBase
|
| 208 |
+
: public CRC32AcceleratedX86ARMCombined {
|
| 209 |
+
protected:
|
| 210 |
+
// Update partialCRC with crc of 64 byte block. Calling FinalizePclmulStream
|
| 211 |
+
// would produce a single crc checksum, but it is expensive. PCLMULQDQ has a
|
| 212 |
+
// high latency, so we run 4 128-bit partial checksums that can be reduced to
|
| 213 |
+
// a single value by FinalizePclmulStream later. Computing crc for arbitrary
|
| 214 |
+
// polynomialas with PCLMULQDQ is described in Intel paper "Fast CRC
|
| 215 |
+
// Computation for Generic Polynomials Using PCLMULQDQ Instruction"
|
| 216 |
+
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
|
| 217 |
+
// We are applying it to CRC32C polynomial.
|
| 218 |
+
ABSL_ATTRIBUTE_ALWAYS_INLINE void Process64BytesPclmul(
|
| 219 |
+
const uint8_t* p, V128* partialCRC) const {
|
| 220 |
+
V128 loopMultiplicands = V128_Load(reinterpret_cast<const V128*>(k1k2));
|
| 221 |
+
|
| 222 |
+
V128 partialCRC1 = partialCRC[0];
|
| 223 |
+
V128 partialCRC2 = partialCRC[1];
|
| 224 |
+
V128 partialCRC3 = partialCRC[2];
|
| 225 |
+
V128 partialCRC4 = partialCRC[3];
|
| 226 |
+
|
| 227 |
+
V128 tmp1 = V128_PMulHi(partialCRC1, loopMultiplicands);
|
| 228 |
+
V128 tmp2 = V128_PMulHi(partialCRC2, loopMultiplicands);
|
| 229 |
+
V128 tmp3 = V128_PMulHi(partialCRC3, loopMultiplicands);
|
| 230 |
+
V128 tmp4 = V128_PMulHi(partialCRC4, loopMultiplicands);
|
| 231 |
+
V128 data1 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 0));
|
| 232 |
+
V128 data2 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 1));
|
| 233 |
+
V128 data3 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 2));
|
| 234 |
+
V128 data4 = V128_LoadU(reinterpret_cast<const V128*>(p + 16 * 3));
|
| 235 |
+
partialCRC1 = V128_PMulLow(partialCRC1, loopMultiplicands);
|
| 236 |
+
partialCRC2 = V128_PMulLow(partialCRC2, loopMultiplicands);
|
| 237 |
+
partialCRC3 = V128_PMulLow(partialCRC3, loopMultiplicands);
|
| 238 |
+
partialCRC4 = V128_PMulLow(partialCRC4, loopMultiplicands);
|
| 239 |
+
partialCRC1 = V128_Xor(tmp1, partialCRC1);
|
| 240 |
+
partialCRC2 = V128_Xor(tmp2, partialCRC2);
|
| 241 |
+
partialCRC3 = V128_Xor(tmp3, partialCRC3);
|
| 242 |
+
partialCRC4 = V128_Xor(tmp4, partialCRC4);
|
| 243 |
+
partialCRC1 = V128_Xor(partialCRC1, data1);
|
| 244 |
+
partialCRC2 = V128_Xor(partialCRC2, data2);
|
| 245 |
+
partialCRC3 = V128_Xor(partialCRC3, data3);
|
| 246 |
+
partialCRC4 = V128_Xor(partialCRC4, data4);
|
| 247 |
+
partialCRC[0] = partialCRC1;
|
| 248 |
+
partialCRC[1] = partialCRC2;
|
| 249 |
+
partialCRC[2] = partialCRC3;
|
| 250 |
+
partialCRC[3] = partialCRC4;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
// Reduce partialCRC produced by Process64BytesPclmul into a single value,
|
| 254 |
+
// that represents crc checksum of all the processed bytes.
|
| 255 |
+
ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t
|
| 256 |
+
FinalizePclmulStream(V128* partialCRC) const {
|
| 257 |
+
V128 partialCRC1 = partialCRC[0];
|
| 258 |
+
V128 partialCRC2 = partialCRC[1];
|
| 259 |
+
V128 partialCRC3 = partialCRC[2];
|
| 260 |
+
V128 partialCRC4 = partialCRC[3];
|
| 261 |
+
|
| 262 |
+
// Combine 4 vectors of partial crc into a single vector.
|
| 263 |
+
V128 reductionMultiplicands =
|
| 264 |
+
V128_Load(reinterpret_cast<const V128*>(k5k6));
|
| 265 |
+
|
| 266 |
+
V128 low = V128_PMulLow(reductionMultiplicands, partialCRC1);
|
| 267 |
+
V128 high = V128_PMulHi(reductionMultiplicands, partialCRC1);
|
| 268 |
+
|
| 269 |
+
partialCRC1 = V128_Xor(low, high);
|
| 270 |
+
partialCRC1 = V128_Xor(partialCRC1, partialCRC2);
|
| 271 |
+
|
| 272 |
+
low = V128_PMulLow(reductionMultiplicands, partialCRC3);
|
| 273 |
+
high = V128_PMulHi(reductionMultiplicands, partialCRC3);
|
| 274 |
+
|
| 275 |
+
partialCRC3 = V128_Xor(low, high);
|
| 276 |
+
partialCRC3 = V128_Xor(partialCRC3, partialCRC4);
|
| 277 |
+
|
| 278 |
+
reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k3k4));
|
| 279 |
+
|
| 280 |
+
low = V128_PMulLow(reductionMultiplicands, partialCRC1);
|
| 281 |
+
high = V128_PMulHi(reductionMultiplicands, partialCRC1);
|
| 282 |
+
V128 fullCRC = V128_Xor(low, high);
|
| 283 |
+
fullCRC = V128_Xor(fullCRC, partialCRC3);
|
| 284 |
+
|
| 285 |
+
// Reduce fullCRC into scalar value.
|
| 286 |
+
reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k5k6));
|
| 287 |
+
|
| 288 |
+
V128 mask = V128_Load(reinterpret_cast<const V128*>(kMask));
|
| 289 |
+
|
| 290 |
+
V128 tmp = V128_PMul01(reductionMultiplicands, fullCRC);
|
| 291 |
+
fullCRC = V128_ShiftRight<8>(fullCRC);
|
| 292 |
+
fullCRC = V128_Xor(fullCRC, tmp);
|
| 293 |
+
|
| 294 |
+
reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(k7k0));
|
| 295 |
+
|
| 296 |
+
tmp = V128_ShiftRight<4>(fullCRC);
|
| 297 |
+
fullCRC = V128_And(fullCRC, mask);
|
| 298 |
+
fullCRC = V128_PMulLow(reductionMultiplicands, fullCRC);
|
| 299 |
+
fullCRC = V128_Xor(tmp, fullCRC);
|
| 300 |
+
|
| 301 |
+
reductionMultiplicands = V128_Load(reinterpret_cast<const V128*>(kPoly));
|
| 302 |
+
|
| 303 |
+
tmp = V128_And(fullCRC, mask);
|
| 304 |
+
tmp = V128_PMul01(reductionMultiplicands, tmp);
|
| 305 |
+
tmp = V128_And(tmp, mask);
|
| 306 |
+
tmp = V128_PMulLow(reductionMultiplicands, tmp);
|
| 307 |
+
|
| 308 |
+
fullCRC = V128_Xor(tmp, fullCRC);
|
| 309 |
+
|
| 310 |
+
return static_cast<uint64_t>(V128_Extract32<1>(fullCRC));
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
// Update crc with 64 bytes of data from p.
|
| 314 |
+
ABSL_ATTRIBUTE_ALWAYS_INLINE uint64_t Process64BytesCRC(const uint8_t* p,
|
| 315 |
+
uint64_t crc) const {
|
| 316 |
+
for (int i = 0; i < 8; i++) {
|
| 317 |
+
crc =
|
| 318 |
+
CRC32_u64(static_cast<uint32_t>(crc), absl::little_endian::Load64(p));
|
| 319 |
+
p += 8;
|
| 320 |
+
}
|
| 321 |
+
return crc;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
// Generated by crc32c_x86_test --crc32c_generate_constants=true
|
| 325 |
+
// and verified against constants in linux kernel for S390:
|
| 326 |
+
// https://github.com/torvalds/linux/blob/master/arch/s390/crypto/crc32le-vx.S
|
| 327 |
+
alignas(16) static constexpr uint64_t k1k2[2] = {0x0740eef02, 0x09e4addf8};
|
| 328 |
+
alignas(16) static constexpr uint64_t k3k4[2] = {0x1384aa63a, 0x0ba4fc28e};
|
| 329 |
+
alignas(16) static constexpr uint64_t k5k6[2] = {0x0f20c0dfe, 0x14cd00bd6};
|
| 330 |
+
alignas(16) static constexpr uint64_t k7k0[2] = {0x0dd45aab8, 0x000000000};
|
| 331 |
+
alignas(16) static constexpr uint64_t kPoly[2] = {0x105ec76f0, 0x0dea713f1};
|
| 332 |
+
alignas(16) static constexpr uint32_t kMask[4] = {~0u, 0u, ~0u, 0u};
|
| 333 |
+
|
| 334 |
+
// Medium runs of bytes are broken into groups of kGroupsSmall blocks of same
|
| 335 |
+
// size. Each group is CRCed in parallel then combined at the end of the
|
| 336 |
+
// block.
|
| 337 |
+
static constexpr size_t kGroupsSmall = 3;
|
| 338 |
+
// For large runs we use up to kMaxStreams blocks computed with CRC
|
| 339 |
+
// instruction, and up to kMaxStreams blocks computed with PCLMULQDQ, which
|
| 340 |
+
// are combined in the end.
|
| 341 |
+
static constexpr size_t kMaxStreams = 3;
|
| 342 |
+
};
|
| 343 |
+
|
| 344 |
+
#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
| 345 |
+
alignas(16) constexpr uint64_t
|
| 346 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k1k2[2];
|
| 347 |
+
alignas(16) constexpr uint64_t
|
| 348 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k3k4[2];
|
| 349 |
+
alignas(16) constexpr uint64_t
|
| 350 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k5k6[2];
|
| 351 |
+
alignas(16) constexpr uint64_t
|
| 352 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::k7k0[2];
|
| 353 |
+
alignas(16) constexpr uint64_t
|
| 354 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kPoly[2];
|
| 355 |
+
alignas(16) constexpr uint32_t
|
| 356 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMask[4];
|
| 357 |
+
constexpr size_t
|
| 358 |
+
CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kGroupsSmall;
|
| 359 |
+
constexpr size_t CRC32AcceleratedX86ARMCombinedMultipleStreamsBase::kMaxStreams;
|
| 360 |
+
#endif // ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
|
| 361 |
+
|
| 362 |
+
template <size_t num_crc_streams, size_t num_pclmul_streams,
|
| 363 |
+
CutoffStrategy strategy>
|
| 364 |
+
class CRC32AcceleratedX86ARMCombinedMultipleStreams
|
| 365 |
+
: public CRC32AcceleratedX86ARMCombinedMultipleStreamsBase {
|
| 366 |
+
ABSL_ATTRIBUTE_HOT
|
| 367 |
+
void Extend(uint32_t* crc, const void* bytes, size_t length) const override {
|
| 368 |
+
static_assert(num_crc_streams >= 1 && num_crc_streams <= kMaxStreams,
|
| 369 |
+
"Invalid number of crc streams");
|
| 370 |
+
static_assert(num_pclmul_streams >= 0 && num_pclmul_streams <= kMaxStreams,
|
| 371 |
+
"Invalid number of pclmul streams");
|
| 372 |
+
const uint8_t* p = static_cast<const uint8_t*>(bytes);
|
| 373 |
+
const uint8_t* e = p + length;
|
| 374 |
+
uint32_t l = *crc;
|
| 375 |
+
uint64_t l64;
|
| 376 |
+
|
| 377 |
+
// We have dedicated instruction for 1,2,4 and 8 bytes.
|
| 378 |
+
if (length & 8) {
|
| 379 |
+
ABSL_INTERNAL_STEP8(l, p);
|
| 380 |
+
length &= ~size_t{8};
|
| 381 |
+
}
|
| 382 |
+
if (length & 4) {
|
| 383 |
+
ABSL_INTERNAL_STEP4(l);
|
| 384 |
+
length &= ~size_t{4};
|
| 385 |
+
}
|
| 386 |
+
if (length & 2) {
|
| 387 |
+
ABSL_INTERNAL_STEP2(l);
|
| 388 |
+
length &= ~size_t{2};
|
| 389 |
+
}
|
| 390 |
+
if (length & 1) {
|
| 391 |
+
ABSL_INTERNAL_STEP1(l);
|
| 392 |
+
length &= ~size_t{1};
|
| 393 |
+
}
|
| 394 |
+
if (length == 0) {
|
| 395 |
+
*crc = l;
|
| 396 |
+
return;
|
| 397 |
+
}
|
| 398 |
+
// length is now multiple of 16.
|
| 399 |
+
|
| 400 |
+
// For small blocks just run simple loop, because cost of combining multiple
|
| 401 |
+
// streams is significant.
|
| 402 |
+
if (strategy != CutoffStrategy::Unroll64CRC) {
|
| 403 |
+
if (length < kSmallCutoff) {
|
| 404 |
+
while (length >= 16) {
|
| 405 |
+
ABSL_INTERNAL_STEP8(l, p);
|
| 406 |
+
ABSL_INTERNAL_STEP8(l, p);
|
| 407 |
+
length -= 16;
|
| 408 |
+
}
|
| 409 |
+
*crc = l;
|
| 410 |
+
return;
|
| 411 |
+
}
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
// For medium blocks we run 3 crc streams and combine them as described in
|
| 415 |
+
// Intel paper above. Running 4th stream doesn't help, because crc
|
| 416 |
+
// instruction has latency 3 and throughput 1.
|
| 417 |
+
if (length < kMediumCutoff) {
|
| 418 |
+
l64 = l;
|
| 419 |
+
if (strategy == CutoffStrategy::Fold3) {
|
| 420 |
+
uint64_t l641 = 0;
|
| 421 |
+
uint64_t l642 = 0;
|
| 422 |
+
const size_t blockSize = 32;
|
| 423 |
+
size_t bs = static_cast<size_t>(e - p) / kGroupsSmall / blockSize;
|
| 424 |
+
const uint8_t* p1 = p + bs * blockSize;
|
| 425 |
+
const uint8_t* p2 = p1 + bs * blockSize;
|
| 426 |
+
|
| 427 |
+
for (size_t i = 0; i + 1 < bs; ++i) {
|
| 428 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 429 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 430 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 431 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 432 |
+
PrefetchToLocalCache(
|
| 433 |
+
reinterpret_cast<const char*>(p + kPrefetchHorizonMedium));
|
| 434 |
+
PrefetchToLocalCache(
|
| 435 |
+
reinterpret_cast<const char*>(p1 + kPrefetchHorizonMedium));
|
| 436 |
+
PrefetchToLocalCache(
|
| 437 |
+
reinterpret_cast<const char*>(p2 + kPrefetchHorizonMedium));
|
| 438 |
+
}
|
| 439 |
+
// Don't run crc on last 8 bytes.
|
| 440 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 441 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 442 |
+
ABSL_INTERNAL_STEP8BY3(l64, l641, l642, p, p1, p2);
|
| 443 |
+
ABSL_INTERNAL_STEP8BY2(l64, l641, p, p1);
|
| 444 |
+
|
| 445 |
+
V128 magic = *(reinterpret_cast<const V128*>(kClmulConstants) + bs - 1);
|
| 446 |
+
|
| 447 |
+
V128 tmp = V128_From2x64(0, l64);
|
| 448 |
+
|
| 449 |
+
V128 res1 = V128_PMulLow(tmp, magic);
|
| 450 |
+
|
| 451 |
+
tmp = V128_From2x64(0, l641);
|
| 452 |
+
|
| 453 |
+
V128 res2 = V128_PMul10(tmp, magic);
|
| 454 |
+
V128 x = V128_Xor(res1, res2);
|
| 455 |
+
l64 = static_cast<uint64_t>(V128_Low64(x)) ^
|
| 456 |
+
absl::little_endian::Load64(p2);
|
| 457 |
+
l64 = CRC32_u64(static_cast<uint32_t>(l642), l64);
|
| 458 |
+
|
| 459 |
+
p = p2 + 8;
|
| 460 |
+
} else if (strategy == CutoffStrategy::Unroll64CRC) {
|
| 461 |
+
while ((e - p) >= 64) {
|
| 462 |
+
l64 = Process64BytesCRC(p, l64);
|
| 463 |
+
p += 64;
|
| 464 |
+
}
|
| 465 |
+
}
|
| 466 |
+
} else {
|
| 467 |
+
// There is a lot of data, we can ignore combine costs and run all
|
| 468 |
+
// requested streams (num_crc_streams + num_pclmul_streams),
|
| 469 |
+
// using prefetch. CRC and PCLMULQDQ use different cpu execution units,
|
| 470 |
+
// so on some cpus it makes sense to execute both of them for different
|
| 471 |
+
// streams.
|
| 472 |
+
|
| 473 |
+
// Point x at first 8-byte aligned byte in string.
|
| 474 |
+
const uint8_t* x = RoundUp<8>(p);
|
| 475 |
+
// Process bytes until p is 8-byte aligned, if that isn't past the end.
|
| 476 |
+
while (p != x) {
|
| 477 |
+
ABSL_INTERNAL_STEP1(l);
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
size_t bs = static_cast<size_t>(e - p) /
|
| 481 |
+
(num_crc_streams + num_pclmul_streams) / 64;
|
| 482 |
+
const uint8_t* crc_streams[kMaxStreams];
|
| 483 |
+
const uint8_t* pclmul_streams[kMaxStreams];
|
| 484 |
+
// We are guaranteed to have at least one crc stream.
|
| 485 |
+
crc_streams[0] = p;
|
| 486 |
+
for (size_t i = 1; i < num_crc_streams; i++) {
|
| 487 |
+
crc_streams[i] = crc_streams[i - 1] + bs * 64;
|
| 488 |
+
}
|
| 489 |
+
pclmul_streams[0] = crc_streams[num_crc_streams - 1] + bs * 64;
|
| 490 |
+
for (size_t i = 1; i < num_pclmul_streams; i++) {
|
| 491 |
+
pclmul_streams[i] = pclmul_streams[i - 1] + bs * 64;
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
+
// Per stream crc sums.
|
| 495 |
+
uint64_t l64_crc[kMaxStreams] = {l};
|
| 496 |
+
uint64_t l64_pclmul[kMaxStreams] = {0};
|
| 497 |
+
|
| 498 |
+
// Peel first iteration, because PCLMULQDQ stream, needs setup.
|
| 499 |
+
for (size_t i = 0; i < num_crc_streams; i++) {
|
| 500 |
+
l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]);
|
| 501 |
+
crc_streams[i] += 16 * 4;
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
V128 partialCRC[kMaxStreams][4];
|
| 505 |
+
for (size_t i = 0; i < num_pclmul_streams; i++) {
|
| 506 |
+
partialCRC[i][0] = V128_LoadU(
|
| 507 |
+
reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 0));
|
| 508 |
+
partialCRC[i][1] = V128_LoadU(
|
| 509 |
+
reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 1));
|
| 510 |
+
partialCRC[i][2] = V128_LoadU(
|
| 511 |
+
reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 2));
|
| 512 |
+
partialCRC[i][3] = V128_LoadU(
|
| 513 |
+
reinterpret_cast<const V128*>(pclmul_streams[i] + 16 * 3));
|
| 514 |
+
pclmul_streams[i] += 16 * 4;
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
for (size_t i = 1; i < bs; i++) {
|
| 518 |
+
// Prefetch data for next iterations.
|
| 519 |
+
for (size_t j = 0; j < num_crc_streams; j++) {
|
| 520 |
+
PrefetchToLocalCache(
|
| 521 |
+
reinterpret_cast<const char*>(crc_streams[j] + kPrefetchHorizon));
|
| 522 |
+
}
|
| 523 |
+
for (size_t j = 0; j < num_pclmul_streams; j++) {
|
| 524 |
+
PrefetchToLocalCache(reinterpret_cast<const char*>(pclmul_streams[j] +
|
| 525 |
+
kPrefetchHorizon));
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
// We process each stream in 64 byte blocks. This can be written as
|
| 529 |
+
// for (int i = 0; i < num_pclmul_streams; i++) {
|
| 530 |
+
// Process64BytesPclmul(pclmul_streams[i], partialCRC[i]);
|
| 531 |
+
// pclmul_streams[i] += 16 * 4;
|
| 532 |
+
// }
|
| 533 |
+
// for (int i = 0; i < num_crc_streams; i++) {
|
| 534 |
+
// l64_crc[i] = Process64BytesCRC(crc_streams[i], l64_crc[i]);
|
| 535 |
+
// crc_streams[i] += 16*4;
|
| 536 |
+
// }
|
| 537 |
+
// But unrolling and interleaving PCLMULQDQ and CRC blocks manually
|
| 538 |
+
// gives ~2% performance boost.
|
| 539 |
+
l64_crc[0] = Process64BytesCRC(crc_streams[0], l64_crc[0]);
|
| 540 |
+
crc_streams[0] += 16 * 4;
|
| 541 |
+
if (num_pclmul_streams > 0) {
|
| 542 |
+
Process64BytesPclmul(pclmul_streams[0], partialCRC[0]);
|
| 543 |
+
pclmul_streams[0] += 16 * 4;
|
| 544 |
+
}
|
| 545 |
+
if (num_crc_streams > 1) {
|
| 546 |
+
l64_crc[1] = Process64BytesCRC(crc_streams[1], l64_crc[1]);
|
| 547 |
+
crc_streams[1] += 16 * 4;
|
| 548 |
+
}
|
| 549 |
+
if (num_pclmul_streams > 1) {
|
| 550 |
+
Process64BytesPclmul(pclmul_streams[1], partialCRC[1]);
|
| 551 |
+
pclmul_streams[1] += 16 * 4;
|
| 552 |
+
}
|
| 553 |
+
if (num_crc_streams > 2) {
|
| 554 |
+
l64_crc[2] = Process64BytesCRC(crc_streams[2], l64_crc[2]);
|
| 555 |
+
crc_streams[2] += 16 * 4;
|
| 556 |
+
}
|
| 557 |
+
if (num_pclmul_streams > 2) {
|
| 558 |
+
Process64BytesPclmul(pclmul_streams[2], partialCRC[2]);
|
| 559 |
+
pclmul_streams[2] += 16 * 4;
|
| 560 |
+
}
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
// PCLMULQDQ based streams require special final step;
|
| 564 |
+
// CRC based don't.
|
| 565 |
+
for (size_t i = 0; i < num_pclmul_streams; i++) {
|
| 566 |
+
l64_pclmul[i] = FinalizePclmulStream(partialCRC[i]);
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
// Combine all streams into single result.
|
| 570 |
+
uint32_t magic = ComputeZeroConstant(bs * 64);
|
| 571 |
+
l64 = l64_crc[0];
|
| 572 |
+
for (size_t i = 1; i < num_crc_streams; i++) {
|
| 573 |
+
l64 = multiply(static_cast<uint32_t>(l64), magic);
|
| 574 |
+
l64 ^= l64_crc[i];
|
| 575 |
+
}
|
| 576 |
+
for (size_t i = 0; i < num_pclmul_streams; i++) {
|
| 577 |
+
l64 = multiply(static_cast<uint32_t>(l64), magic);
|
| 578 |
+
l64 ^= l64_pclmul[i];
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
// Update p.
|
| 582 |
+
if (num_pclmul_streams > 0) {
|
| 583 |
+
p = pclmul_streams[num_pclmul_streams - 1];
|
| 584 |
+
} else {
|
| 585 |
+
p = crc_streams[num_crc_streams - 1];
|
| 586 |
+
}
|
| 587 |
+
}
|
| 588 |
+
l = static_cast<uint32_t>(l64);
|
| 589 |
+
|
| 590 |
+
while ((e - p) >= 16) {
|
| 591 |
+
ABSL_INTERNAL_STEP8(l, p);
|
| 592 |
+
ABSL_INTERNAL_STEP8(l, p);
|
| 593 |
+
}
|
| 594 |
+
// Process the last few bytes
|
| 595 |
+
while (p != e) {
|
| 596 |
+
ABSL_INTERNAL_STEP1(l);
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
#undef ABSL_INTERNAL_STEP8BY3
|
| 600 |
+
#undef ABSL_INTERNAL_STEP8BY2
|
| 601 |
+
#undef ABSL_INTERNAL_STEP8
|
| 602 |
+
#undef ABSL_INTERNAL_STEP4
|
| 603 |
+
#undef ABSL_INTERNAL_STEP2
|
| 604 |
+
#undef ABSL_INTERNAL_STEP1
|
| 605 |
+
|
| 606 |
+
*crc = l;
|
| 607 |
+
}
|
| 608 |
+
};
|
| 609 |
+
|
| 610 |
+
} // namespace
|
| 611 |
+
|
| 612 |
+
// Intel processors with SSE4.2 have an instruction for one particular
|
| 613 |
+
// 32-bit CRC polynomial: crc32c
|
| 614 |
+
CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() {
|
| 615 |
+
CpuType type = GetCpuType();
|
| 616 |
+
switch (type) {
|
| 617 |
+
case CpuType::kIntelHaswell:
|
| 618 |
+
case CpuType::kAmdRome:
|
| 619 |
+
case CpuType::kAmdNaples:
|
| 620 |
+
case CpuType::kAmdMilan:
|
| 621 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 622 |
+
3, 1, CutoffStrategy::Fold3>();
|
| 623 |
+
// PCLMULQDQ is fast, use combined PCLMULQDQ + CRC implementation.
|
| 624 |
+
case CpuType::kIntelCascadelakeXeon:
|
| 625 |
+
case CpuType::kIntelSkylakeXeon:
|
| 626 |
+
case CpuType::kIntelBroadwell:
|
| 627 |
+
case CpuType::kIntelSkylake:
|
| 628 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 629 |
+
3, 2, CutoffStrategy::Fold3>();
|
| 630 |
+
// PCLMULQDQ is slow, don't use it.
|
| 631 |
+
case CpuType::kIntelIvybridge:
|
| 632 |
+
case CpuType::kIntelSandybridge:
|
| 633 |
+
case CpuType::kIntelWestmere:
|
| 634 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 635 |
+
3, 0, CutoffStrategy::Fold3>();
|
| 636 |
+
case CpuType::kArmNeoverseN1:
|
| 637 |
+
case CpuType::kArmNeoverseN2:
|
| 638 |
+
case CpuType::kArmNeoverseV1:
|
| 639 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 640 |
+
1, 1, CutoffStrategy::Unroll64CRC>();
|
| 641 |
+
case CpuType::kAmpereSiryn:
|
| 642 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 643 |
+
3, 2, CutoffStrategy::Fold3>();
|
| 644 |
+
case CpuType::kArmNeoverseV2:
|
| 645 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 646 |
+
1, 2, CutoffStrategy::Unroll64CRC>();
|
| 647 |
+
#if defined(__aarch64__)
|
| 648 |
+
default:
|
| 649 |
+
// Not all ARM processors support the needed instructions, so check here
|
| 650 |
+
// before trying to use an accelerated implementation.
|
| 651 |
+
if (SupportsArmCRC32PMULL()) {
|
| 652 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 653 |
+
1, 1, CutoffStrategy::Unroll64CRC>();
|
| 654 |
+
} else {
|
| 655 |
+
return nullptr;
|
| 656 |
+
}
|
| 657 |
+
#else
|
| 658 |
+
default:
|
| 659 |
+
// Something else, play it safe and assume slow PCLMULQDQ.
|
| 660 |
+
return new CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 661 |
+
3, 0, CutoffStrategy::Fold3>();
|
| 662 |
+
#endif
|
| 663 |
+
}
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() {
|
| 667 |
+
auto ret = std::vector<std::unique_ptr<CRCImpl>>();
|
| 668 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 669 |
+
1, 0, CutoffStrategy::Fold3>>());
|
| 670 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 671 |
+
1, 1, CutoffStrategy::Fold3>>());
|
| 672 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 673 |
+
1, 2, CutoffStrategy::Fold3>>());
|
| 674 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 675 |
+
1, 3, CutoffStrategy::Fold3>>());
|
| 676 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 677 |
+
2, 0, CutoffStrategy::Fold3>>());
|
| 678 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 679 |
+
2, 1, CutoffStrategy::Fold3>>());
|
| 680 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 681 |
+
2, 2, CutoffStrategy::Fold3>>());
|
| 682 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 683 |
+
2, 3, CutoffStrategy::Fold3>>());
|
| 684 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 685 |
+
3, 0, CutoffStrategy::Fold3>>());
|
| 686 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 687 |
+
3, 1, CutoffStrategy::Fold3>>());
|
| 688 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 689 |
+
3, 2, CutoffStrategy::Fold3>>());
|
| 690 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 691 |
+
3, 3, CutoffStrategy::Fold3>>());
|
| 692 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 693 |
+
1, 0, CutoffStrategy::Unroll64CRC>>());
|
| 694 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 695 |
+
1, 1, CutoffStrategy::Unroll64CRC>>());
|
| 696 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 697 |
+
1, 2, CutoffStrategy::Unroll64CRC>>());
|
| 698 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 699 |
+
1, 3, CutoffStrategy::Unroll64CRC>>());
|
| 700 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 701 |
+
2, 0, CutoffStrategy::Unroll64CRC>>());
|
| 702 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 703 |
+
2, 1, CutoffStrategy::Unroll64CRC>>());
|
| 704 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 705 |
+
2, 2, CutoffStrategy::Unroll64CRC>>());
|
| 706 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 707 |
+
2, 3, CutoffStrategy::Unroll64CRC>>());
|
| 708 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 709 |
+
3, 0, CutoffStrategy::Unroll64CRC>>());
|
| 710 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 711 |
+
3, 1, CutoffStrategy::Unroll64CRC>>());
|
| 712 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 713 |
+
3, 2, CutoffStrategy::Unroll64CRC>>());
|
| 714 |
+
ret.push_back(absl::make_unique<CRC32AcceleratedX86ARMCombinedMultipleStreams<
|
| 715 |
+
3, 3, CutoffStrategy::Unroll64CRC>>());
|
| 716 |
+
|
| 717 |
+
return ret;
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
#else // !ABSL_INTERNAL_CAN_USE_SIMD_CRC32C
|
| 721 |
+
|
| 722 |
+
std::vector<std::unique_ptr<CRCImpl>> NewCRC32AcceleratedX86ARMCombinedAll() {
|
| 723 |
+
return std::vector<std::unique_ptr<CRCImpl>>();
|
| 724 |
+
}
|
| 725 |
+
|
| 726 |
+
// no hardware acceleration available
|
| 727 |
+
CRCImpl* TryNewCRC32AcceleratedX86ARMCombined() { return nullptr; }
|
| 728 |
+
|
| 729 |
+
#endif
|
| 730 |
+
|
| 731 |
+
} // namespace crc_internal
|
| 732 |
+
ABSL_NAMESPACE_END
|
| 733 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_arm_intrinsics.h
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
|
| 16 |
+
#define ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
|
| 17 |
+
|
| 18 |
+
#include "absl/base/config.h"
|
| 19 |
+
|
| 20 |
+
#ifdef __aarch64__
|
| 21 |
+
#include <arm_neon.h>
|
| 22 |
+
|
| 23 |
+
typedef int64x2_t __m128i; /* 128-bit vector containing integers */
|
| 24 |
+
#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x)
|
| 25 |
+
#define vreinterpretq_s64_m128i(x) (x)
|
| 26 |
+
|
| 27 |
+
// Guarantees that every preceding store is globally visible before any
|
| 28 |
+
// subsequent store.
|
| 29 |
+
// https://msdn.microsoft.com/en-us/library/5h2w73d1%28v=vs.90%29.aspx
|
| 30 |
+
static inline __attribute__((always_inline)) void _mm_sfence(void) {
|
| 31 |
+
__sync_synchronize();
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// Load 128-bits of integer data from unaligned memory into dst. This intrinsic
|
| 35 |
+
// may perform better than _mm_loadu_si128 when the data crosses a cache line
|
| 36 |
+
// boundary.
|
| 37 |
+
//
|
| 38 |
+
// dst[127:0] := MEM[mem_addr+127:mem_addr]
|
| 39 |
+
//
|
| 40 |
+
// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_lddqu_si128
|
| 41 |
+
#define _mm_lddqu_si128 _mm_loadu_si128
|
| 42 |
+
|
| 43 |
+
// Loads 128-bit value. :
|
| 44 |
+
// https://msdn.microsoft.com/zh-cn/library/f4k12ae8(v=vs.90).aspx
|
| 45 |
+
static inline __attribute__((always_inline)) __m128i _mm_loadu_si128(
|
| 46 |
+
const __m128i *p) {
|
| 47 |
+
return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *)p));
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
// Stores the data in a to the address p without polluting the caches. If the
|
| 51 |
+
// cache line containing address p is already in the cache, the cache will be
|
| 52 |
+
// updated.
|
| 53 |
+
// https://msdn.microsoft.com/en-us/library/ba08y07y%28v=vs.90%29.aspx
|
| 54 |
+
static inline __attribute__((always_inline)) void _mm_stream_si128(__m128i *p,
|
| 55 |
+
__m128i a) {
|
| 56 |
+
#if ABSL_HAVE_BUILTIN(__builtin_nontemporal_store)
|
| 57 |
+
__builtin_nontemporal_store(a, p);
|
| 58 |
+
#else
|
| 59 |
+
vst1q_s64((int64_t *)p, vreinterpretq_s64_m128i(a));
|
| 60 |
+
#endif
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
// Sets the 16 signed 8-bit integer values.
|
| 64 |
+
// https://msdn.microsoft.com/en-us/library/x0cx8zd3(v=vs.90).aspx
|
| 65 |
+
static inline __attribute__((always_inline)) __m128i _mm_set_epi8(
|
| 66 |
+
signed char b15, signed char b14, signed char b13, signed char b12,
|
| 67 |
+
signed char b11, signed char b10, signed char b9, signed char b8,
|
| 68 |
+
signed char b7, signed char b6, signed char b5, signed char b4,
|
| 69 |
+
signed char b3, signed char b2, signed char b1, signed char b0) {
|
| 70 |
+
int8_t __attribute__((aligned(16)))
|
| 71 |
+
data[16] = {(int8_t)b0, (int8_t)b1, (int8_t)b2, (int8_t)b3,
|
| 72 |
+
(int8_t)b4, (int8_t)b5, (int8_t)b6, (int8_t)b7,
|
| 73 |
+
(int8_t)b8, (int8_t)b9, (int8_t)b10, (int8_t)b11,
|
| 74 |
+
(int8_t)b12, (int8_t)b13, (int8_t)b14, (int8_t)b15};
|
| 75 |
+
return (__m128i)vld1q_s8(data);
|
| 76 |
+
}
|
| 77 |
+
#endif // __aarch64__
|
| 78 |
+
|
| 79 |
+
#endif // ABSL_CRC_INTERNAL_NON_TEMPORAL_ARM_INTRINSICS_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_memcpy.h
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
|
| 16 |
+
#define ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
|
| 17 |
+
|
| 18 |
+
#ifdef _MSC_VER
|
| 19 |
+
#include <intrin.h>
|
| 20 |
+
#endif
|
| 21 |
+
|
| 22 |
+
#ifdef __SSE__
|
| 23 |
+
#include <xmmintrin.h>
|
| 24 |
+
#endif
|
| 25 |
+
|
| 26 |
+
#ifdef __SSE2__
|
| 27 |
+
#include <emmintrin.h>
|
| 28 |
+
#endif
|
| 29 |
+
|
| 30 |
+
#ifdef __SSE3__
|
| 31 |
+
#include <pmmintrin.h>
|
| 32 |
+
#endif
|
| 33 |
+
|
| 34 |
+
#ifdef __AVX__
|
| 35 |
+
#include <immintrin.h>
|
| 36 |
+
#endif
|
| 37 |
+
|
| 38 |
+
#ifdef __aarch64__
|
| 39 |
+
#include "absl/crc/internal/non_temporal_arm_intrinsics.h"
|
| 40 |
+
#endif
|
| 41 |
+
|
| 42 |
+
#include <algorithm>
|
| 43 |
+
#include <cassert>
|
| 44 |
+
#include <cstdint>
|
| 45 |
+
#include <cstring>
|
| 46 |
+
|
| 47 |
+
#include "absl/base/config.h"
|
| 48 |
+
#include "absl/base/optimization.h"
|
| 49 |
+
|
| 50 |
+
namespace absl {
|
| 51 |
+
ABSL_NAMESPACE_BEGIN
|
| 52 |
+
namespace crc_internal {
|
| 53 |
+
|
| 54 |
+
// This non-temporal memcpy does regular load and non-temporal store memory
|
| 55 |
+
// copy. It is compatible to both 16-byte aligned and unaligned addresses. If
|
| 56 |
+
// data at the destination is not immediately accessed, using non-temporal
|
| 57 |
+
// memcpy can save 1 DRAM load of the destination cacheline.
|
| 58 |
+
constexpr size_t kCacheLineSize = ABSL_CACHELINE_SIZE;
|
| 59 |
+
|
| 60 |
+
// If the objects overlap, the behavior is undefined.
|
| 61 |
+
inline void *non_temporal_store_memcpy(void *__restrict dst,
|
| 62 |
+
const void *__restrict src, size_t len) {
|
| 63 |
+
#if defined(__SSE3__) || defined(__aarch64__) || \
|
| 64 |
+
(defined(_MSC_VER) && defined(__AVX__))
|
| 65 |
+
// This implementation requires SSE3.
|
| 66 |
+
// MSVC cannot target SSE3 directly, but when MSVC targets AVX,
|
| 67 |
+
// SSE3 support is implied.
|
| 68 |
+
uint8_t *d = reinterpret_cast<uint8_t *>(dst);
|
| 69 |
+
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
|
| 70 |
+
|
| 71 |
+
// memcpy() the misaligned header. At the end of this if block, <d> is
|
| 72 |
+
// aligned to a 64-byte cacheline boundary or <len> == 0.
|
| 73 |
+
if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) {
|
| 74 |
+
uintptr_t bytes_before_alignment_boundary =
|
| 75 |
+
kCacheLineSize -
|
| 76 |
+
(reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1));
|
| 77 |
+
size_t header_len = (std::min)(bytes_before_alignment_boundary, len);
|
| 78 |
+
assert(bytes_before_alignment_boundary < kCacheLineSize);
|
| 79 |
+
memcpy(d, s, header_len);
|
| 80 |
+
d += header_len;
|
| 81 |
+
s += header_len;
|
| 82 |
+
len -= header_len;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
if (len >= kCacheLineSize) {
|
| 86 |
+
_mm_sfence();
|
| 87 |
+
__m128i *dst_cacheline = reinterpret_cast<__m128i *>(d);
|
| 88 |
+
const __m128i *src_cacheline = reinterpret_cast<const __m128i *>(s);
|
| 89 |
+
constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m128i);
|
| 90 |
+
size_t loops = len / kCacheLineSize;
|
| 91 |
+
|
| 92 |
+
while (len >= kCacheLineSize) {
|
| 93 |
+
__m128i temp1, temp2, temp3, temp4;
|
| 94 |
+
temp1 = _mm_lddqu_si128(src_cacheline + 0);
|
| 95 |
+
temp2 = _mm_lddqu_si128(src_cacheline + 1);
|
| 96 |
+
temp3 = _mm_lddqu_si128(src_cacheline + 2);
|
| 97 |
+
temp4 = _mm_lddqu_si128(src_cacheline + 3);
|
| 98 |
+
_mm_stream_si128(dst_cacheline + 0, temp1);
|
| 99 |
+
_mm_stream_si128(dst_cacheline + 1, temp2);
|
| 100 |
+
_mm_stream_si128(dst_cacheline + 2, temp3);
|
| 101 |
+
_mm_stream_si128(dst_cacheline + 3, temp4);
|
| 102 |
+
src_cacheline += kOpsPerCacheLine;
|
| 103 |
+
dst_cacheline += kOpsPerCacheLine;
|
| 104 |
+
len -= kCacheLineSize;
|
| 105 |
+
}
|
| 106 |
+
d += loops * kCacheLineSize;
|
| 107 |
+
s += loops * kCacheLineSize;
|
| 108 |
+
_mm_sfence();
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// memcpy the tail.
|
| 112 |
+
if (len) {
|
| 113 |
+
memcpy(d, s, len);
|
| 114 |
+
}
|
| 115 |
+
return dst;
|
| 116 |
+
#else
|
| 117 |
+
// Fallback to regular memcpy.
|
| 118 |
+
return memcpy(dst, src, len);
|
| 119 |
+
#endif // __SSE3__ || __aarch64__ || (_MSC_VER && __AVX__)
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
inline void *non_temporal_store_memcpy_avx(void *__restrict dst,
|
| 123 |
+
const void *__restrict src,
|
| 124 |
+
size_t len) {
|
| 125 |
+
#ifdef __AVX__
|
| 126 |
+
uint8_t *d = reinterpret_cast<uint8_t *>(dst);
|
| 127 |
+
const uint8_t *s = reinterpret_cast<const uint8_t *>(src);
|
| 128 |
+
|
| 129 |
+
// memcpy() the misaligned header. At the end of this if block, <d> is
|
| 130 |
+
// aligned to a 64-byte cacheline boundary or <len> == 0.
|
| 131 |
+
if (reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1)) {
|
| 132 |
+
uintptr_t bytes_before_alignment_boundary =
|
| 133 |
+
kCacheLineSize -
|
| 134 |
+
(reinterpret_cast<uintptr_t>(d) & (kCacheLineSize - 1));
|
| 135 |
+
size_t header_len = (std::min)(bytes_before_alignment_boundary, len);
|
| 136 |
+
assert(bytes_before_alignment_boundary < kCacheLineSize);
|
| 137 |
+
memcpy(d, s, header_len);
|
| 138 |
+
d += header_len;
|
| 139 |
+
s += header_len;
|
| 140 |
+
len -= header_len;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
if (len >= kCacheLineSize) {
|
| 144 |
+
_mm_sfence();
|
| 145 |
+
__m256i *dst_cacheline = reinterpret_cast<__m256i *>(d);
|
| 146 |
+
const __m256i *src_cacheline = reinterpret_cast<const __m256i *>(s);
|
| 147 |
+
constexpr int kOpsPerCacheLine = kCacheLineSize / sizeof(__m256i);
|
| 148 |
+
size_t loops = len / kCacheLineSize;
|
| 149 |
+
|
| 150 |
+
while (len >= kCacheLineSize) {
|
| 151 |
+
__m256i temp1, temp2;
|
| 152 |
+
temp1 = _mm256_lddqu_si256(src_cacheline + 0);
|
| 153 |
+
temp2 = _mm256_lddqu_si256(src_cacheline + 1);
|
| 154 |
+
_mm256_stream_si256(dst_cacheline + 0, temp1);
|
| 155 |
+
_mm256_stream_si256(dst_cacheline + 1, temp2);
|
| 156 |
+
src_cacheline += kOpsPerCacheLine;
|
| 157 |
+
dst_cacheline += kOpsPerCacheLine;
|
| 158 |
+
len -= kCacheLineSize;
|
| 159 |
+
}
|
| 160 |
+
d += loops * kCacheLineSize;
|
| 161 |
+
s += loops * kCacheLineSize;
|
| 162 |
+
_mm_sfence();
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
// memcpy the tail.
|
| 166 |
+
if (len) {
|
| 167 |
+
memcpy(d, s, len);
|
| 168 |
+
}
|
| 169 |
+
return dst;
|
| 170 |
+
#else
|
| 171 |
+
// Fallback to regular memcpy when AVX is not available.
|
| 172 |
+
return memcpy(dst, src, len);
|
| 173 |
+
#endif // __AVX__
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
} // namespace crc_internal
|
| 177 |
+
ABSL_NAMESPACE_END
|
| 178 |
+
} // namespace absl
|
| 179 |
+
|
| 180 |
+
#endif // ABSL_CRC_INTERNAL_NON_TEMPORAL_MEMCPY_H_
|
weight/_dep/abseil-cpp/absl/crc/internal/non_temporal_memcpy_test.cc
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2022 The Abseil Authors
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/crc/internal/non_temporal_memcpy.h"
|
| 16 |
+
|
| 17 |
+
#include <algorithm>
|
| 18 |
+
#include <cstdint>
|
| 19 |
+
#include <iostream>
|
| 20 |
+
#include <vector>
|
| 21 |
+
|
| 22 |
+
#include "gtest/gtest.h"
|
| 23 |
+
|
| 24 |
+
namespace {
|
| 25 |
+
|
| 26 |
+
struct TestParam {
|
| 27 |
+
size_t copy_size;
|
| 28 |
+
uint32_t src_offset;
|
| 29 |
+
uint32_t dst_offset;
|
| 30 |
+
};
|
| 31 |
+
|
| 32 |
+
class NonTemporalMemcpyTest : public testing::TestWithParam<TestParam> {
|
| 33 |
+
protected:
|
| 34 |
+
void SetUp() override {
|
| 35 |
+
// Make buf_size multiple of 16 bytes.
|
| 36 |
+
size_t buf_size = ((std::max(GetParam().src_offset, GetParam().dst_offset) +
|
| 37 |
+
GetParam().copy_size) +
|
| 38 |
+
15) /
|
| 39 |
+
16 * 16;
|
| 40 |
+
a_.resize(buf_size);
|
| 41 |
+
b_.resize(buf_size);
|
| 42 |
+
for (size_t i = 0; i < buf_size; i++) {
|
| 43 |
+
a_[i] = static_cast<uint8_t>(i % 256);
|
| 44 |
+
b_[i] = ~a_[i];
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
std::vector<uint8_t> a_, b_;
|
| 49 |
+
};
|
| 50 |
+
|
| 51 |
+
TEST_P(NonTemporalMemcpyTest, SSEEquality) {
|
| 52 |
+
uint8_t *src = a_.data() + GetParam().src_offset;
|
| 53 |
+
uint8_t *dst = b_.data() + GetParam().dst_offset;
|
| 54 |
+
absl::crc_internal::non_temporal_store_memcpy(dst, src, GetParam().copy_size);
|
| 55 |
+
for (size_t i = 0; i < GetParam().copy_size; i++) {
|
| 56 |
+
EXPECT_EQ(src[i], dst[i]);
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
TEST_P(NonTemporalMemcpyTest, AVXEquality) {
|
| 61 |
+
uint8_t* src = a_.data() + GetParam().src_offset;
|
| 62 |
+
uint8_t* dst = b_.data() + GetParam().dst_offset;
|
| 63 |
+
|
| 64 |
+
absl::crc_internal::non_temporal_store_memcpy_avx(dst, src,
|
| 65 |
+
GetParam().copy_size);
|
| 66 |
+
for (size_t i = 0; i < GetParam().copy_size; i++) {
|
| 67 |
+
EXPECT_EQ(src[i], dst[i]);
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
// 63B is smaller than one cacheline operation thus the non-temporal routine
|
| 72 |
+
// will not be called.
|
| 73 |
+
// 4352B is sufficient for testing 4092B data copy with room for offsets.
|
| 74 |
+
constexpr TestParam params[] = {
|
| 75 |
+
{63, 0, 0}, {58, 5, 5}, {61, 2, 0}, {61, 0, 2},
|
| 76 |
+
{58, 5, 2}, {4096, 0, 0}, {4096, 0, 1}, {4096, 0, 2},
|
| 77 |
+
{4096, 0, 3}, {4096, 0, 4}, {4096, 0, 5}, {4096, 0, 6},
|
| 78 |
+
{4096, 0, 7}, {4096, 0, 8}, {4096, 0, 9}, {4096, 0, 10},
|
| 79 |
+
{4096, 0, 11}, {4096, 0, 12}, {4096, 0, 13}, {4096, 0, 14},
|
| 80 |
+
{4096, 0, 15}, {4096, 7, 7}, {4096, 3, 0}, {4096, 1, 0},
|
| 81 |
+
{4096, 9, 3}, {4096, 9, 11}, {8192, 0, 0}, {8192, 5, 2},
|
| 82 |
+
{1024768, 7, 11}, {1, 0, 0}, {1, 0, 1}, {1, 1, 0},
|
| 83 |
+
{1, 1, 1}};
|
| 84 |
+
|
| 85 |
+
INSTANTIATE_TEST_SUITE_P(ParameterizedNonTemporalMemcpyTest,
|
| 86 |
+
NonTemporalMemcpyTest, testing::ValuesIn(params));
|
| 87 |
+
|
| 88 |
+
} // namespace
|
weight/_dep/abseil-cpp/absl/debugging/BUILD.bazel
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Copyright 2017 The Abseil Authors.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
#
|
| 16 |
+
|
| 17 |
+
load(
|
| 18 |
+
"//absl:copts/configure_copts.bzl",
|
| 19 |
+
"ABSL_DEFAULT_COPTS",
|
| 20 |
+
"ABSL_DEFAULT_LINKOPTS",
|
| 21 |
+
"ABSL_TEST_COPTS",
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
package(
|
| 25 |
+
default_visibility = ["//visibility:public"],
|
| 26 |
+
features = [
|
| 27 |
+
"header_modules",
|
| 28 |
+
"layering_check",
|
| 29 |
+
"parse_headers",
|
| 30 |
+
],
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
licenses(["notice"])
|
| 34 |
+
|
| 35 |
+
cc_library(
|
| 36 |
+
name = "stacktrace",
|
| 37 |
+
srcs = [
|
| 38 |
+
"internal/stacktrace_aarch64-inl.inc",
|
| 39 |
+
"internal/stacktrace_arm-inl.inc",
|
| 40 |
+
"internal/stacktrace_config.h",
|
| 41 |
+
"internal/stacktrace_emscripten-inl.inc",
|
| 42 |
+
"internal/stacktrace_generic-inl.inc",
|
| 43 |
+
"internal/stacktrace_powerpc-inl.inc",
|
| 44 |
+
"internal/stacktrace_riscv-inl.inc",
|
| 45 |
+
"internal/stacktrace_unimplemented-inl.inc",
|
| 46 |
+
"internal/stacktrace_win32-inl.inc",
|
| 47 |
+
"internal/stacktrace_x86-inl.inc",
|
| 48 |
+
"stacktrace.cc",
|
| 49 |
+
],
|
| 50 |
+
hdrs = ["stacktrace.h"],
|
| 51 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 52 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 53 |
+
deps = [
|
| 54 |
+
":debugging_internal",
|
| 55 |
+
"//absl/base:config",
|
| 56 |
+
"//absl/base:core_headers",
|
| 57 |
+
"//absl/base:dynamic_annotations",
|
| 58 |
+
"//absl/base:raw_logging_internal",
|
| 59 |
+
],
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
cc_test(
|
| 63 |
+
name = "stacktrace_test",
|
| 64 |
+
srcs = ["stacktrace_test.cc"],
|
| 65 |
+
copts = ABSL_TEST_COPTS,
|
| 66 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 67 |
+
deps = [
|
| 68 |
+
":stacktrace",
|
| 69 |
+
"//absl/base:core_headers",
|
| 70 |
+
"@com_google_googletest//:gtest",
|
| 71 |
+
"@com_google_googletest//:gtest_main",
|
| 72 |
+
],
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
cc_library(
|
| 76 |
+
name = "symbolize",
|
| 77 |
+
srcs = [
|
| 78 |
+
"symbolize.cc",
|
| 79 |
+
"symbolize_darwin.inc",
|
| 80 |
+
"symbolize_elf.inc",
|
| 81 |
+
"symbolize_emscripten.inc",
|
| 82 |
+
"symbolize_unimplemented.inc",
|
| 83 |
+
"symbolize_win32.inc",
|
| 84 |
+
],
|
| 85 |
+
hdrs = [
|
| 86 |
+
"internal/symbolize.h",
|
| 87 |
+
"symbolize.h",
|
| 88 |
+
],
|
| 89 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 90 |
+
linkopts = ABSL_DEFAULT_LINKOPTS + select({
|
| 91 |
+
"//absl:msvc_compiler": ["-DEFAULTLIB:dbghelp.lib"],
|
| 92 |
+
"//absl:clang-cl_compiler": ["-DEFAULTLIB:dbghelp.lib"],
|
| 93 |
+
"//absl:mingw_compiler": [
|
| 94 |
+
"-DEFAULTLIB:dbghelp.lib",
|
| 95 |
+
"-ldbghelp",
|
| 96 |
+
],
|
| 97 |
+
"//conditions:default": [],
|
| 98 |
+
}),
|
| 99 |
+
deps = [
|
| 100 |
+
":debugging_internal",
|
| 101 |
+
":demangle_internal",
|
| 102 |
+
"//absl/base",
|
| 103 |
+
"//absl/base:config",
|
| 104 |
+
"//absl/base:core_headers",
|
| 105 |
+
"//absl/base:dynamic_annotations",
|
| 106 |
+
"//absl/base:malloc_internal",
|
| 107 |
+
"//absl/base:raw_logging_internal",
|
| 108 |
+
"//absl/strings",
|
| 109 |
+
],
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
cc_test(
|
| 113 |
+
name = "symbolize_test",
|
| 114 |
+
srcs = ["symbolize_test.cc"],
|
| 115 |
+
copts = ABSL_TEST_COPTS + select({
|
| 116 |
+
"//absl:msvc_compiler": ["/Z7"],
|
| 117 |
+
"//absl:clang-cl_compiler": ["/Z7"],
|
| 118 |
+
"//conditions:default": [],
|
| 119 |
+
}),
|
| 120 |
+
linkopts = ABSL_DEFAULT_LINKOPTS + select({
|
| 121 |
+
"//absl:msvc_compiler": ["/DEBUG"],
|
| 122 |
+
"//absl:clang-cl_compiler": ["/DEBUG"],
|
| 123 |
+
"//conditions:default": [],
|
| 124 |
+
}),
|
| 125 |
+
deps = [
|
| 126 |
+
":stack_consumption",
|
| 127 |
+
":symbolize",
|
| 128 |
+
"//absl/base",
|
| 129 |
+
"//absl/base:config",
|
| 130 |
+
"//absl/base:core_headers",
|
| 131 |
+
"//absl/log",
|
| 132 |
+
"//absl/log:check",
|
| 133 |
+
"//absl/memory",
|
| 134 |
+
"//absl/strings",
|
| 135 |
+
"@com_google_googletest//:gtest",
|
| 136 |
+
],
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
cc_library(
|
| 140 |
+
name = "examine_stack",
|
| 141 |
+
srcs = [
|
| 142 |
+
"internal/examine_stack.cc",
|
| 143 |
+
],
|
| 144 |
+
hdrs = [
|
| 145 |
+
"internal/examine_stack.h",
|
| 146 |
+
],
|
| 147 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 148 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 149 |
+
visibility = ["//absl/log/internal:__pkg__"],
|
| 150 |
+
deps = [
|
| 151 |
+
":stacktrace",
|
| 152 |
+
":symbolize",
|
| 153 |
+
"//absl/base:config",
|
| 154 |
+
"//absl/base:core_headers",
|
| 155 |
+
"//absl/base:raw_logging_internal",
|
| 156 |
+
],
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
cc_library(
|
| 160 |
+
name = "failure_signal_handler",
|
| 161 |
+
srcs = ["failure_signal_handler.cc"],
|
| 162 |
+
hdrs = ["failure_signal_handler.h"],
|
| 163 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 164 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 165 |
+
deps = [
|
| 166 |
+
":examine_stack",
|
| 167 |
+
":stacktrace",
|
| 168 |
+
"//absl/base",
|
| 169 |
+
"//absl/base:config",
|
| 170 |
+
"//absl/base:core_headers",
|
| 171 |
+
"//absl/base:raw_logging_internal",
|
| 172 |
+
],
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
cc_test(
|
| 176 |
+
name = "failure_signal_handler_test",
|
| 177 |
+
srcs = ["failure_signal_handler_test.cc"],
|
| 178 |
+
copts = ABSL_TEST_COPTS,
|
| 179 |
+
linkopts = select({
|
| 180 |
+
"//absl:msvc_compiler": [],
|
| 181 |
+
"//absl:clang-cl_compiler": [],
|
| 182 |
+
"//absl:wasm": [],
|
| 183 |
+
"//conditions:default": ["-pthread"],
|
| 184 |
+
}) + ABSL_DEFAULT_LINKOPTS,
|
| 185 |
+
deps = [
|
| 186 |
+
":failure_signal_handler",
|
| 187 |
+
":stacktrace",
|
| 188 |
+
":symbolize",
|
| 189 |
+
"//absl/base:raw_logging_internal",
|
| 190 |
+
"//absl/log:check",
|
| 191 |
+
"//absl/strings",
|
| 192 |
+
"@com_google_googletest//:gtest",
|
| 193 |
+
],
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
cc_library(
|
| 197 |
+
name = "debugging_internal",
|
| 198 |
+
srcs = [
|
| 199 |
+
"internal/address_is_readable.cc",
|
| 200 |
+
"internal/elf_mem_image.cc",
|
| 201 |
+
"internal/vdso_support.cc",
|
| 202 |
+
],
|
| 203 |
+
hdrs = [
|
| 204 |
+
"internal/address_is_readable.h",
|
| 205 |
+
"internal/elf_mem_image.h",
|
| 206 |
+
"internal/vdso_support.h",
|
| 207 |
+
],
|
| 208 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 209 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 210 |
+
visibility = ["//visibility:private"],
|
| 211 |
+
deps = [
|
| 212 |
+
"//absl/base:config",
|
| 213 |
+
"//absl/base:core_headers",
|
| 214 |
+
"//absl/base:dynamic_annotations",
|
| 215 |
+
"//absl/base:errno_saver",
|
| 216 |
+
"//absl/base:raw_logging_internal",
|
| 217 |
+
],
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
cc_library(
|
| 221 |
+
name = "demangle_internal",
|
| 222 |
+
srcs = ["internal/demangle.cc"],
|
| 223 |
+
hdrs = ["internal/demangle.h"],
|
| 224 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 225 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 226 |
+
visibility = [
|
| 227 |
+
"//absl/container:__pkg__",
|
| 228 |
+
"//absl/debugging:__pkg__",
|
| 229 |
+
],
|
| 230 |
+
deps = [
|
| 231 |
+
"//absl/base",
|
| 232 |
+
"//absl/base:config",
|
| 233 |
+
"//absl/base:core_headers",
|
| 234 |
+
],
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
cc_test(
|
| 238 |
+
name = "demangle_test",
|
| 239 |
+
srcs = ["internal/demangle_test.cc"],
|
| 240 |
+
copts = ABSL_TEST_COPTS,
|
| 241 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 242 |
+
deps = [
|
| 243 |
+
":demangle_internal",
|
| 244 |
+
":stack_consumption",
|
| 245 |
+
"//absl/base:config",
|
| 246 |
+
"//absl/base:core_headers",
|
| 247 |
+
"//absl/log",
|
| 248 |
+
"//absl/memory",
|
| 249 |
+
"@com_google_googletest//:gtest",
|
| 250 |
+
"@com_google_googletest//:gtest_main",
|
| 251 |
+
],
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
cc_library(
|
| 255 |
+
name = "leak_check",
|
| 256 |
+
srcs = ["leak_check.cc"],
|
| 257 |
+
hdrs = ["leak_check.h"],
|
| 258 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 259 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 260 |
+
deps = [
|
| 261 |
+
"//absl/base:config",
|
| 262 |
+
"//absl/base:core_headers",
|
| 263 |
+
],
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
cc_test(
|
| 267 |
+
name = "leak_check_test",
|
| 268 |
+
srcs = ["leak_check_test.cc"],
|
| 269 |
+
copts = ABSL_TEST_COPTS,
|
| 270 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 271 |
+
tags = ["notsan"],
|
| 272 |
+
deps = [
|
| 273 |
+
":leak_check",
|
| 274 |
+
"//absl/base:config",
|
| 275 |
+
"//absl/log",
|
| 276 |
+
"@com_google_googletest//:gtest",
|
| 277 |
+
"@com_google_googletest//:gtest_main",
|
| 278 |
+
],
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
# Binary that leaks memory and expects to fail on exit. This isn't a
|
| 282 |
+
# test that expected to pass on its own; it exists to be called by a
|
| 283 |
+
# script that checks exit status and output.
|
| 284 |
+
# TODO(absl-team): Write a test to run this with a script that
|
| 285 |
+
# verifies that it correctly fails.
|
| 286 |
+
cc_binary(
|
| 287 |
+
name = "leak_check_fail_test_binary",
|
| 288 |
+
srcs = ["leak_check_fail_test.cc"],
|
| 289 |
+
copts = ABSL_TEST_COPTS,
|
| 290 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 291 |
+
deps = [
|
| 292 |
+
":leak_check",
|
| 293 |
+
"//absl/log",
|
| 294 |
+
"@com_google_googletest//:gtest",
|
| 295 |
+
"@com_google_googletest//:gtest_main",
|
| 296 |
+
],
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
cc_library(
|
| 300 |
+
name = "stack_consumption",
|
| 301 |
+
testonly = 1,
|
| 302 |
+
srcs = ["internal/stack_consumption.cc"],
|
| 303 |
+
hdrs = ["internal/stack_consumption.h"],
|
| 304 |
+
copts = ABSL_DEFAULT_COPTS,
|
| 305 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 306 |
+
visibility = ["//visibility:private"],
|
| 307 |
+
deps = [
|
| 308 |
+
"//absl/base:config",
|
| 309 |
+
"//absl/base:core_headers",
|
| 310 |
+
"//absl/base:raw_logging_internal",
|
| 311 |
+
],
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
cc_test(
|
| 315 |
+
name = "stack_consumption_test",
|
| 316 |
+
srcs = ["internal/stack_consumption_test.cc"],
|
| 317 |
+
copts = ABSL_TEST_COPTS,
|
| 318 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 319 |
+
tags = ["notsan"],
|
| 320 |
+
deps = [
|
| 321 |
+
":stack_consumption",
|
| 322 |
+
"//absl/base:core_headers",
|
| 323 |
+
"//absl/log",
|
| 324 |
+
"@com_google_googletest//:gtest",
|
| 325 |
+
"@com_google_googletest//:gtest_main",
|
| 326 |
+
],
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
cc_binary(
|
| 330 |
+
name = "stacktrace_benchmark",
|
| 331 |
+
testonly = 1,
|
| 332 |
+
srcs = ["stacktrace_benchmark.cc"],
|
| 333 |
+
copts = ABSL_TEST_COPTS,
|
| 334 |
+
linkopts = ABSL_DEFAULT_LINKOPTS,
|
| 335 |
+
tags = ["benchmark"],
|
| 336 |
+
deps = [
|
| 337 |
+
":stacktrace",
|
| 338 |
+
"//absl/base:config",
|
| 339 |
+
"//absl/base:core_headers",
|
| 340 |
+
"@com_github_google_benchmark//:benchmark_main",
|
| 341 |
+
],
|
| 342 |
+
)
|
weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler.cc
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
//
|
| 16 |
+
|
| 17 |
+
#include "absl/debugging/failure_signal_handler.h"
|
| 18 |
+
|
| 19 |
+
#include "absl/base/config.h"
|
| 20 |
+
|
| 21 |
+
#ifdef _WIN32
|
| 22 |
+
#include <windows.h>
|
| 23 |
+
#else
|
| 24 |
+
#include <sched.h>
|
| 25 |
+
#include <unistd.h>
|
| 26 |
+
#endif
|
| 27 |
+
|
| 28 |
+
#ifdef __APPLE__
|
| 29 |
+
#include <TargetConditionals.h>
|
| 30 |
+
#endif
|
| 31 |
+
|
| 32 |
+
#ifdef ABSL_HAVE_MMAP
|
| 33 |
+
#include <sys/mman.h>
|
| 34 |
+
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
|
| 35 |
+
#define MAP_ANONYMOUS MAP_ANON
|
| 36 |
+
#endif
|
| 37 |
+
#endif
|
| 38 |
+
|
| 39 |
+
#ifdef __linux__
|
| 40 |
+
#include <sys/prctl.h>
|
| 41 |
+
#endif
|
| 42 |
+
|
| 43 |
+
#include <algorithm>
|
| 44 |
+
#include <atomic>
|
| 45 |
+
#include <cerrno>
|
| 46 |
+
#include <csignal>
|
| 47 |
+
#include <cstdio>
|
| 48 |
+
#include <cstring>
|
| 49 |
+
#include <ctime>
|
| 50 |
+
|
| 51 |
+
#include "absl/base/attributes.h"
|
| 52 |
+
#include "absl/base/internal/raw_logging.h"
|
| 53 |
+
#include "absl/base/internal/sysinfo.h"
|
| 54 |
+
#include "absl/debugging/internal/examine_stack.h"
|
| 55 |
+
#include "absl/debugging/stacktrace.h"
|
| 56 |
+
|
| 57 |
+
#if !defined(_WIN32) && !defined(__wasi__)
|
| 58 |
+
#define ABSL_HAVE_SIGACTION
|
| 59 |
+
// Apple WatchOS and TVOS don't allow sigaltstack
|
| 60 |
+
// Apple macOS has sigaltstack, but using it makes backtrace() unusable.
|
| 61 |
+
#if !(defined(TARGET_OS_OSX) && TARGET_OS_OSX) && \
|
| 62 |
+
!(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \
|
| 63 |
+
!(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__)
|
| 64 |
+
#define ABSL_HAVE_SIGALTSTACK
|
| 65 |
+
#endif
|
| 66 |
+
#endif
|
| 67 |
+
|
| 68 |
+
namespace absl {
|
| 69 |
+
ABSL_NAMESPACE_BEGIN
|
| 70 |
+
|
| 71 |
+
ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options;
|
| 72 |
+
|
| 73 |
+
// Resets the signal handler for signo to the default action for that
|
| 74 |
+
// signal, then raises the signal.
|
| 75 |
+
static void RaiseToDefaultHandler(int signo) {
|
| 76 |
+
signal(signo, SIG_DFL);
|
| 77 |
+
raise(signo);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
struct FailureSignalData {
|
| 81 |
+
const int signo;
|
| 82 |
+
const char* const as_string;
|
| 83 |
+
#ifdef ABSL_HAVE_SIGACTION
|
| 84 |
+
struct sigaction previous_action;
|
| 85 |
+
// StructSigaction is used to silence -Wmissing-field-initializers.
|
| 86 |
+
using StructSigaction = struct sigaction;
|
| 87 |
+
#define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction()
|
| 88 |
+
#else
|
| 89 |
+
void (*previous_handler)(int);
|
| 90 |
+
#define FSD_PREVIOUS_INIT SIG_DFL
|
| 91 |
+
#endif
|
| 92 |
+
};
|
| 93 |
+
|
| 94 |
+
ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = {
|
| 95 |
+
{SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT},
|
| 96 |
+
{SIGILL, "SIGILL", FSD_PREVIOUS_INIT},
|
| 97 |
+
{SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT},
|
| 98 |
+
{SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT},
|
| 99 |
+
{SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT},
|
| 100 |
+
#ifndef _WIN32
|
| 101 |
+
{SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT},
|
| 102 |
+
{SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT},
|
| 103 |
+
#endif
|
| 104 |
+
};
|
| 105 |
+
|
| 106 |
+
#undef FSD_PREVIOUS_INIT
|
| 107 |
+
|
| 108 |
+
static void RaiseToPreviousHandler(int signo) {
|
| 109 |
+
// Search for the previous handler.
|
| 110 |
+
for (const auto& it : failure_signal_data) {
|
| 111 |
+
if (it.signo == signo) {
|
| 112 |
+
#ifdef ABSL_HAVE_SIGACTION
|
| 113 |
+
sigaction(signo, &it.previous_action, nullptr);
|
| 114 |
+
#else
|
| 115 |
+
signal(signo, it.previous_handler);
|
| 116 |
+
#endif
|
| 117 |
+
raise(signo);
|
| 118 |
+
return;
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
// Not found, use the default handler.
|
| 123 |
+
RaiseToDefaultHandler(signo);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
namespace debugging_internal {
|
| 127 |
+
|
| 128 |
+
const char* FailureSignalToString(int signo) {
|
| 129 |
+
for (const auto& it : failure_signal_data) {
|
| 130 |
+
if (it.signo == signo) {
|
| 131 |
+
return it.as_string;
|
| 132 |
+
}
|
| 133 |
+
}
|
| 134 |
+
return "";
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
} // namespace debugging_internal
|
| 138 |
+
|
| 139 |
+
#ifdef ABSL_HAVE_SIGALTSTACK
|
| 140 |
+
|
| 141 |
+
static bool SetupAlternateStackOnce() {
|
| 142 |
+
#if defined(__wasm__) || defined(__asjms__)
|
| 143 |
+
const size_t page_mask = getpagesize() - 1;
|
| 144 |
+
#else
|
| 145 |
+
const size_t page_mask = static_cast<size_t>(sysconf(_SC_PAGESIZE)) - 1;
|
| 146 |
+
#endif
|
| 147 |
+
size_t stack_size =
|
| 148 |
+
(std::max(static_cast<size_t>(SIGSTKSZ), size_t{65536}) + page_mask) &
|
| 149 |
+
~page_mask;
|
| 150 |
+
#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
|
| 151 |
+
defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
|
| 152 |
+
// Account for sanitizer instrumentation requiring additional stack space.
|
| 153 |
+
stack_size *= 5;
|
| 154 |
+
#endif
|
| 155 |
+
|
| 156 |
+
stack_t sigstk;
|
| 157 |
+
memset(&sigstk, 0, sizeof(sigstk));
|
| 158 |
+
sigstk.ss_size = stack_size;
|
| 159 |
+
|
| 160 |
+
#ifdef ABSL_HAVE_MMAP
|
| 161 |
+
#ifndef MAP_STACK
|
| 162 |
+
#define MAP_STACK 0
|
| 163 |
+
#endif
|
| 164 |
+
sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE,
|
| 165 |
+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
| 166 |
+
if (sigstk.ss_sp == MAP_FAILED) {
|
| 167 |
+
ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed");
|
| 168 |
+
}
|
| 169 |
+
#else
|
| 170 |
+
sigstk.ss_sp = malloc(sigstk.ss_size);
|
| 171 |
+
if (sigstk.ss_sp == nullptr) {
|
| 172 |
+
ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed");
|
| 173 |
+
}
|
| 174 |
+
#endif
|
| 175 |
+
|
| 176 |
+
if (sigaltstack(&sigstk, nullptr) != 0) {
|
| 177 |
+
ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno);
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
#ifdef __linux__
|
| 181 |
+
#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME)
|
| 182 |
+
// Make a best-effort attempt to name the allocated region in
|
| 183 |
+
// /proc/$PID/smaps.
|
| 184 |
+
//
|
| 185 |
+
// The call to prctl() may fail if the kernel was not configured with the
|
| 186 |
+
// CONFIG_ANON_VMA_NAME kernel option. This is OK since the call is
|
| 187 |
+
// primarily a debugging aid.
|
| 188 |
+
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, sigstk.ss_sp, sigstk.ss_size,
|
| 189 |
+
"absl-signalstack");
|
| 190 |
+
#endif
|
| 191 |
+
#endif // __linux__
|
| 192 |
+
|
| 193 |
+
return true;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
#endif
|
| 197 |
+
|
| 198 |
+
#ifdef ABSL_HAVE_SIGACTION
|
| 199 |
+
|
| 200 |
+
// Sets up an alternate stack for signal handlers once.
|
| 201 |
+
// Returns the appropriate flag for sig_action.sa_flags
|
| 202 |
+
// if the system supports using an alternate stack.
|
| 203 |
+
static int MaybeSetupAlternateStack() {
|
| 204 |
+
#ifdef ABSL_HAVE_SIGALTSTACK
|
| 205 |
+
ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce();
|
| 206 |
+
return SA_ONSTACK;
|
| 207 |
+
#else
|
| 208 |
+
return 0;
|
| 209 |
+
#endif
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
static void InstallOneFailureHandler(FailureSignalData* data,
|
| 213 |
+
void (*handler)(int, siginfo_t*, void*)) {
|
| 214 |
+
struct sigaction act;
|
| 215 |
+
memset(&act, 0, sizeof(act));
|
| 216 |
+
sigemptyset(&act.sa_mask);
|
| 217 |
+
act.sa_flags |= SA_SIGINFO;
|
| 218 |
+
// SA_NODEFER is required to handle SIGABRT from
|
| 219 |
+
// ImmediateAbortSignalHandler().
|
| 220 |
+
act.sa_flags |= SA_NODEFER;
|
| 221 |
+
if (fsh_options.use_alternate_stack) {
|
| 222 |
+
act.sa_flags |= MaybeSetupAlternateStack();
|
| 223 |
+
}
|
| 224 |
+
act.sa_sigaction = handler;
|
| 225 |
+
ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0,
|
| 226 |
+
"sigaction() failed");
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
#else
|
| 230 |
+
|
| 231 |
+
static void InstallOneFailureHandler(FailureSignalData* data,
|
| 232 |
+
void (*handler)(int)) {
|
| 233 |
+
data->previous_handler = signal(data->signo, handler);
|
| 234 |
+
ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed");
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
#endif
|
| 238 |
+
|
| 239 |
+
static void WriteSignalMessage(int signo, int cpu,
|
| 240 |
+
void (*writerfn)(const char*)) {
|
| 241 |
+
char buf[96];
|
| 242 |
+
char on_cpu[32] = {0};
|
| 243 |
+
if (cpu != -1) {
|
| 244 |
+
snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu);
|
| 245 |
+
}
|
| 246 |
+
const char* const signal_string =
|
| 247 |
+
debugging_internal::FailureSignalToString(signo);
|
| 248 |
+
if (signal_string != nullptr && signal_string[0] != '\0') {
|
| 249 |
+
snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n",
|
| 250 |
+
signal_string,
|
| 251 |
+
static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
|
| 252 |
+
on_cpu);
|
| 253 |
+
} else {
|
| 254 |
+
snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n",
|
| 255 |
+
signo, static_cast<long>(time(nullptr)), // NOLINT(runtime/int)
|
| 256 |
+
on_cpu);
|
| 257 |
+
}
|
| 258 |
+
writerfn(buf);
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
// `void*` might not be big enough to store `void(*)(const char*)`.
|
| 262 |
+
struct WriterFnStruct {
|
| 263 |
+
void (*writerfn)(const char*);
|
| 264 |
+
};
|
| 265 |
+
|
| 266 |
+
// Many of the absl::debugging_internal::Dump* functions in
|
| 267 |
+
// examine_stack.h take a writer function pointer that has a void* arg
|
| 268 |
+
// for historical reasons. failure_signal_handler_writer only takes a
|
| 269 |
+
// data pointer. This function converts between these types.
|
| 270 |
+
static void WriterFnWrapper(const char* data, void* arg) {
|
| 271 |
+
static_cast<WriterFnStruct*>(arg)->writerfn(data);
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
// Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal
|
| 275 |
+
// handlers. "noinline" so that GetStackFrames() skips the top-most stack
|
| 276 |
+
// frame for this function.
|
| 277 |
+
ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace(
|
| 278 |
+
void* ucontext, bool symbolize_stacktrace,
|
| 279 |
+
void (*writerfn)(const char*, void*), void* writerfn_arg) {
|
| 280 |
+
constexpr int kNumStackFrames = 32;
|
| 281 |
+
void* stack[kNumStackFrames];
|
| 282 |
+
int frame_sizes[kNumStackFrames];
|
| 283 |
+
int min_dropped_frames;
|
| 284 |
+
int depth = absl::GetStackFramesWithContext(
|
| 285 |
+
stack, frame_sizes, kNumStackFrames,
|
| 286 |
+
1, // Do not include this function in stack trace.
|
| 287 |
+
ucontext, &min_dropped_frames);
|
| 288 |
+
absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace(
|
| 289 |
+
absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes,
|
| 290 |
+
depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg);
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
// Called by AbslFailureSignalHandler() to write the failure info. It is
|
| 294 |
+
// called once with writerfn set to WriteToStderr() and then possibly
|
| 295 |
+
// with writerfn set to the user provided function.
|
| 296 |
+
static void WriteFailureInfo(int signo, void* ucontext, int cpu,
|
| 297 |
+
void (*writerfn)(const char*)) {
|
| 298 |
+
WriterFnStruct writerfn_struct{writerfn};
|
| 299 |
+
WriteSignalMessage(signo, cpu, writerfn);
|
| 300 |
+
WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper,
|
| 301 |
+
&writerfn_struct);
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
// absl::SleepFor() can't be used here since AbslInternalSleepFor()
|
| 305 |
+
// may be overridden to do something that isn't async-signal-safe on
|
| 306 |
+
// some platforms.
|
| 307 |
+
static void PortableSleepForSeconds(int seconds) {
|
| 308 |
+
#ifdef _WIN32
|
| 309 |
+
Sleep(static_cast<DWORD>(seconds * 1000));
|
| 310 |
+
#else
|
| 311 |
+
struct timespec sleep_time;
|
| 312 |
+
sleep_time.tv_sec = seconds;
|
| 313 |
+
sleep_time.tv_nsec = 0;
|
| 314 |
+
while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
|
| 315 |
+
}
|
| 316 |
+
#endif
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
#ifdef ABSL_HAVE_ALARM
|
| 320 |
+
// AbslFailureSignalHandler() installs this as a signal handler for
|
| 321 |
+
// SIGALRM, then sets an alarm to be delivered to the program after a
|
| 322 |
+
// set amount of time. If AbslFailureSignalHandler() hangs for more than
|
| 323 |
+
// the alarm timeout, ImmediateAbortSignalHandler() will abort the
|
| 324 |
+
// program.
|
| 325 |
+
static void ImmediateAbortSignalHandler(int) { RaiseToDefaultHandler(SIGABRT); }
|
| 326 |
+
#endif
|
| 327 |
+
|
| 328 |
+
// absl::base_internal::GetTID() returns pid_t on most platforms, but
|
| 329 |
+
// returns absl::base_internal::pid_t on Windows.
|
| 330 |
+
using GetTidType = decltype(absl::base_internal::GetTID());
|
| 331 |
+
ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0);
|
| 332 |
+
|
| 333 |
+
#ifndef ABSL_HAVE_SIGACTION
|
| 334 |
+
static void AbslFailureSignalHandler(int signo) {
|
| 335 |
+
void* ucontext = nullptr;
|
| 336 |
+
#else
|
| 337 |
+
static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) {
|
| 338 |
+
#endif
|
| 339 |
+
|
| 340 |
+
const GetTidType this_tid = absl::base_internal::GetTID();
|
| 341 |
+
GetTidType previous_failed_tid = 0;
|
| 342 |
+
if (!failed_tid.compare_exchange_strong(previous_failed_tid, this_tid,
|
| 343 |
+
std::memory_order_acq_rel,
|
| 344 |
+
std::memory_order_relaxed)) {
|
| 345 |
+
ABSL_RAW_LOG(
|
| 346 |
+
ERROR,
|
| 347 |
+
"Signal %d raised at PC=%p while already in AbslFailureSignalHandler()",
|
| 348 |
+
signo, absl::debugging_internal::GetProgramCounter(ucontext));
|
| 349 |
+
if (this_tid != previous_failed_tid) {
|
| 350 |
+
// Another thread is already in AbslFailureSignalHandler(), so wait
|
| 351 |
+
// a bit for it to finish. If the other thread doesn't kill us,
|
| 352 |
+
// we do so after sleeping.
|
| 353 |
+
PortableSleepForSeconds(3);
|
| 354 |
+
RaiseToDefaultHandler(signo);
|
| 355 |
+
// The recursively raised signal may be blocked until we return.
|
| 356 |
+
return;
|
| 357 |
+
}
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
// Increase the chance that the CPU we report was the same CPU on which the
|
| 361 |
+
// signal was received by doing this as early as possible, i.e. after
|
| 362 |
+
// verifying that this is not a recursive signal handler invocation.
|
| 363 |
+
int my_cpu = -1;
|
| 364 |
+
#ifdef ABSL_HAVE_SCHED_GETCPU
|
| 365 |
+
my_cpu = sched_getcpu();
|
| 366 |
+
#endif
|
| 367 |
+
|
| 368 |
+
#ifdef ABSL_HAVE_ALARM
|
| 369 |
+
// Set an alarm to abort the program in case this code hangs or deadlocks.
|
| 370 |
+
if (fsh_options.alarm_on_failure_secs > 0) {
|
| 371 |
+
alarm(0); // Cancel any existing alarms.
|
| 372 |
+
signal(SIGALRM, ImmediateAbortSignalHandler);
|
| 373 |
+
alarm(static_cast<unsigned int>(fsh_options.alarm_on_failure_secs));
|
| 374 |
+
}
|
| 375 |
+
#endif
|
| 376 |
+
|
| 377 |
+
// First write to stderr.
|
| 378 |
+
WriteFailureInfo(
|
| 379 |
+
signo, ucontext, my_cpu, +[](const char* data) {
|
| 380 |
+
absl::raw_log_internal::AsyncSignalSafeWriteError(data, strlen(data));
|
| 381 |
+
});
|
| 382 |
+
|
| 383 |
+
// Riskier code (because it is less likely to be async-signal-safe)
|
| 384 |
+
// goes after this point.
|
| 385 |
+
if (fsh_options.writerfn != nullptr) {
|
| 386 |
+
WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn);
|
| 387 |
+
fsh_options.writerfn(nullptr);
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
if (fsh_options.call_previous_handler) {
|
| 391 |
+
RaiseToPreviousHandler(signo);
|
| 392 |
+
} else {
|
| 393 |
+
RaiseToDefaultHandler(signo);
|
| 394 |
+
}
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) {
|
| 398 |
+
fsh_options = options;
|
| 399 |
+
for (auto& it : failure_signal_data) {
|
| 400 |
+
InstallOneFailureHandler(&it, AbslFailureSignalHandler);
|
| 401 |
+
}
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
ABSL_NAMESPACE_END
|
| 405 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler.h
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
//
|
| 15 |
+
// -----------------------------------------------------------------------------
|
| 16 |
+
// File: failure_signal_handler.h
|
| 17 |
+
// -----------------------------------------------------------------------------
|
| 18 |
+
//
|
| 19 |
+
// This file configures the Abseil *failure signal handler* to capture and dump
|
| 20 |
+
// useful debugging information (such as a stacktrace) upon program failure.
|
| 21 |
+
//
|
| 22 |
+
// To use the failure signal handler, call `absl::InstallFailureSignalHandler()`
|
| 23 |
+
// very early in your program, usually in the first few lines of main():
|
| 24 |
+
//
|
| 25 |
+
// int main(int argc, char** argv) {
|
| 26 |
+
// // Initialize the symbolizer to get a human-readable stack trace
|
| 27 |
+
// absl::InitializeSymbolizer(argv[0]);
|
| 28 |
+
//
|
| 29 |
+
// absl::FailureSignalHandlerOptions options;
|
| 30 |
+
// absl::InstallFailureSignalHandler(options);
|
| 31 |
+
// DoSomethingInteresting();
|
| 32 |
+
// return 0;
|
| 33 |
+
// }
|
| 34 |
+
//
|
| 35 |
+
// Any program that raises a fatal signal (such as `SIGSEGV`, `SIGILL`,
|
| 36 |
+
// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP`) will call the
|
| 37 |
+
// installed failure signal handler and provide debugging information to stderr.
|
| 38 |
+
//
|
| 39 |
+
// Note that you should *not* install the Abseil failure signal handler more
|
| 40 |
+
// than once. You may, of course, have another (non-Abseil) failure signal
|
| 41 |
+
// handler installed (which would be triggered if Abseil's failure signal
|
| 42 |
+
// handler sets `call_previous_handler` to `true`).
|
| 43 |
+
|
| 44 |
+
#ifndef ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
|
| 45 |
+
#define ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
|
| 46 |
+
|
| 47 |
+
#include "absl/base/config.h"
|
| 48 |
+
|
| 49 |
+
namespace absl {
|
| 50 |
+
ABSL_NAMESPACE_BEGIN
|
| 51 |
+
|
| 52 |
+
// FailureSignalHandlerOptions
|
| 53 |
+
//
|
| 54 |
+
// Struct for holding `absl::InstallFailureSignalHandler()` configuration
|
| 55 |
+
// options.
|
| 56 |
+
struct FailureSignalHandlerOptions {
|
| 57 |
+
// If true, try to symbolize the stacktrace emitted on failure, provided that
|
| 58 |
+
// you have initialized a symbolizer for that purpose. (See symbolize.h for
|
| 59 |
+
// more information.)
|
| 60 |
+
bool symbolize_stacktrace = true;
|
| 61 |
+
|
| 62 |
+
// If true, try to run signal handlers on an alternate stack (if supported on
|
| 63 |
+
// the given platform). An alternate stack is useful for program crashes due
|
| 64 |
+
// to a stack overflow; by running on a alternate stack, the signal handler
|
| 65 |
+
// may run even when normal stack space has been exhausted. The downside of
|
| 66 |
+
// using an alternate stack is that extra memory for the alternate stack needs
|
| 67 |
+
// to be pre-allocated.
|
| 68 |
+
bool use_alternate_stack = true;
|
| 69 |
+
|
| 70 |
+
// If positive, indicates the number of seconds after which the failure signal
|
| 71 |
+
// handler is invoked to abort the program. Setting such an alarm is useful in
|
| 72 |
+
// cases where the failure signal handler itself may become hung or
|
| 73 |
+
// deadlocked.
|
| 74 |
+
int alarm_on_failure_secs = 3;
|
| 75 |
+
|
| 76 |
+
// If true, call the previously registered signal handler for the signal that
|
| 77 |
+
// was received (if one was registered) after the existing signal handler
|
| 78 |
+
// runs. This mechanism can be used to chain signal handlers together.
|
| 79 |
+
//
|
| 80 |
+
// If false, the signal is raised to the default handler for that signal
|
| 81 |
+
// (which normally terminates the program).
|
| 82 |
+
//
|
| 83 |
+
// IMPORTANT: If true, the chained fatal signal handlers must not try to
|
| 84 |
+
// recover from the fatal signal. Instead, they should terminate the program
|
| 85 |
+
// via some mechanism, like raising the default handler for the signal, or by
|
| 86 |
+
// calling `_exit()`. Note that the failure signal handler may put parts of
|
| 87 |
+
// the Abseil library into a state from which they cannot recover.
|
| 88 |
+
bool call_previous_handler = false;
|
| 89 |
+
|
| 90 |
+
// If non-null, indicates a pointer to a callback function that will be called
|
| 91 |
+
// upon failure, with a string argument containing failure data. This function
|
| 92 |
+
// may be used as a hook to write failure data to a secondary location, such
|
| 93 |
+
// as a log file. This function will also be called with null data, as a hint
|
| 94 |
+
// to flush any buffered data before the program may be terminated. Consider
|
| 95 |
+
// flushing any buffered data in all calls to this function.
|
| 96 |
+
//
|
| 97 |
+
// Since this function runs within a signal handler, it should be
|
| 98 |
+
// async-signal-safe if possible.
|
| 99 |
+
// See http://man7.org/linux/man-pages/man7/signal-safety.7.html
|
| 100 |
+
void (*writerfn)(const char*) = nullptr;
|
| 101 |
+
};
|
| 102 |
+
|
| 103 |
+
// InstallFailureSignalHandler()
|
| 104 |
+
//
|
| 105 |
+
// Installs a signal handler for the common failure signals `SIGSEGV`, `SIGILL`,
|
| 106 |
+
// `SIGFPE`, `SIGABRT`, `SIGTERM`, `SIGBUG`, and `SIGTRAP` (provided they exist
|
| 107 |
+
// on the given platform). The failure signal handler dumps program failure data
|
| 108 |
+
// useful for debugging in an unspecified format to stderr. This data may
|
| 109 |
+
// include the program counter, a stacktrace, and register information on some
|
| 110 |
+
// systems; do not rely on an exact format for the output, as it is subject to
|
| 111 |
+
// change.
|
| 112 |
+
void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options);
|
| 113 |
+
|
| 114 |
+
namespace debugging_internal {
|
| 115 |
+
const char* FailureSignalToString(int signo);
|
| 116 |
+
} // namespace debugging_internal
|
| 117 |
+
|
| 118 |
+
ABSL_NAMESPACE_END
|
| 119 |
+
} // namespace absl
|
| 120 |
+
|
| 121 |
+
#endif // ABSL_DEBUGGING_FAILURE_SIGNAL_HANDLER_H_
|
weight/_dep/abseil-cpp/absl/debugging/failure_signal_handler_test.cc
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
//
|
| 16 |
+
|
| 17 |
+
#include "absl/debugging/failure_signal_handler.h"
|
| 18 |
+
|
| 19 |
+
#include <csignal>
|
| 20 |
+
#include <cstdio>
|
| 21 |
+
#include <cstdlib>
|
| 22 |
+
#include <cstring>
|
| 23 |
+
#include <fstream>
|
| 24 |
+
|
| 25 |
+
#include "gmock/gmock.h"
|
| 26 |
+
#include "gtest/gtest.h"
|
| 27 |
+
#include "absl/base/internal/raw_logging.h"
|
| 28 |
+
#include "absl/debugging/stacktrace.h"
|
| 29 |
+
#include "absl/debugging/symbolize.h"
|
| 30 |
+
#include "absl/log/check.h"
|
| 31 |
+
#include "absl/strings/match.h"
|
| 32 |
+
#include "absl/strings/str_cat.h"
|
| 33 |
+
|
| 34 |
+
namespace {
|
| 35 |
+
|
| 36 |
+
using testing::StartsWith;
|
| 37 |
+
|
| 38 |
+
#if GTEST_HAS_DEATH_TEST
|
| 39 |
+
|
| 40 |
+
// For the parameterized death tests. GetParam() returns the signal number.
|
| 41 |
+
using FailureSignalHandlerDeathTest = ::testing::TestWithParam<int>;
|
| 42 |
+
|
| 43 |
+
// This function runs in a fork()ed process on most systems.
|
| 44 |
+
void InstallHandlerAndRaise(int signo) {
|
| 45 |
+
absl::InstallFailureSignalHandler(absl::FailureSignalHandlerOptions());
|
| 46 |
+
raise(signo);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
TEST_P(FailureSignalHandlerDeathTest, AbslFailureSignal) {
|
| 50 |
+
const int signo = GetParam();
|
| 51 |
+
std::string exit_regex = absl::StrCat(
|
| 52 |
+
"\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
|
| 53 |
+
" received at time=");
|
| 54 |
+
#ifndef _WIN32
|
| 55 |
+
EXPECT_EXIT(InstallHandlerAndRaise(signo), testing::KilledBySignal(signo),
|
| 56 |
+
exit_regex);
|
| 57 |
+
#else
|
| 58 |
+
// Windows doesn't have testing::KilledBySignal().
|
| 59 |
+
EXPECT_DEATH_IF_SUPPORTED(InstallHandlerAndRaise(signo), exit_regex);
|
| 60 |
+
#endif
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
ABSL_CONST_INIT FILE* error_file = nullptr;
|
| 64 |
+
|
| 65 |
+
void WriteToErrorFile(const char* msg) {
|
| 66 |
+
if (msg != nullptr) {
|
| 67 |
+
ABSL_RAW_CHECK(fwrite(msg, strlen(msg), 1, error_file) == 1,
|
| 68 |
+
"fwrite() failed");
|
| 69 |
+
}
|
| 70 |
+
ABSL_RAW_CHECK(fflush(error_file) == 0, "fflush() failed");
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
std::string GetTmpDir() {
|
| 74 |
+
// TEST_TMPDIR is set by Bazel. Try the others when not running under Bazel.
|
| 75 |
+
static const char* const kTmpEnvVars[] = {"TEST_TMPDIR", "TMPDIR", "TEMP",
|
| 76 |
+
"TEMPDIR", "TMP"};
|
| 77 |
+
for (const char* const var : kTmpEnvVars) {
|
| 78 |
+
const char* tmp_dir = std::getenv(var);
|
| 79 |
+
if (tmp_dir != nullptr) {
|
| 80 |
+
return tmp_dir;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// Try something reasonable.
|
| 85 |
+
return "/tmp";
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
// This function runs in a fork()ed process on most systems.
|
| 89 |
+
void InstallHandlerWithWriteToFileAndRaise(const char* file, int signo) {
|
| 90 |
+
error_file = fopen(file, "w");
|
| 91 |
+
CHECK_NE(error_file, nullptr) << "Failed create error_file";
|
| 92 |
+
absl::FailureSignalHandlerOptions options;
|
| 93 |
+
options.writerfn = WriteToErrorFile;
|
| 94 |
+
absl::InstallFailureSignalHandler(options);
|
| 95 |
+
raise(signo);
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) {
|
| 99 |
+
const int signo = GetParam();
|
| 100 |
+
std::string tmp_dir = GetTmpDir();
|
| 101 |
+
std::string file = absl::StrCat(tmp_dir, "/signo_", signo);
|
| 102 |
+
|
| 103 |
+
std::string exit_regex = absl::StrCat(
|
| 104 |
+
"\\*\\*\\* ", absl::debugging_internal::FailureSignalToString(signo),
|
| 105 |
+
" received at time=");
|
| 106 |
+
#ifndef _WIN32
|
| 107 |
+
EXPECT_EXIT(InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo),
|
| 108 |
+
testing::KilledBySignal(signo), exit_regex);
|
| 109 |
+
#else
|
| 110 |
+
// Windows doesn't have testing::KilledBySignal().
|
| 111 |
+
EXPECT_DEATH_IF_SUPPORTED(
|
| 112 |
+
InstallHandlerWithWriteToFileAndRaise(file.c_str(), signo), exit_regex);
|
| 113 |
+
#endif
|
| 114 |
+
|
| 115 |
+
// Open the file in this process and check its contents.
|
| 116 |
+
std::fstream error_output(file);
|
| 117 |
+
ASSERT_TRUE(error_output.is_open()) << file;
|
| 118 |
+
std::string error_line;
|
| 119 |
+
std::getline(error_output, error_line);
|
| 120 |
+
EXPECT_THAT(
|
| 121 |
+
error_line,
|
| 122 |
+
StartsWith(absl::StrCat(
|
| 123 |
+
"*** ", absl::debugging_internal::FailureSignalToString(signo),
|
| 124 |
+
" received at ")));
|
| 125 |
+
|
| 126 |
+
// On platforms where it is possible to get the current CPU, the
|
| 127 |
+
// CPU number is also logged. Check that it is present in output.
|
| 128 |
+
#if defined(__linux__)
|
| 129 |
+
EXPECT_THAT(error_line, testing::HasSubstr(" on cpu "));
|
| 130 |
+
#endif
|
| 131 |
+
|
| 132 |
+
if (absl::debugging_internal::StackTraceWorksForTest()) {
|
| 133 |
+
std::getline(error_output, error_line);
|
| 134 |
+
EXPECT_THAT(error_line, StartsWith("PC: "));
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
constexpr int kFailureSignals[] = {
|
| 139 |
+
SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGTERM,
|
| 140 |
+
#ifndef _WIN32
|
| 141 |
+
SIGBUS, SIGTRAP,
|
| 142 |
+
#endif
|
| 143 |
+
};
|
| 144 |
+
|
| 145 |
+
std::string SignalParamToString(const ::testing::TestParamInfo<int>& info) {
|
| 146 |
+
std::string result =
|
| 147 |
+
absl::debugging_internal::FailureSignalToString(info.param);
|
| 148 |
+
if (result.empty()) {
|
| 149 |
+
result = absl::StrCat(info.param);
|
| 150 |
+
}
|
| 151 |
+
return result;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
INSTANTIATE_TEST_SUITE_P(AbslDeathTest, FailureSignalHandlerDeathTest,
|
| 155 |
+
::testing::ValuesIn(kFailureSignals),
|
| 156 |
+
SignalParamToString);
|
| 157 |
+
|
| 158 |
+
#endif // GTEST_HAS_DEATH_TEST
|
| 159 |
+
|
| 160 |
+
} // namespace
|
| 161 |
+
|
| 162 |
+
int main(int argc, char** argv) {
|
| 163 |
+
absl::InitializeSymbolizer(argv[0]);
|
| 164 |
+
testing::InitGoogleTest(&argc, argv);
|
| 165 |
+
return RUN_ALL_TESTS();
|
| 166 |
+
}
|
weight/_dep/abseil-cpp/absl/debugging/internal/demangle.cc
ADDED
|
@@ -0,0 +1,2012 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
// For reference check out:
|
| 16 |
+
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
|
| 17 |
+
//
|
| 18 |
+
// Note that we only have partial C++11 support yet.
|
| 19 |
+
|
| 20 |
+
#include "absl/debugging/internal/demangle.h"
|
| 21 |
+
|
| 22 |
+
#include <cstdint>
|
| 23 |
+
#include <cstdio>
|
| 24 |
+
#include <cstdlib>
|
| 25 |
+
#include <limits>
|
| 26 |
+
#include <string>
|
| 27 |
+
|
| 28 |
+
#include "absl/base/config.h"
|
| 29 |
+
|
| 30 |
+
#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
|
| 31 |
+
#include <cxxabi.h>
|
| 32 |
+
#endif
|
| 33 |
+
|
| 34 |
+
namespace absl {
|
| 35 |
+
ABSL_NAMESPACE_BEGIN
|
| 36 |
+
namespace debugging_internal {
|
| 37 |
+
|
| 38 |
+
typedef struct {
|
| 39 |
+
const char *abbrev;
|
| 40 |
+
const char *real_name;
|
| 41 |
+
// Number of arguments in <expression> context, or 0 if disallowed.
|
| 42 |
+
int arity;
|
| 43 |
+
} AbbrevPair;
|
| 44 |
+
|
| 45 |
+
// List of operators from Itanium C++ ABI.
|
| 46 |
+
static const AbbrevPair kOperatorList[] = {
|
| 47 |
+
// New has special syntax (not currently supported).
|
| 48 |
+
{"nw", "new", 0},
|
| 49 |
+
{"na", "new[]", 0},
|
| 50 |
+
|
| 51 |
+
// Works except that the 'gs' prefix is not supported.
|
| 52 |
+
{"dl", "delete", 1},
|
| 53 |
+
{"da", "delete[]", 1},
|
| 54 |
+
|
| 55 |
+
{"ps", "+", 1}, // "positive"
|
| 56 |
+
{"ng", "-", 1}, // "negative"
|
| 57 |
+
{"ad", "&", 1}, // "address-of"
|
| 58 |
+
{"de", "*", 1}, // "dereference"
|
| 59 |
+
{"co", "~", 1},
|
| 60 |
+
|
| 61 |
+
{"pl", "+", 2},
|
| 62 |
+
{"mi", "-", 2},
|
| 63 |
+
{"ml", "*", 2},
|
| 64 |
+
{"dv", "/", 2},
|
| 65 |
+
{"rm", "%", 2},
|
| 66 |
+
{"an", "&", 2},
|
| 67 |
+
{"or", "|", 2},
|
| 68 |
+
{"eo", "^", 2},
|
| 69 |
+
{"aS", "=", 2},
|
| 70 |
+
{"pL", "+=", 2},
|
| 71 |
+
{"mI", "-=", 2},
|
| 72 |
+
{"mL", "*=", 2},
|
| 73 |
+
{"dV", "/=", 2},
|
| 74 |
+
{"rM", "%=", 2},
|
| 75 |
+
{"aN", "&=", 2},
|
| 76 |
+
{"oR", "|=", 2},
|
| 77 |
+
{"eO", "^=", 2},
|
| 78 |
+
{"ls", "<<", 2},
|
| 79 |
+
{"rs", ">>", 2},
|
| 80 |
+
{"lS", "<<=", 2},
|
| 81 |
+
{"rS", ">>=", 2},
|
| 82 |
+
{"eq", "==", 2},
|
| 83 |
+
{"ne", "!=", 2},
|
| 84 |
+
{"lt", "<", 2},
|
| 85 |
+
{"gt", ">", 2},
|
| 86 |
+
{"le", "<=", 2},
|
| 87 |
+
{"ge", ">=", 2},
|
| 88 |
+
{"nt", "!", 1},
|
| 89 |
+
{"aa", "&&", 2},
|
| 90 |
+
{"oo", "||", 2},
|
| 91 |
+
{"pp", "++", 1},
|
| 92 |
+
{"mm", "--", 1},
|
| 93 |
+
{"cm", ",", 2},
|
| 94 |
+
{"pm", "->*", 2},
|
| 95 |
+
{"pt", "->", 0}, // Special syntax
|
| 96 |
+
{"cl", "()", 0}, // Special syntax
|
| 97 |
+
{"ix", "[]", 2},
|
| 98 |
+
{"qu", "?", 3},
|
| 99 |
+
{"st", "sizeof", 0}, // Special syntax
|
| 100 |
+
{"sz", "sizeof", 1}, // Not a real operator name, but used in expressions.
|
| 101 |
+
{nullptr, nullptr, 0},
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
// List of builtin types from Itanium C++ ABI.
|
| 105 |
+
//
|
| 106 |
+
// Invariant: only one- or two-character type abbreviations here.
|
| 107 |
+
static const AbbrevPair kBuiltinTypeList[] = {
|
| 108 |
+
{"v", "void", 0},
|
| 109 |
+
{"w", "wchar_t", 0},
|
| 110 |
+
{"b", "bool", 0},
|
| 111 |
+
{"c", "char", 0},
|
| 112 |
+
{"a", "signed char", 0},
|
| 113 |
+
{"h", "unsigned char", 0},
|
| 114 |
+
{"s", "short", 0},
|
| 115 |
+
{"t", "unsigned short", 0},
|
| 116 |
+
{"i", "int", 0},
|
| 117 |
+
{"j", "unsigned int", 0},
|
| 118 |
+
{"l", "long", 0},
|
| 119 |
+
{"m", "unsigned long", 0},
|
| 120 |
+
{"x", "long long", 0},
|
| 121 |
+
{"y", "unsigned long long", 0},
|
| 122 |
+
{"n", "__int128", 0},
|
| 123 |
+
{"o", "unsigned __int128", 0},
|
| 124 |
+
{"f", "float", 0},
|
| 125 |
+
{"d", "double", 0},
|
| 126 |
+
{"e", "long double", 0},
|
| 127 |
+
{"g", "__float128", 0},
|
| 128 |
+
{"z", "ellipsis", 0},
|
| 129 |
+
|
| 130 |
+
{"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits)
|
| 131 |
+
{"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits)
|
| 132 |
+
{"Dc", "decltype(auto)", 0},
|
| 133 |
+
{"Da", "auto", 0},
|
| 134 |
+
{"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr)
|
| 135 |
+
{"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits)
|
| 136 |
+
{"Di", "char32_t", 0},
|
| 137 |
+
{"Du", "char8_t", 0},
|
| 138 |
+
{"Ds", "char16_t", 0},
|
| 139 |
+
{"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits)
|
| 140 |
+
{nullptr, nullptr, 0},
|
| 141 |
+
};
|
| 142 |
+
|
| 143 |
+
// List of substitutions Itanium C++ ABI.
|
| 144 |
+
static const AbbrevPair kSubstitutionList[] = {
|
| 145 |
+
{"St", "", 0},
|
| 146 |
+
{"Sa", "allocator", 0},
|
| 147 |
+
{"Sb", "basic_string", 0},
|
| 148 |
+
// std::basic_string<char, std::char_traits<char>,std::allocator<char> >
|
| 149 |
+
{"Ss", "string", 0},
|
| 150 |
+
// std::basic_istream<char, std::char_traits<char> >
|
| 151 |
+
{"Si", "istream", 0},
|
| 152 |
+
// std::basic_ostream<char, std::char_traits<char> >
|
| 153 |
+
{"So", "ostream", 0},
|
| 154 |
+
// std::basic_iostream<char, std::char_traits<char> >
|
| 155 |
+
{"Sd", "iostream", 0},
|
| 156 |
+
{nullptr, nullptr, 0},
|
| 157 |
+
};
|
| 158 |
+
|
| 159 |
+
// State needed for demangling. This struct is copied in almost every stack
|
| 160 |
+
// frame, so every byte counts.
|
| 161 |
+
typedef struct {
|
| 162 |
+
int mangled_idx; // Cursor of mangled name.
|
| 163 |
+
int out_cur_idx; // Cursor of output string.
|
| 164 |
+
int prev_name_idx; // For constructors/destructors.
|
| 165 |
+
unsigned int prev_name_length : 16; // For constructors/destructors.
|
| 166 |
+
signed int nest_level : 15; // For nested names.
|
| 167 |
+
unsigned int append : 1; // Append flag.
|
| 168 |
+
// Note: for some reason MSVC can't pack "bool append : 1" into the same int
|
| 169 |
+
// with the above two fields, so we use an int instead. Amusingly it can pack
|
| 170 |
+
// "signed bool" as expected, but relying on that to continue to be a legal
|
| 171 |
+
// type seems ill-advised (as it's illegal in at least clang).
|
| 172 |
+
} ParseState;
|
| 173 |
+
|
| 174 |
+
static_assert(sizeof(ParseState) == 4 * sizeof(int),
|
| 175 |
+
"unexpected size of ParseState");
|
| 176 |
+
|
| 177 |
+
// One-off state for demangling that's not subject to backtracking -- either
|
| 178 |
+
// constant data, data that's intentionally immune to backtracking (steps), or
|
| 179 |
+
// data that would never be changed by backtracking anyway (recursion_depth).
|
| 180 |
+
//
|
| 181 |
+
// Only one copy of this exists for each call to Demangle, so the size of this
|
| 182 |
+
// struct is nearly inconsequential.
|
| 183 |
+
typedef struct {
|
| 184 |
+
const char *mangled_begin; // Beginning of input string.
|
| 185 |
+
char *out; // Beginning of output string.
|
| 186 |
+
int out_end_idx; // One past last allowed output character.
|
| 187 |
+
int recursion_depth; // For stack exhaustion prevention.
|
| 188 |
+
int steps; // Cap how much work we'll do, regardless of depth.
|
| 189 |
+
ParseState parse_state; // Backtrackable state copied for most frames.
|
| 190 |
+
} State;
|
| 191 |
+
|
| 192 |
+
namespace {
|
| 193 |
+
// Prevent deep recursion / stack exhaustion.
|
| 194 |
+
// Also prevent unbounded handling of complex inputs.
|
| 195 |
+
class ComplexityGuard {
|
| 196 |
+
public:
|
| 197 |
+
explicit ComplexityGuard(State *state) : state_(state) {
|
| 198 |
+
++state->recursion_depth;
|
| 199 |
+
++state->steps;
|
| 200 |
+
}
|
| 201 |
+
~ComplexityGuard() { --state_->recursion_depth; }
|
| 202 |
+
|
| 203 |
+
// 256 levels of recursion seems like a reasonable upper limit on depth.
|
| 204 |
+
// 128 is not enough to demagle synthetic tests from demangle_unittest.txt:
|
| 205 |
+
// "_ZaaZZZZ..." and "_ZaaZcvZcvZ..."
|
| 206 |
+
static constexpr int kRecursionDepthLimit = 256;
|
| 207 |
+
|
| 208 |
+
// We're trying to pick a charitable upper-limit on how many parse steps are
|
| 209 |
+
// necessary to handle something that a human could actually make use of.
|
| 210 |
+
// This is mostly in place as a bound on how much work we'll do if we are
|
| 211 |
+
// asked to demangle an mangled name from an untrusted source, so it should be
|
| 212 |
+
// much larger than the largest expected symbol, but much smaller than the
|
| 213 |
+
// amount of work we can do in, e.g., a second.
|
| 214 |
+
//
|
| 215 |
+
// Some real-world symbols from an arbitrary binary started failing between
|
| 216 |
+
// 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set
|
| 217 |
+
// the limit.
|
| 218 |
+
//
|
| 219 |
+
// Spending one second on 2^17 parse steps would require each step to take
|
| 220 |
+
// 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in
|
| 221 |
+
// under a second.
|
| 222 |
+
static constexpr int kParseStepsLimit = 1 << 17;
|
| 223 |
+
|
| 224 |
+
bool IsTooComplex() const {
|
| 225 |
+
return state_->recursion_depth > kRecursionDepthLimit ||
|
| 226 |
+
state_->steps > kParseStepsLimit;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
private:
|
| 230 |
+
State *state_;
|
| 231 |
+
};
|
| 232 |
+
} // namespace
|
| 233 |
+
|
| 234 |
+
// We don't use strlen() in libc since it's not guaranteed to be async
|
| 235 |
+
// signal safe.
|
| 236 |
+
static size_t StrLen(const char *str) {
|
| 237 |
+
size_t len = 0;
|
| 238 |
+
while (*str != '\0') {
|
| 239 |
+
++str;
|
| 240 |
+
++len;
|
| 241 |
+
}
|
| 242 |
+
return len;
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
// Returns true if "str" has at least "n" characters remaining.
|
| 246 |
+
static bool AtLeastNumCharsRemaining(const char *str, size_t n) {
|
| 247 |
+
for (size_t i = 0; i < n; ++i) {
|
| 248 |
+
if (str[i] == '\0') {
|
| 249 |
+
return false;
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
return true;
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
// Returns true if "str" has "prefix" as a prefix.
|
| 256 |
+
static bool StrPrefix(const char *str, const char *prefix) {
|
| 257 |
+
size_t i = 0;
|
| 258 |
+
while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) {
|
| 259 |
+
++i;
|
| 260 |
+
}
|
| 261 |
+
return prefix[i] == '\0'; // Consumed everything in "prefix".
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
static void InitState(State* state,
|
| 265 |
+
const char* mangled,
|
| 266 |
+
char* out,
|
| 267 |
+
size_t out_size) {
|
| 268 |
+
state->mangled_begin = mangled;
|
| 269 |
+
state->out = out;
|
| 270 |
+
state->out_end_idx = static_cast<int>(out_size);
|
| 271 |
+
state->recursion_depth = 0;
|
| 272 |
+
state->steps = 0;
|
| 273 |
+
|
| 274 |
+
state->parse_state.mangled_idx = 0;
|
| 275 |
+
state->parse_state.out_cur_idx = 0;
|
| 276 |
+
state->parse_state.prev_name_idx = 0;
|
| 277 |
+
state->parse_state.prev_name_length = 0;
|
| 278 |
+
state->parse_state.nest_level = -1;
|
| 279 |
+
state->parse_state.append = true;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
static inline const char *RemainingInput(State *state) {
|
| 283 |
+
return &state->mangled_begin[state->parse_state.mangled_idx];
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
// Returns true and advances "mangled_idx" if we find "one_char_token"
|
| 287 |
+
// at "mangled_idx" position. It is assumed that "one_char_token" does
|
| 288 |
+
// not contain '\0'.
|
| 289 |
+
static bool ParseOneCharToken(State *state, const char one_char_token) {
|
| 290 |
+
ComplexityGuard guard(state);
|
| 291 |
+
if (guard.IsTooComplex()) return false;
|
| 292 |
+
if (RemainingInput(state)[0] == one_char_token) {
|
| 293 |
+
++state->parse_state.mangled_idx;
|
| 294 |
+
return true;
|
| 295 |
+
}
|
| 296 |
+
return false;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// Returns true and advances "mangled_cur" if we find "two_char_token"
|
| 300 |
+
// at "mangled_cur" position. It is assumed that "two_char_token" does
|
| 301 |
+
// not contain '\0'.
|
| 302 |
+
static bool ParseTwoCharToken(State *state, const char *two_char_token) {
|
| 303 |
+
ComplexityGuard guard(state);
|
| 304 |
+
if (guard.IsTooComplex()) return false;
|
| 305 |
+
if (RemainingInput(state)[0] == two_char_token[0] &&
|
| 306 |
+
RemainingInput(state)[1] == two_char_token[1]) {
|
| 307 |
+
state->parse_state.mangled_idx += 2;
|
| 308 |
+
return true;
|
| 309 |
+
}
|
| 310 |
+
return false;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
// Returns true and advances "mangled_cur" if we find any character in
|
| 314 |
+
// "char_class" at "mangled_cur" position.
|
| 315 |
+
static bool ParseCharClass(State *state, const char *char_class) {
|
| 316 |
+
ComplexityGuard guard(state);
|
| 317 |
+
if (guard.IsTooComplex()) return false;
|
| 318 |
+
if (RemainingInput(state)[0] == '\0') {
|
| 319 |
+
return false;
|
| 320 |
+
}
|
| 321 |
+
const char *p = char_class;
|
| 322 |
+
for (; *p != '\0'; ++p) {
|
| 323 |
+
if (RemainingInput(state)[0] == *p) {
|
| 324 |
+
++state->parse_state.mangled_idx;
|
| 325 |
+
return true;
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
return false;
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
static bool ParseDigit(State *state, int *digit) {
|
| 332 |
+
char c = RemainingInput(state)[0];
|
| 333 |
+
if (ParseCharClass(state, "0123456789")) {
|
| 334 |
+
if (digit != nullptr) {
|
| 335 |
+
*digit = c - '0';
|
| 336 |
+
}
|
| 337 |
+
return true;
|
| 338 |
+
}
|
| 339 |
+
return false;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
// This function is used for handling an optional non-terminal.
|
| 343 |
+
static bool Optional(bool /*status*/) { return true; }
|
| 344 |
+
|
| 345 |
+
// This function is used for handling <non-terminal>+ syntax.
|
| 346 |
+
typedef bool (*ParseFunc)(State *);
|
| 347 |
+
static bool OneOrMore(ParseFunc parse_func, State *state) {
|
| 348 |
+
if (parse_func(state)) {
|
| 349 |
+
while (parse_func(state)) {
|
| 350 |
+
}
|
| 351 |
+
return true;
|
| 352 |
+
}
|
| 353 |
+
return false;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
// This function is used for handling <non-terminal>* syntax. The function
|
| 357 |
+
// always returns true and must be followed by a termination token or a
|
| 358 |
+
// terminating sequence not handled by parse_func (e.g.
|
| 359 |
+
// ParseOneCharToken(state, 'E')).
|
| 360 |
+
static bool ZeroOrMore(ParseFunc parse_func, State *state) {
|
| 361 |
+
while (parse_func(state)) {
|
| 362 |
+
}
|
| 363 |
+
return true;
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is
|
| 367 |
+
// set to out_end_idx+1. The output string is ensured to
|
| 368 |
+
// always terminate with '\0' as long as there is no overflow.
|
| 369 |
+
static void Append(State *state, const char *const str, const size_t length) {
|
| 370 |
+
for (size_t i = 0; i < length; ++i) {
|
| 371 |
+
if (state->parse_state.out_cur_idx + 1 <
|
| 372 |
+
state->out_end_idx) { // +1 for '\0'
|
| 373 |
+
state->out[state->parse_state.out_cur_idx++] = str[i];
|
| 374 |
+
} else {
|
| 375 |
+
// signal overflow
|
| 376 |
+
state->parse_state.out_cur_idx = state->out_end_idx + 1;
|
| 377 |
+
break;
|
| 378 |
+
}
|
| 379 |
+
}
|
| 380 |
+
if (state->parse_state.out_cur_idx < state->out_end_idx) {
|
| 381 |
+
state->out[state->parse_state.out_cur_idx] =
|
| 382 |
+
'\0'; // Terminate it with '\0'
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
// We don't use equivalents in libc to avoid locale issues.
|
| 387 |
+
static bool IsLower(char c) { return c >= 'a' && c <= 'z'; }
|
| 388 |
+
|
| 389 |
+
static bool IsAlpha(char c) {
|
| 390 |
+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
static bool IsDigit(char c) { return c >= '0' && c <= '9'; }
|
| 394 |
+
|
| 395 |
+
// Returns true if "str" is a function clone suffix. These suffixes are used
|
| 396 |
+
// by GCC 4.5.x and later versions (and our locally-modified version of GCC
|
| 397 |
+
// 4.4.x) to indicate functions which have been cloned during optimization.
|
| 398 |
+
// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix.
|
| 399 |
+
// Additionally, '_' is allowed along with the alphanumeric sequence.
|
| 400 |
+
static bool IsFunctionCloneSuffix(const char *str) {
|
| 401 |
+
size_t i = 0;
|
| 402 |
+
while (str[i] != '\0') {
|
| 403 |
+
bool parsed = false;
|
| 404 |
+
// Consume a single [.<alpha> | _]*[.<digit>]* sequence.
|
| 405 |
+
if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) {
|
| 406 |
+
parsed = true;
|
| 407 |
+
i += 2;
|
| 408 |
+
while (IsAlpha(str[i]) || str[i] == '_') {
|
| 409 |
+
++i;
|
| 410 |
+
}
|
| 411 |
+
}
|
| 412 |
+
if (str[i] == '.' && IsDigit(str[i + 1])) {
|
| 413 |
+
parsed = true;
|
| 414 |
+
i += 2;
|
| 415 |
+
while (IsDigit(str[i])) {
|
| 416 |
+
++i;
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
+
if (!parsed)
|
| 420 |
+
return false;
|
| 421 |
+
}
|
| 422 |
+
return true; // Consumed everything in "str".
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
static bool EndsWith(State *state, const char chr) {
|
| 426 |
+
return state->parse_state.out_cur_idx > 0 &&
|
| 427 |
+
state->parse_state.out_cur_idx < state->out_end_idx &&
|
| 428 |
+
chr == state->out[state->parse_state.out_cur_idx - 1];
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
// Append "str" with some tweaks, iff "append" state is true.
|
| 432 |
+
static void MaybeAppendWithLength(State *state, const char *const str,
|
| 433 |
+
const size_t length) {
|
| 434 |
+
if (state->parse_state.append && length > 0) {
|
| 435 |
+
// Append a space if the output buffer ends with '<' and "str"
|
| 436 |
+
// starts with '<' to avoid <<<.
|
| 437 |
+
if (str[0] == '<' && EndsWith(state, '<')) {
|
| 438 |
+
Append(state, " ", 1);
|
| 439 |
+
}
|
| 440 |
+
// Remember the last identifier name for ctors/dtors,
|
| 441 |
+
// but only if we haven't yet overflown the buffer.
|
| 442 |
+
if (state->parse_state.out_cur_idx < state->out_end_idx &&
|
| 443 |
+
(IsAlpha(str[0]) || str[0] == '_')) {
|
| 444 |
+
state->parse_state.prev_name_idx = state->parse_state.out_cur_idx;
|
| 445 |
+
state->parse_state.prev_name_length = static_cast<unsigned int>(length);
|
| 446 |
+
}
|
| 447 |
+
Append(state, str, length);
|
| 448 |
+
}
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
// Appends a positive decimal number to the output if appending is enabled.
|
| 452 |
+
static bool MaybeAppendDecimal(State *state, int val) {
|
| 453 |
+
// Max {32-64}-bit unsigned int is 20 digits.
|
| 454 |
+
constexpr size_t kMaxLength = 20;
|
| 455 |
+
char buf[kMaxLength];
|
| 456 |
+
|
| 457 |
+
// We can't use itoa or sprintf as neither is specified to be
|
| 458 |
+
// async-signal-safe.
|
| 459 |
+
if (state->parse_state.append) {
|
| 460 |
+
// We can't have a one-before-the-beginning pointer, so instead start with
|
| 461 |
+
// one-past-the-end and manipulate one character before the pointer.
|
| 462 |
+
char *p = &buf[kMaxLength];
|
| 463 |
+
do { // val=0 is the only input that should write a leading zero digit.
|
| 464 |
+
*--p = static_cast<char>((val % 10) + '0');
|
| 465 |
+
val /= 10;
|
| 466 |
+
} while (p > buf && val != 0);
|
| 467 |
+
|
| 468 |
+
// 'p' landed on the last character we set. How convenient.
|
| 469 |
+
Append(state, p, kMaxLength - static_cast<size_t>(p - buf));
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
return true;
|
| 473 |
+
}
|
| 474 |
+
|
| 475 |
+
// A convenient wrapper around MaybeAppendWithLength().
|
| 476 |
+
// Returns true so that it can be placed in "if" conditions.
|
| 477 |
+
static bool MaybeAppend(State *state, const char *const str) {
|
| 478 |
+
if (state->parse_state.append) {
|
| 479 |
+
size_t length = StrLen(str);
|
| 480 |
+
MaybeAppendWithLength(state, str, length);
|
| 481 |
+
}
|
| 482 |
+
return true;
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
+
// This function is used for handling nested names.
|
| 486 |
+
static bool EnterNestedName(State *state) {
|
| 487 |
+
state->parse_state.nest_level = 0;
|
| 488 |
+
return true;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
// This function is used for handling nested names.
|
| 492 |
+
static bool LeaveNestedName(State *state, int16_t prev_value) {
|
| 493 |
+
state->parse_state.nest_level = prev_value;
|
| 494 |
+
return true;
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
// Disable the append mode not to print function parameters, etc.
|
| 498 |
+
static bool DisableAppend(State *state) {
|
| 499 |
+
state->parse_state.append = false;
|
| 500 |
+
return true;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
// Restore the append mode to the previous state.
|
| 504 |
+
static bool RestoreAppend(State *state, bool prev_value) {
|
| 505 |
+
state->parse_state.append = prev_value;
|
| 506 |
+
return true;
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
// Increase the nest level for nested names.
|
| 510 |
+
static void MaybeIncreaseNestLevel(State *state) {
|
| 511 |
+
if (state->parse_state.nest_level > -1) {
|
| 512 |
+
++state->parse_state.nest_level;
|
| 513 |
+
}
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
// Appends :: for nested names if necessary.
|
| 517 |
+
static void MaybeAppendSeparator(State *state) {
|
| 518 |
+
if (state->parse_state.nest_level >= 1) {
|
| 519 |
+
MaybeAppend(state, "::");
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
// Cancel the last separator if necessary.
|
| 524 |
+
static void MaybeCancelLastSeparator(State *state) {
|
| 525 |
+
if (state->parse_state.nest_level >= 1 && state->parse_state.append &&
|
| 526 |
+
state->parse_state.out_cur_idx >= 2) {
|
| 527 |
+
state->parse_state.out_cur_idx -= 2;
|
| 528 |
+
state->out[state->parse_state.out_cur_idx] = '\0';
|
| 529 |
+
}
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
// Returns true if the identifier of the given length pointed to by
|
| 533 |
+
// "mangled_cur" is anonymous namespace.
|
| 534 |
+
static bool IdentifierIsAnonymousNamespace(State *state, size_t length) {
|
| 535 |
+
// Returns true if "anon_prefix" is a proper prefix of "mangled_cur".
|
| 536 |
+
static const char anon_prefix[] = "_GLOBAL__N_";
|
| 537 |
+
return (length > (sizeof(anon_prefix) - 1) &&
|
| 538 |
+
StrPrefix(RemainingInput(state), anon_prefix));
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
// Forward declarations of our parsing functions.
|
| 542 |
+
static bool ParseMangledName(State *state);
|
| 543 |
+
static bool ParseEncoding(State *state);
|
| 544 |
+
static bool ParseName(State *state);
|
| 545 |
+
static bool ParseUnscopedName(State *state);
|
| 546 |
+
static bool ParseNestedName(State *state);
|
| 547 |
+
static bool ParsePrefix(State *state);
|
| 548 |
+
static bool ParseUnqualifiedName(State *state);
|
| 549 |
+
static bool ParseSourceName(State *state);
|
| 550 |
+
static bool ParseLocalSourceName(State *state);
|
| 551 |
+
static bool ParseUnnamedTypeName(State *state);
|
| 552 |
+
static bool ParseNumber(State *state, int *number_out);
|
| 553 |
+
static bool ParseFloatNumber(State *state);
|
| 554 |
+
static bool ParseSeqId(State *state);
|
| 555 |
+
static bool ParseIdentifier(State *state, size_t length);
|
| 556 |
+
static bool ParseOperatorName(State *state, int *arity);
|
| 557 |
+
static bool ParseSpecialName(State *state);
|
| 558 |
+
static bool ParseCallOffset(State *state);
|
| 559 |
+
static bool ParseNVOffset(State *state);
|
| 560 |
+
static bool ParseVOffset(State *state);
|
| 561 |
+
static bool ParseAbiTags(State *state);
|
| 562 |
+
static bool ParseCtorDtorName(State *state);
|
| 563 |
+
static bool ParseDecltype(State *state);
|
| 564 |
+
static bool ParseType(State *state);
|
| 565 |
+
static bool ParseCVQualifiers(State *state);
|
| 566 |
+
static bool ParseBuiltinType(State *state);
|
| 567 |
+
static bool ParseFunctionType(State *state);
|
| 568 |
+
static bool ParseBareFunctionType(State *state);
|
| 569 |
+
static bool ParseClassEnumType(State *state);
|
| 570 |
+
static bool ParseArrayType(State *state);
|
| 571 |
+
static bool ParsePointerToMemberType(State *state);
|
| 572 |
+
static bool ParseTemplateParam(State *state);
|
| 573 |
+
static bool ParseTemplateTemplateParam(State *state);
|
| 574 |
+
static bool ParseTemplateArgs(State *state);
|
| 575 |
+
static bool ParseTemplateArg(State *state);
|
| 576 |
+
static bool ParseBaseUnresolvedName(State *state);
|
| 577 |
+
static bool ParseUnresolvedName(State *state);
|
| 578 |
+
static bool ParseExpression(State *state);
|
| 579 |
+
static bool ParseExprPrimary(State *state);
|
| 580 |
+
static bool ParseExprCastValue(State *state);
|
| 581 |
+
static bool ParseLocalName(State *state);
|
| 582 |
+
static bool ParseLocalNameSuffix(State *state);
|
| 583 |
+
static bool ParseDiscriminator(State *state);
|
| 584 |
+
static bool ParseSubstitution(State *state, bool accept_std);
|
| 585 |
+
|
| 586 |
+
// Implementation note: the following code is a straightforward
|
| 587 |
+
// translation of the Itanium C++ ABI defined in BNF with a couple of
|
| 588 |
+
// exceptions.
|
| 589 |
+
//
|
| 590 |
+
// - Support GNU extensions not defined in the Itanium C++ ABI
|
| 591 |
+
// - <prefix> and <template-prefix> are combined to avoid infinite loop
|
| 592 |
+
// - Reorder patterns to shorten the code
|
| 593 |
+
// - Reorder patterns to give greedier functions precedence
|
| 594 |
+
// We'll mark "Less greedy than" for these cases in the code
|
| 595 |
+
//
|
| 596 |
+
// Each parsing function changes the parse state and returns true on
|
| 597 |
+
// success, or returns false and doesn't change the parse state (note:
|
| 598 |
+
// the parse-steps counter increases regardless of success or failure).
|
| 599 |
+
// To ensure that the parse state isn't changed in the latter case, we
|
| 600 |
+
// save the original state before we call multiple parsing functions
|
| 601 |
+
// consecutively with &&, and restore it if unsuccessful. See
|
| 602 |
+
// ParseEncoding() as an example of this convention. We follow the
|
| 603 |
+
// convention throughout the code.
|
| 604 |
+
//
|
| 605 |
+
// Originally we tried to do demangling without following the full ABI
|
| 606 |
+
// syntax but it turned out we needed to follow the full syntax to
|
| 607 |
+
// parse complicated cases like nested template arguments. Note that
|
| 608 |
+
// implementing a full-fledged demangler isn't trivial (libiberty's
|
| 609 |
+
// cp-demangle.c has +4300 lines).
|
| 610 |
+
//
|
| 611 |
+
// Note that (foo) in <(foo) ...> is a modifier to be ignored.
|
| 612 |
+
//
|
| 613 |
+
// Reference:
|
| 614 |
+
// - Itanium C++ ABI
|
| 615 |
+
// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling>
|
| 616 |
+
|
| 617 |
+
// <mangled-name> ::= _Z <encoding>
|
| 618 |
+
static bool ParseMangledName(State *state) {
|
| 619 |
+
ComplexityGuard guard(state);
|
| 620 |
+
if (guard.IsTooComplex()) return false;
|
| 621 |
+
return ParseTwoCharToken(state, "_Z") && ParseEncoding(state);
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
// <encoding> ::= <(function) name> <bare-function-type>
|
| 625 |
+
// ::= <(data) name>
|
| 626 |
+
// ::= <special-name>
|
| 627 |
+
static bool ParseEncoding(State *state) {
|
| 628 |
+
ComplexityGuard guard(state);
|
| 629 |
+
if (guard.IsTooComplex()) return false;
|
| 630 |
+
// Implementing the first two productions together as <name>
|
| 631 |
+
// [<bare-function-type>] avoids exponential blowup of backtracking.
|
| 632 |
+
//
|
| 633 |
+
// Since Optional(...) can't fail, there's no need to copy the state for
|
| 634 |
+
// backtracking.
|
| 635 |
+
if (ParseName(state) && Optional(ParseBareFunctionType(state))) {
|
| 636 |
+
return true;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
if (ParseSpecialName(state)) {
|
| 640 |
+
return true;
|
| 641 |
+
}
|
| 642 |
+
return false;
|
| 643 |
+
}
|
| 644 |
+
|
| 645 |
+
// <name> ::= <nested-name>
|
| 646 |
+
// ::= <unscoped-template-name> <template-args>
|
| 647 |
+
// ::= <unscoped-name>
|
| 648 |
+
// ::= <local-name>
|
| 649 |
+
static bool ParseName(State *state) {
|
| 650 |
+
ComplexityGuard guard(state);
|
| 651 |
+
if (guard.IsTooComplex()) return false;
|
| 652 |
+
if (ParseNestedName(state) || ParseLocalName(state)) {
|
| 653 |
+
return true;
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
// We reorganize the productions to avoid re-parsing unscoped names.
|
| 657 |
+
// - Inline <unscoped-template-name> productions:
|
| 658 |
+
// <name> ::= <substitution> <template-args>
|
| 659 |
+
// ::= <unscoped-name> <template-args>
|
| 660 |
+
// ::= <unscoped-name>
|
| 661 |
+
// - Merge the two productions that start with unscoped-name:
|
| 662 |
+
// <name> ::= <unscoped-name> [<template-args>]
|
| 663 |
+
|
| 664 |
+
ParseState copy = state->parse_state;
|
| 665 |
+
// "std<...>" isn't a valid name.
|
| 666 |
+
if (ParseSubstitution(state, /*accept_std=*/false) &&
|
| 667 |
+
ParseTemplateArgs(state)) {
|
| 668 |
+
return true;
|
| 669 |
+
}
|
| 670 |
+
state->parse_state = copy;
|
| 671 |
+
|
| 672 |
+
// Note there's no need to restore state after this since only the first
|
| 673 |
+
// subparser can fail.
|
| 674 |
+
return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state));
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
// <unscoped-name> ::= <unqualified-name>
|
| 678 |
+
// ::= St <unqualified-name>
|
| 679 |
+
static bool ParseUnscopedName(State *state) {
|
| 680 |
+
ComplexityGuard guard(state);
|
| 681 |
+
if (guard.IsTooComplex()) return false;
|
| 682 |
+
if (ParseUnqualifiedName(state)) {
|
| 683 |
+
return true;
|
| 684 |
+
}
|
| 685 |
+
|
| 686 |
+
ParseState copy = state->parse_state;
|
| 687 |
+
if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") &&
|
| 688 |
+
ParseUnqualifiedName(state)) {
|
| 689 |
+
return true;
|
| 690 |
+
}
|
| 691 |
+
state->parse_state = copy;
|
| 692 |
+
return false;
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
// <ref-qualifer> ::= R // lvalue method reference qualifier
|
| 696 |
+
// ::= O // rvalue method reference qualifier
|
| 697 |
+
static inline bool ParseRefQualifier(State *state) {
|
| 698 |
+
return ParseCharClass(state, "OR");
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
+
// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix>
|
| 702 |
+
// <unqualified-name> E
|
| 703 |
+
// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix>
|
| 704 |
+
// <template-args> E
|
| 705 |
+
static bool ParseNestedName(State *state) {
|
| 706 |
+
ComplexityGuard guard(state);
|
| 707 |
+
if (guard.IsTooComplex()) return false;
|
| 708 |
+
ParseState copy = state->parse_state;
|
| 709 |
+
if (ParseOneCharToken(state, 'N') && EnterNestedName(state) &&
|
| 710 |
+
Optional(ParseCVQualifiers(state)) &&
|
| 711 |
+
Optional(ParseRefQualifier(state)) && ParsePrefix(state) &&
|
| 712 |
+
LeaveNestedName(state, copy.nest_level) &&
|
| 713 |
+
ParseOneCharToken(state, 'E')) {
|
| 714 |
+
return true;
|
| 715 |
+
}
|
| 716 |
+
state->parse_state = copy;
|
| 717 |
+
return false;
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
// This part is tricky. If we literally translate them to code, we'll
|
| 721 |
+
// end up infinite loop. Hence we merge them to avoid the case.
|
| 722 |
+
//
|
| 723 |
+
// <prefix> ::= <prefix> <unqualified-name>
|
| 724 |
+
// ::= <template-prefix> <template-args>
|
| 725 |
+
// ::= <template-param>
|
| 726 |
+
// ::= <substitution>
|
| 727 |
+
// ::= # empty
|
| 728 |
+
// <template-prefix> ::= <prefix> <(template) unqualified-name>
|
| 729 |
+
// ::= <template-param>
|
| 730 |
+
// ::= <substitution>
|
| 731 |
+
static bool ParsePrefix(State *state) {
|
| 732 |
+
ComplexityGuard guard(state);
|
| 733 |
+
if (guard.IsTooComplex()) return false;
|
| 734 |
+
bool has_something = false;
|
| 735 |
+
while (true) {
|
| 736 |
+
MaybeAppendSeparator(state);
|
| 737 |
+
if (ParseTemplateParam(state) ||
|
| 738 |
+
ParseSubstitution(state, /*accept_std=*/true) ||
|
| 739 |
+
ParseUnscopedName(state) ||
|
| 740 |
+
(ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) {
|
| 741 |
+
has_something = true;
|
| 742 |
+
MaybeIncreaseNestLevel(state);
|
| 743 |
+
continue;
|
| 744 |
+
}
|
| 745 |
+
MaybeCancelLastSeparator(state);
|
| 746 |
+
if (has_something && ParseTemplateArgs(state)) {
|
| 747 |
+
return ParsePrefix(state);
|
| 748 |
+
} else {
|
| 749 |
+
break;
|
| 750 |
+
}
|
| 751 |
+
}
|
| 752 |
+
return true;
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
// <unqualified-name> ::= <operator-name> [<abi-tags>]
|
| 756 |
+
// ::= <ctor-dtor-name> [<abi-tags>]
|
| 757 |
+
// ::= <source-name> [<abi-tags>]
|
| 758 |
+
// ::= <local-source-name> [<abi-tags>]
|
| 759 |
+
// ::= <unnamed-type-name> [<abi-tags>]
|
| 760 |
+
//
|
| 761 |
+
// <local-source-name> is a GCC extension; see below.
|
| 762 |
+
static bool ParseUnqualifiedName(State *state) {
|
| 763 |
+
ComplexityGuard guard(state);
|
| 764 |
+
if (guard.IsTooComplex()) return false;
|
| 765 |
+
if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) ||
|
| 766 |
+
ParseSourceName(state) || ParseLocalSourceName(state) ||
|
| 767 |
+
ParseUnnamedTypeName(state)) {
|
| 768 |
+
return ParseAbiTags(state);
|
| 769 |
+
}
|
| 770 |
+
return false;
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
// <abi-tags> ::= <abi-tag> [<abi-tags>]
|
| 774 |
+
// <abi-tag> ::= B <source-name>
|
| 775 |
+
static bool ParseAbiTags(State *state) {
|
| 776 |
+
ComplexityGuard guard(state);
|
| 777 |
+
if (guard.IsTooComplex()) return false;
|
| 778 |
+
|
| 779 |
+
while (ParseOneCharToken(state, 'B')) {
|
| 780 |
+
ParseState copy = state->parse_state;
|
| 781 |
+
MaybeAppend(state, "[abi:");
|
| 782 |
+
|
| 783 |
+
if (!ParseSourceName(state)) {
|
| 784 |
+
state->parse_state = copy;
|
| 785 |
+
return false;
|
| 786 |
+
}
|
| 787 |
+
MaybeAppend(state, "]");
|
| 788 |
+
}
|
| 789 |
+
|
| 790 |
+
return true;
|
| 791 |
+
}
|
| 792 |
+
|
| 793 |
+
// <source-name> ::= <positive length number> <identifier>
|
| 794 |
+
static bool ParseSourceName(State *state) {
|
| 795 |
+
ComplexityGuard guard(state);
|
| 796 |
+
if (guard.IsTooComplex()) return false;
|
| 797 |
+
ParseState copy = state->parse_state;
|
| 798 |
+
int length = -1;
|
| 799 |
+
if (ParseNumber(state, &length) &&
|
| 800 |
+
ParseIdentifier(state, static_cast<size_t>(length))) {
|
| 801 |
+
return true;
|
| 802 |
+
}
|
| 803 |
+
state->parse_state = copy;
|
| 804 |
+
return false;
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
// <local-source-name> ::= L <source-name> [<discriminator>]
|
| 808 |
+
//
|
| 809 |
+
// References:
|
| 810 |
+
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
|
| 811 |
+
// https://gcc.gnu.org/viewcvs?view=rev&revision=124467
|
| 812 |
+
static bool ParseLocalSourceName(State *state) {
|
| 813 |
+
ComplexityGuard guard(state);
|
| 814 |
+
if (guard.IsTooComplex()) return false;
|
| 815 |
+
ParseState copy = state->parse_state;
|
| 816 |
+
if (ParseOneCharToken(state, 'L') && ParseSourceName(state) &&
|
| 817 |
+
Optional(ParseDiscriminator(state))) {
|
| 818 |
+
return true;
|
| 819 |
+
}
|
| 820 |
+
state->parse_state = copy;
|
| 821 |
+
return false;
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _
|
| 825 |
+
// ::= <closure-type-name>
|
| 826 |
+
// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _
|
| 827 |
+
// <lambda-sig> ::= <(parameter) type>+
|
| 828 |
+
static bool ParseUnnamedTypeName(State *state) {
|
| 829 |
+
ComplexityGuard guard(state);
|
| 830 |
+
if (guard.IsTooComplex()) return false;
|
| 831 |
+
ParseState copy = state->parse_state;
|
| 832 |
+
// Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }.
|
| 833 |
+
// Optionally parse the encoded value into 'which' and add 2 to get the index.
|
| 834 |
+
int which = -1;
|
| 835 |
+
|
| 836 |
+
// Unnamed type local to function or class.
|
| 837 |
+
if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) &&
|
| 838 |
+
which <= std::numeric_limits<int>::max() - 2 && // Don't overflow.
|
| 839 |
+
ParseOneCharToken(state, '_')) {
|
| 840 |
+
MaybeAppend(state, "{unnamed type#");
|
| 841 |
+
MaybeAppendDecimal(state, 2 + which);
|
| 842 |
+
MaybeAppend(state, "}");
|
| 843 |
+
return true;
|
| 844 |
+
}
|
| 845 |
+
state->parse_state = copy;
|
| 846 |
+
|
| 847 |
+
// Closure type.
|
| 848 |
+
which = -1;
|
| 849 |
+
if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) &&
|
| 850 |
+
OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) &&
|
| 851 |
+
ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) &&
|
| 852 |
+
which <= std::numeric_limits<int>::max() - 2 && // Don't overflow.
|
| 853 |
+
ParseOneCharToken(state, '_')) {
|
| 854 |
+
MaybeAppend(state, "{lambda()#");
|
| 855 |
+
MaybeAppendDecimal(state, 2 + which);
|
| 856 |
+
MaybeAppend(state, "}");
|
| 857 |
+
return true;
|
| 858 |
+
}
|
| 859 |
+
state->parse_state = copy;
|
| 860 |
+
|
| 861 |
+
return false;
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
// <number> ::= [n] <non-negative decimal integer>
|
| 865 |
+
// If "number_out" is non-null, then *number_out is set to the value of the
|
| 866 |
+
// parsed number on success.
|
| 867 |
+
static bool ParseNumber(State *state, int *number_out) {
|
| 868 |
+
ComplexityGuard guard(state);
|
| 869 |
+
if (guard.IsTooComplex()) return false;
|
| 870 |
+
bool negative = false;
|
| 871 |
+
if (ParseOneCharToken(state, 'n')) {
|
| 872 |
+
negative = true;
|
| 873 |
+
}
|
| 874 |
+
const char *p = RemainingInput(state);
|
| 875 |
+
uint64_t number = 0;
|
| 876 |
+
for (; *p != '\0'; ++p) {
|
| 877 |
+
if (IsDigit(*p)) {
|
| 878 |
+
number = number * 10 + static_cast<uint64_t>(*p - '0');
|
| 879 |
+
} else {
|
| 880 |
+
break;
|
| 881 |
+
}
|
| 882 |
+
}
|
| 883 |
+
// Apply the sign with uint64_t arithmetic so overflows aren't UB. Gives
|
| 884 |
+
// "incorrect" results for out-of-range inputs, but negative values only
|
| 885 |
+
// appear for literals, which aren't printed.
|
| 886 |
+
if (negative) {
|
| 887 |
+
number = ~number + 1;
|
| 888 |
+
}
|
| 889 |
+
if (p != RemainingInput(state)) { // Conversion succeeded.
|
| 890 |
+
state->parse_state.mangled_idx += p - RemainingInput(state);
|
| 891 |
+
if (number_out != nullptr) {
|
| 892 |
+
// Note: possibly truncate "number".
|
| 893 |
+
*number_out = static_cast<int>(number);
|
| 894 |
+
}
|
| 895 |
+
return true;
|
| 896 |
+
}
|
| 897 |
+
return false;
|
| 898 |
+
}
|
| 899 |
+
|
| 900 |
+
// Floating-point literals are encoded using a fixed-length lowercase
|
| 901 |
+
// hexadecimal string.
|
| 902 |
+
static bool ParseFloatNumber(State *state) {
|
| 903 |
+
ComplexityGuard guard(state);
|
| 904 |
+
if (guard.IsTooComplex()) return false;
|
| 905 |
+
const char *p = RemainingInput(state);
|
| 906 |
+
for (; *p != '\0'; ++p) {
|
| 907 |
+
if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) {
|
| 908 |
+
break;
|
| 909 |
+
}
|
| 910 |
+
}
|
| 911 |
+
if (p != RemainingInput(state)) { // Conversion succeeded.
|
| 912 |
+
state->parse_state.mangled_idx += p - RemainingInput(state);
|
| 913 |
+
return true;
|
| 914 |
+
}
|
| 915 |
+
return false;
|
| 916 |
+
}
|
| 917 |
+
|
| 918 |
+
// The <seq-id> is a sequence number in base 36,
|
| 919 |
+
// using digits and upper case letters
|
| 920 |
+
static bool ParseSeqId(State *state) {
|
| 921 |
+
ComplexityGuard guard(state);
|
| 922 |
+
if (guard.IsTooComplex()) return false;
|
| 923 |
+
const char *p = RemainingInput(state);
|
| 924 |
+
for (; *p != '\0'; ++p) {
|
| 925 |
+
if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) {
|
| 926 |
+
break;
|
| 927 |
+
}
|
| 928 |
+
}
|
| 929 |
+
if (p != RemainingInput(state)) { // Conversion succeeded.
|
| 930 |
+
state->parse_state.mangled_idx += p - RemainingInput(state);
|
| 931 |
+
return true;
|
| 932 |
+
}
|
| 933 |
+
return false;
|
| 934 |
+
}
|
| 935 |
+
|
| 936 |
+
// <identifier> ::= <unqualified source code identifier> (of given length)
|
| 937 |
+
static bool ParseIdentifier(State *state, size_t length) {
|
| 938 |
+
ComplexityGuard guard(state);
|
| 939 |
+
if (guard.IsTooComplex()) return false;
|
| 940 |
+
if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) {
|
| 941 |
+
return false;
|
| 942 |
+
}
|
| 943 |
+
if (IdentifierIsAnonymousNamespace(state, length)) {
|
| 944 |
+
MaybeAppend(state, "(anonymous namespace)");
|
| 945 |
+
} else {
|
| 946 |
+
MaybeAppendWithLength(state, RemainingInput(state), length);
|
| 947 |
+
}
|
| 948 |
+
state->parse_state.mangled_idx += length;
|
| 949 |
+
return true;
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
// <operator-name> ::= nw, and other two letters cases
|
| 953 |
+
// ::= cv <type> # (cast)
|
| 954 |
+
// ::= v <digit> <source-name> # vendor extended operator
|
| 955 |
+
static bool ParseOperatorName(State *state, int *arity) {
|
| 956 |
+
ComplexityGuard guard(state);
|
| 957 |
+
if (guard.IsTooComplex()) return false;
|
| 958 |
+
if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) {
|
| 959 |
+
return false;
|
| 960 |
+
}
|
| 961 |
+
// First check with "cv" (cast) case.
|
| 962 |
+
ParseState copy = state->parse_state;
|
| 963 |
+
if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") &&
|
| 964 |
+
EnterNestedName(state) && ParseType(state) &&
|
| 965 |
+
LeaveNestedName(state, copy.nest_level)) {
|
| 966 |
+
if (arity != nullptr) {
|
| 967 |
+
*arity = 1;
|
| 968 |
+
}
|
| 969 |
+
return true;
|
| 970 |
+
}
|
| 971 |
+
state->parse_state = copy;
|
| 972 |
+
|
| 973 |
+
// Then vendor extended operators.
|
| 974 |
+
if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) &&
|
| 975 |
+
ParseSourceName(state)) {
|
| 976 |
+
return true;
|
| 977 |
+
}
|
| 978 |
+
state->parse_state = copy;
|
| 979 |
+
|
| 980 |
+
// Other operator names should start with a lower alphabet followed
|
| 981 |
+
// by a lower/upper alphabet.
|
| 982 |
+
if (!(IsLower(RemainingInput(state)[0]) &&
|
| 983 |
+
IsAlpha(RemainingInput(state)[1]))) {
|
| 984 |
+
return false;
|
| 985 |
+
}
|
| 986 |
+
// We may want to perform a binary search if we really need speed.
|
| 987 |
+
const AbbrevPair *p;
|
| 988 |
+
for (p = kOperatorList; p->abbrev != nullptr; ++p) {
|
| 989 |
+
if (RemainingInput(state)[0] == p->abbrev[0] &&
|
| 990 |
+
RemainingInput(state)[1] == p->abbrev[1]) {
|
| 991 |
+
if (arity != nullptr) {
|
| 992 |
+
*arity = p->arity;
|
| 993 |
+
}
|
| 994 |
+
MaybeAppend(state, "operator");
|
| 995 |
+
if (IsLower(*p->real_name)) { // new, delete, etc.
|
| 996 |
+
MaybeAppend(state, " ");
|
| 997 |
+
}
|
| 998 |
+
MaybeAppend(state, p->real_name);
|
| 999 |
+
state->parse_state.mangled_idx += 2;
|
| 1000 |
+
return true;
|
| 1001 |
+
}
|
| 1002 |
+
}
|
| 1003 |
+
return false;
|
| 1004 |
+
}
|
| 1005 |
+
|
| 1006 |
+
// <special-name> ::= TV <type>
|
| 1007 |
+
// ::= TT <type>
|
| 1008 |
+
// ::= TI <type>
|
| 1009 |
+
// ::= TS <type>
|
| 1010 |
+
// ::= TH <type> # thread-local
|
| 1011 |
+
// ::= Tc <call-offset> <call-offset> <(base) encoding>
|
| 1012 |
+
// ::= GV <(object) name>
|
| 1013 |
+
// ::= T <call-offset> <(base) encoding>
|
| 1014 |
+
// G++ extensions:
|
| 1015 |
+
// ::= TC <type> <(offset) number> _ <(base) type>
|
| 1016 |
+
// ::= TF <type>
|
| 1017 |
+
// ::= TJ <type>
|
| 1018 |
+
// ::= GR <name>
|
| 1019 |
+
// ::= GA <encoding>
|
| 1020 |
+
// ::= Th <call-offset> <(base) encoding>
|
| 1021 |
+
// ::= Tv <call-offset> <(base) encoding>
|
| 1022 |
+
//
|
| 1023 |
+
// Note: we don't care much about them since they don't appear in
|
| 1024 |
+
// stack traces. The are special data.
|
| 1025 |
+
static bool ParseSpecialName(State *state) {
|
| 1026 |
+
ComplexityGuard guard(state);
|
| 1027 |
+
if (guard.IsTooComplex()) return false;
|
| 1028 |
+
ParseState copy = state->parse_state;
|
| 1029 |
+
if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") &&
|
| 1030 |
+
ParseType(state)) {
|
| 1031 |
+
return true;
|
| 1032 |
+
}
|
| 1033 |
+
state->parse_state = copy;
|
| 1034 |
+
|
| 1035 |
+
if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) &&
|
| 1036 |
+
ParseCallOffset(state) && ParseEncoding(state)) {
|
| 1037 |
+
return true;
|
| 1038 |
+
}
|
| 1039 |
+
state->parse_state = copy;
|
| 1040 |
+
|
| 1041 |
+
if (ParseTwoCharToken(state, "GV") && ParseName(state)) {
|
| 1042 |
+
return true;
|
| 1043 |
+
}
|
| 1044 |
+
state->parse_state = copy;
|
| 1045 |
+
|
| 1046 |
+
if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) &&
|
| 1047 |
+
ParseEncoding(state)) {
|
| 1048 |
+
return true;
|
| 1049 |
+
}
|
| 1050 |
+
state->parse_state = copy;
|
| 1051 |
+
|
| 1052 |
+
// G++ extensions
|
| 1053 |
+
if (ParseTwoCharToken(state, "TC") && ParseType(state) &&
|
| 1054 |
+
ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') &&
|
| 1055 |
+
DisableAppend(state) && ParseType(state)) {
|
| 1056 |
+
RestoreAppend(state, copy.append);
|
| 1057 |
+
return true;
|
| 1058 |
+
}
|
| 1059 |
+
state->parse_state = copy;
|
| 1060 |
+
|
| 1061 |
+
if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") &&
|
| 1062 |
+
ParseType(state)) {
|
| 1063 |
+
return true;
|
| 1064 |
+
}
|
| 1065 |
+
state->parse_state = copy;
|
| 1066 |
+
|
| 1067 |
+
if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
|
| 1068 |
+
return true;
|
| 1069 |
+
}
|
| 1070 |
+
state->parse_state = copy;
|
| 1071 |
+
|
| 1072 |
+
if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
|
| 1073 |
+
return true;
|
| 1074 |
+
}
|
| 1075 |
+
state->parse_state = copy;
|
| 1076 |
+
|
| 1077 |
+
if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") &&
|
| 1078 |
+
ParseCallOffset(state) && ParseEncoding(state)) {
|
| 1079 |
+
return true;
|
| 1080 |
+
}
|
| 1081 |
+
state->parse_state = copy;
|
| 1082 |
+
return false;
|
| 1083 |
+
}
|
| 1084 |
+
|
| 1085 |
+
// <call-offset> ::= h <nv-offset> _
|
| 1086 |
+
// ::= v <v-offset> _
|
| 1087 |
+
static bool ParseCallOffset(State *state) {
|
| 1088 |
+
ComplexityGuard guard(state);
|
| 1089 |
+
if (guard.IsTooComplex()) return false;
|
| 1090 |
+
ParseState copy = state->parse_state;
|
| 1091 |
+
if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) &&
|
| 1092 |
+
ParseOneCharToken(state, '_')) {
|
| 1093 |
+
return true;
|
| 1094 |
+
}
|
| 1095 |
+
state->parse_state = copy;
|
| 1096 |
+
|
| 1097 |
+
if (ParseOneCharToken(state, 'v') && ParseVOffset(state) &&
|
| 1098 |
+
ParseOneCharToken(state, '_')) {
|
| 1099 |
+
return true;
|
| 1100 |
+
}
|
| 1101 |
+
state->parse_state = copy;
|
| 1102 |
+
|
| 1103 |
+
return false;
|
| 1104 |
+
}
|
| 1105 |
+
|
| 1106 |
+
// <nv-offset> ::= <(offset) number>
|
| 1107 |
+
static bool ParseNVOffset(State *state) {
|
| 1108 |
+
ComplexityGuard guard(state);
|
| 1109 |
+
if (guard.IsTooComplex()) return false;
|
| 1110 |
+
return ParseNumber(state, nullptr);
|
| 1111 |
+
}
|
| 1112 |
+
|
| 1113 |
+
// <v-offset> ::= <(offset) number> _ <(virtual offset) number>
|
| 1114 |
+
static bool ParseVOffset(State *state) {
|
| 1115 |
+
ComplexityGuard guard(state);
|
| 1116 |
+
if (guard.IsTooComplex()) return false;
|
| 1117 |
+
ParseState copy = state->parse_state;
|
| 1118 |
+
if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') &&
|
| 1119 |
+
ParseNumber(state, nullptr)) {
|
| 1120 |
+
return true;
|
| 1121 |
+
}
|
| 1122 |
+
state->parse_state = copy;
|
| 1123 |
+
return false;
|
| 1124 |
+
}
|
| 1125 |
+
|
| 1126 |
+
// <ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base-class-type> | CI2
|
| 1127 |
+
// <base-class-type>
|
| 1128 |
+
// ::= D0 | D1 | D2
|
| 1129 |
+
// # GCC extensions: "unified" constructor/destructor. See
|
| 1130 |
+
// #
|
| 1131 |
+
// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847
|
| 1132 |
+
// ::= C4 | D4
|
| 1133 |
+
static bool ParseCtorDtorName(State *state) {
|
| 1134 |
+
ComplexityGuard guard(state);
|
| 1135 |
+
if (guard.IsTooComplex()) return false;
|
| 1136 |
+
ParseState copy = state->parse_state;
|
| 1137 |
+
if (ParseOneCharToken(state, 'C')) {
|
| 1138 |
+
if (ParseCharClass(state, "1234")) {
|
| 1139 |
+
const char *const prev_name =
|
| 1140 |
+
state->out + state->parse_state.prev_name_idx;
|
| 1141 |
+
MaybeAppendWithLength(state, prev_name,
|
| 1142 |
+
state->parse_state.prev_name_length);
|
| 1143 |
+
return true;
|
| 1144 |
+
} else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") &&
|
| 1145 |
+
ParseClassEnumType(state)) {
|
| 1146 |
+
return true;
|
| 1147 |
+
}
|
| 1148 |
+
}
|
| 1149 |
+
state->parse_state = copy;
|
| 1150 |
+
|
| 1151 |
+
if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) {
|
| 1152 |
+
const char *const prev_name = state->out + state->parse_state.prev_name_idx;
|
| 1153 |
+
MaybeAppend(state, "~");
|
| 1154 |
+
MaybeAppendWithLength(state, prev_name,
|
| 1155 |
+
state->parse_state.prev_name_length);
|
| 1156 |
+
return true;
|
| 1157 |
+
}
|
| 1158 |
+
state->parse_state = copy;
|
| 1159 |
+
return false;
|
| 1160 |
+
}
|
| 1161 |
+
|
| 1162 |
+
// <decltype> ::= Dt <expression> E # decltype of an id-expression or class
|
| 1163 |
+
// # member access (C++0x)
|
| 1164 |
+
// ::= DT <expression> E # decltype of an expression (C++0x)
|
| 1165 |
+
static bool ParseDecltype(State *state) {
|
| 1166 |
+
ComplexityGuard guard(state);
|
| 1167 |
+
if (guard.IsTooComplex()) return false;
|
| 1168 |
+
|
| 1169 |
+
ParseState copy = state->parse_state;
|
| 1170 |
+
if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") &&
|
| 1171 |
+
ParseExpression(state) && ParseOneCharToken(state, 'E')) {
|
| 1172 |
+
return true;
|
| 1173 |
+
}
|
| 1174 |
+
state->parse_state = copy;
|
| 1175 |
+
|
| 1176 |
+
return false;
|
| 1177 |
+
}
|
| 1178 |
+
|
| 1179 |
+
// <type> ::= <CV-qualifiers> <type>
|
| 1180 |
+
// ::= P <type> # pointer-to
|
| 1181 |
+
// ::= R <type> # reference-to
|
| 1182 |
+
// ::= O <type> # rvalue reference-to (C++0x)
|
| 1183 |
+
// ::= C <type> # complex pair (C 2000)
|
| 1184 |
+
// ::= G <type> # imaginary (C 2000)
|
| 1185 |
+
// ::= U <source-name> <type> # vendor extended type qualifier
|
| 1186 |
+
// ::= <builtin-type>
|
| 1187 |
+
// ::= <function-type>
|
| 1188 |
+
// ::= <class-enum-type> # note: just an alias for <name>
|
| 1189 |
+
// ::= <array-type>
|
| 1190 |
+
// ::= <pointer-to-member-type>
|
| 1191 |
+
// ::= <template-template-param> <template-args>
|
| 1192 |
+
// ::= <template-param>
|
| 1193 |
+
// ::= <decltype>
|
| 1194 |
+
// ::= <substitution>
|
| 1195 |
+
// ::= Dp <type> # pack expansion of (C++0x)
|
| 1196 |
+
// ::= Dv <num-elems> _ # GNU vector extension
|
| 1197 |
+
//
|
| 1198 |
+
static bool ParseType(State *state) {
|
| 1199 |
+
ComplexityGuard guard(state);
|
| 1200 |
+
if (guard.IsTooComplex()) return false;
|
| 1201 |
+
ParseState copy = state->parse_state;
|
| 1202 |
+
|
| 1203 |
+
// We should check CV-qualifers, and PRGC things first.
|
| 1204 |
+
//
|
| 1205 |
+
// CV-qualifiers overlap with some operator names, but an operator name is not
|
| 1206 |
+
// valid as a type. To avoid an ambiguity that can lead to exponential time
|
| 1207 |
+
// complexity, refuse to backtrack the CV-qualifiers.
|
| 1208 |
+
//
|
| 1209 |
+
// _Z4aoeuIrMvvE
|
| 1210 |
+
// => _Z 4aoeuI rM v v E
|
| 1211 |
+
// aoeu<operator%=, void, void>
|
| 1212 |
+
// => _Z 4aoeuI r Mv v E
|
| 1213 |
+
// aoeu<void void::* restrict>
|
| 1214 |
+
//
|
| 1215 |
+
// By consuming the CV-qualifiers first, the former parse is disabled.
|
| 1216 |
+
if (ParseCVQualifiers(state)) {
|
| 1217 |
+
const bool result = ParseType(state);
|
| 1218 |
+
if (!result) state->parse_state = copy;
|
| 1219 |
+
return result;
|
| 1220 |
+
}
|
| 1221 |
+
state->parse_state = copy;
|
| 1222 |
+
|
| 1223 |
+
// Similarly, these tag characters can overlap with other <name>s resulting in
|
| 1224 |
+
// two different parse prefixes that land on <template-args> in the same
|
| 1225 |
+
// place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by
|
| 1226 |
+
// refusing to backtrack the tag characters.
|
| 1227 |
+
if (ParseCharClass(state, "OPRCG")) {
|
| 1228 |
+
const bool result = ParseType(state);
|
| 1229 |
+
if (!result) state->parse_state = copy;
|
| 1230 |
+
return result;
|
| 1231 |
+
}
|
| 1232 |
+
state->parse_state = copy;
|
| 1233 |
+
|
| 1234 |
+
if (ParseTwoCharToken(state, "Dp") && ParseType(state)) {
|
| 1235 |
+
return true;
|
| 1236 |
+
}
|
| 1237 |
+
state->parse_state = copy;
|
| 1238 |
+
|
| 1239 |
+
if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
|
| 1240 |
+
ParseType(state)) {
|
| 1241 |
+
return true;
|
| 1242 |
+
}
|
| 1243 |
+
state->parse_state = copy;
|
| 1244 |
+
|
| 1245 |
+
if (ParseBuiltinType(state) || ParseFunctionType(state) ||
|
| 1246 |
+
ParseClassEnumType(state) || ParseArrayType(state) ||
|
| 1247 |
+
ParsePointerToMemberType(state) || ParseDecltype(state) ||
|
| 1248 |
+
// "std" on its own isn't a type.
|
| 1249 |
+
ParseSubstitution(state, /*accept_std=*/false)) {
|
| 1250 |
+
return true;
|
| 1251 |
+
}
|
| 1252 |
+
|
| 1253 |
+
if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) {
|
| 1254 |
+
return true;
|
| 1255 |
+
}
|
| 1256 |
+
state->parse_state = copy;
|
| 1257 |
+
|
| 1258 |
+
// Less greedy than <template-template-param> <template-args>.
|
| 1259 |
+
if (ParseTemplateParam(state)) {
|
| 1260 |
+
return true;
|
| 1261 |
+
}
|
| 1262 |
+
|
| 1263 |
+
if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) &&
|
| 1264 |
+
ParseOneCharToken(state, '_')) {
|
| 1265 |
+
return true;
|
| 1266 |
+
}
|
| 1267 |
+
state->parse_state = copy;
|
| 1268 |
+
|
| 1269 |
+
return false;
|
| 1270 |
+
}
|
| 1271 |
+
|
| 1272 |
+
// <CV-qualifiers> ::= [r] [V] [K]
|
| 1273 |
+
// We don't allow empty <CV-qualifiers> to avoid infinite loop in
|
| 1274 |
+
// ParseType().
|
| 1275 |
+
static bool ParseCVQualifiers(State *state) {
|
| 1276 |
+
ComplexityGuard guard(state);
|
| 1277 |
+
if (guard.IsTooComplex()) return false;
|
| 1278 |
+
int num_cv_qualifiers = 0;
|
| 1279 |
+
num_cv_qualifiers += ParseOneCharToken(state, 'r');
|
| 1280 |
+
num_cv_qualifiers += ParseOneCharToken(state, 'V');
|
| 1281 |
+
num_cv_qualifiers += ParseOneCharToken(state, 'K');
|
| 1282 |
+
return num_cv_qualifiers > 0;
|
| 1283 |
+
}
|
| 1284 |
+
|
| 1285 |
+
// <builtin-type> ::= v, etc. # single-character builtin types
|
| 1286 |
+
// ::= u <source-name>
|
| 1287 |
+
// ::= Dd, etc. # two-character builtin types
|
| 1288 |
+
//
|
| 1289 |
+
// Not supported:
|
| 1290 |
+
// ::= DF <number> _ # _FloatN (N bits)
|
| 1291 |
+
//
|
| 1292 |
+
static bool ParseBuiltinType(State *state) {
|
| 1293 |
+
ComplexityGuard guard(state);
|
| 1294 |
+
if (guard.IsTooComplex()) return false;
|
| 1295 |
+
const AbbrevPair *p;
|
| 1296 |
+
for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) {
|
| 1297 |
+
// Guaranteed only 1- or 2-character strings in kBuiltinTypeList.
|
| 1298 |
+
if (p->abbrev[1] == '\0') {
|
| 1299 |
+
if (ParseOneCharToken(state, p->abbrev[0])) {
|
| 1300 |
+
MaybeAppend(state, p->real_name);
|
| 1301 |
+
return true;
|
| 1302 |
+
}
|
| 1303 |
+
} else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) {
|
| 1304 |
+
MaybeAppend(state, p->real_name);
|
| 1305 |
+
return true;
|
| 1306 |
+
}
|
| 1307 |
+
}
|
| 1308 |
+
|
| 1309 |
+
ParseState copy = state->parse_state;
|
| 1310 |
+
if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
|
| 1311 |
+
return true;
|
| 1312 |
+
}
|
| 1313 |
+
state->parse_state = copy;
|
| 1314 |
+
return false;
|
| 1315 |
+
}
|
| 1316 |
+
|
| 1317 |
+
// <exception-spec> ::= Do # non-throwing
|
| 1318 |
+
// exception-specification (e.g.,
|
| 1319 |
+
// noexcept, throw())
|
| 1320 |
+
// ::= DO <expression> E # computed (instantiation-dependent)
|
| 1321 |
+
// noexcept
|
| 1322 |
+
// ::= Dw <type>+ E # dynamic exception specification
|
| 1323 |
+
// with instantiation-dependent types
|
| 1324 |
+
static bool ParseExceptionSpec(State *state) {
|
| 1325 |
+
ComplexityGuard guard(state);
|
| 1326 |
+
if (guard.IsTooComplex()) return false;
|
| 1327 |
+
|
| 1328 |
+
if (ParseTwoCharToken(state, "Do")) return true;
|
| 1329 |
+
|
| 1330 |
+
ParseState copy = state->parse_state;
|
| 1331 |
+
if (ParseTwoCharToken(state, "DO") && ParseExpression(state) &&
|
| 1332 |
+
ParseOneCharToken(state, 'E')) {
|
| 1333 |
+
return true;
|
| 1334 |
+
}
|
| 1335 |
+
state->parse_state = copy;
|
| 1336 |
+
if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) &&
|
| 1337 |
+
ParseOneCharToken(state, 'E')) {
|
| 1338 |
+
return true;
|
| 1339 |
+
}
|
| 1340 |
+
state->parse_state = copy;
|
| 1341 |
+
|
| 1342 |
+
return false;
|
| 1343 |
+
}
|
| 1344 |
+
|
| 1345 |
+
// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E
|
| 1346 |
+
static bool ParseFunctionType(State *state) {
|
| 1347 |
+
ComplexityGuard guard(state);
|
| 1348 |
+
if (guard.IsTooComplex()) return false;
|
| 1349 |
+
ParseState copy = state->parse_state;
|
| 1350 |
+
if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') &&
|
| 1351 |
+
Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) &&
|
| 1352 |
+
Optional(ParseOneCharToken(state, 'O')) &&
|
| 1353 |
+
ParseOneCharToken(state, 'E')) {
|
| 1354 |
+
return true;
|
| 1355 |
+
}
|
| 1356 |
+
state->parse_state = copy;
|
| 1357 |
+
return false;
|
| 1358 |
+
}
|
| 1359 |
+
|
| 1360 |
+
// <bare-function-type> ::= <(signature) type>+
|
| 1361 |
+
static bool ParseBareFunctionType(State *state) {
|
| 1362 |
+
ComplexityGuard guard(state);
|
| 1363 |
+
if (guard.IsTooComplex()) return false;
|
| 1364 |
+
ParseState copy = state->parse_state;
|
| 1365 |
+
DisableAppend(state);
|
| 1366 |
+
if (OneOrMore(ParseType, state)) {
|
| 1367 |
+
RestoreAppend(state, copy.append);
|
| 1368 |
+
MaybeAppend(state, "()");
|
| 1369 |
+
return true;
|
| 1370 |
+
}
|
| 1371 |
+
state->parse_state = copy;
|
| 1372 |
+
return false;
|
| 1373 |
+
}
|
| 1374 |
+
|
| 1375 |
+
// <class-enum-type> ::= <name>
|
| 1376 |
+
static bool ParseClassEnumType(State *state) {
|
| 1377 |
+
ComplexityGuard guard(state);
|
| 1378 |
+
if (guard.IsTooComplex()) return false;
|
| 1379 |
+
return ParseName(state);
|
| 1380 |
+
}
|
| 1381 |
+
|
| 1382 |
+
// <array-type> ::= A <(positive dimension) number> _ <(element) type>
|
| 1383 |
+
// ::= A [<(dimension) expression>] _ <(element) type>
|
| 1384 |
+
static bool ParseArrayType(State *state) {
|
| 1385 |
+
ComplexityGuard guard(state);
|
| 1386 |
+
if (guard.IsTooComplex()) return false;
|
| 1387 |
+
ParseState copy = state->parse_state;
|
| 1388 |
+
if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) &&
|
| 1389 |
+
ParseOneCharToken(state, '_') && ParseType(state)) {
|
| 1390 |
+
return true;
|
| 1391 |
+
}
|
| 1392 |
+
state->parse_state = copy;
|
| 1393 |
+
|
| 1394 |
+
if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) &&
|
| 1395 |
+
ParseOneCharToken(state, '_') && ParseType(state)) {
|
| 1396 |
+
return true;
|
| 1397 |
+
}
|
| 1398 |
+
state->parse_state = copy;
|
| 1399 |
+
return false;
|
| 1400 |
+
}
|
| 1401 |
+
|
| 1402 |
+
// <pointer-to-member-type> ::= M <(class) type> <(member) type>
|
| 1403 |
+
static bool ParsePointerToMemberType(State *state) {
|
| 1404 |
+
ComplexityGuard guard(state);
|
| 1405 |
+
if (guard.IsTooComplex()) return false;
|
| 1406 |
+
ParseState copy = state->parse_state;
|
| 1407 |
+
if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) {
|
| 1408 |
+
return true;
|
| 1409 |
+
}
|
| 1410 |
+
state->parse_state = copy;
|
| 1411 |
+
return false;
|
| 1412 |
+
}
|
| 1413 |
+
|
| 1414 |
+
// <template-param> ::= T_
|
| 1415 |
+
// ::= T <parameter-2 non-negative number> _
|
| 1416 |
+
static bool ParseTemplateParam(State *state) {
|
| 1417 |
+
ComplexityGuard guard(state);
|
| 1418 |
+
if (guard.IsTooComplex()) return false;
|
| 1419 |
+
if (ParseTwoCharToken(state, "T_")) {
|
| 1420 |
+
MaybeAppend(state, "?"); // We don't support template substitutions.
|
| 1421 |
+
return true;
|
| 1422 |
+
}
|
| 1423 |
+
|
| 1424 |
+
ParseState copy = state->parse_state;
|
| 1425 |
+
if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) &&
|
| 1426 |
+
ParseOneCharToken(state, '_')) {
|
| 1427 |
+
MaybeAppend(state, "?"); // We don't support template substitutions.
|
| 1428 |
+
return true;
|
| 1429 |
+
}
|
| 1430 |
+
state->parse_state = copy;
|
| 1431 |
+
return false;
|
| 1432 |
+
}
|
| 1433 |
+
|
| 1434 |
+
// <template-template-param> ::= <template-param>
|
| 1435 |
+
// ::= <substitution>
|
| 1436 |
+
static bool ParseTemplateTemplateParam(State *state) {
|
| 1437 |
+
ComplexityGuard guard(state);
|
| 1438 |
+
if (guard.IsTooComplex()) return false;
|
| 1439 |
+
return (ParseTemplateParam(state) ||
|
| 1440 |
+
// "std" on its own isn't a template.
|
| 1441 |
+
ParseSubstitution(state, /*accept_std=*/false));
|
| 1442 |
+
}
|
| 1443 |
+
|
| 1444 |
+
// <template-args> ::= I <template-arg>+ E
|
| 1445 |
+
static bool ParseTemplateArgs(State *state) {
|
| 1446 |
+
ComplexityGuard guard(state);
|
| 1447 |
+
if (guard.IsTooComplex()) return false;
|
| 1448 |
+
ParseState copy = state->parse_state;
|
| 1449 |
+
DisableAppend(state);
|
| 1450 |
+
if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
|
| 1451 |
+
ParseOneCharToken(state, 'E')) {
|
| 1452 |
+
RestoreAppend(state, copy.append);
|
| 1453 |
+
MaybeAppend(state, "<>");
|
| 1454 |
+
return true;
|
| 1455 |
+
}
|
| 1456 |
+
state->parse_state = copy;
|
| 1457 |
+
return false;
|
| 1458 |
+
}
|
| 1459 |
+
|
| 1460 |
+
// <template-arg> ::= <type>
|
| 1461 |
+
// ::= <expr-primary>
|
| 1462 |
+
// ::= J <template-arg>* E # argument pack
|
| 1463 |
+
// ::= X <expression> E
|
| 1464 |
+
static bool ParseTemplateArg(State *state) {
|
| 1465 |
+
ComplexityGuard guard(state);
|
| 1466 |
+
if (guard.IsTooComplex()) return false;
|
| 1467 |
+
ParseState copy = state->parse_state;
|
| 1468 |
+
if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) &&
|
| 1469 |
+
ParseOneCharToken(state, 'E')) {
|
| 1470 |
+
return true;
|
| 1471 |
+
}
|
| 1472 |
+
state->parse_state = copy;
|
| 1473 |
+
|
| 1474 |
+
// There can be significant overlap between the following leading to
|
| 1475 |
+
// exponential backtracking:
|
| 1476 |
+
//
|
| 1477 |
+
// <expr-primary> ::= L <type> <expr-cast-value> E
|
| 1478 |
+
// e.g. L 2xxIvE 1 E
|
| 1479 |
+
// <type> ==> <local-source-name> <template-args>
|
| 1480 |
+
// e.g. L 2xx IvE
|
| 1481 |
+
//
|
| 1482 |
+
// This means parsing an entire <type> twice, and <type> can contain
|
| 1483 |
+
// <template-arg>, so this can generate exponential backtracking. There is
|
| 1484 |
+
// only overlap when the remaining input starts with "L <source-name>", so
|
| 1485 |
+
// parse all cases that can start this way jointly to share the common prefix.
|
| 1486 |
+
//
|
| 1487 |
+
// We have:
|
| 1488 |
+
//
|
| 1489 |
+
// <template-arg> ::= <type>
|
| 1490 |
+
// ::= <expr-primary>
|
| 1491 |
+
//
|
| 1492 |
+
// First, drop all the productions of <type> that must start with something
|
| 1493 |
+
// other than 'L'. All that's left is <class-enum-type>; inline it.
|
| 1494 |
+
//
|
| 1495 |
+
// <type> ::= <nested-name> # starts with 'N'
|
| 1496 |
+
// ::= <unscoped-name>
|
| 1497 |
+
// ::= <unscoped-template-name> <template-args>
|
| 1498 |
+
// ::= <local-name> # starts with 'Z'
|
| 1499 |
+
//
|
| 1500 |
+
// Drop and inline again:
|
| 1501 |
+
//
|
| 1502 |
+
// <type> ::= <unscoped-name>
|
| 1503 |
+
// ::= <unscoped-name> <template-args>
|
| 1504 |
+
// ::= <substitution> <template-args> # starts with 'S'
|
| 1505 |
+
//
|
| 1506 |
+
// Merge the first two, inline <unscoped-name>, drop last:
|
| 1507 |
+
//
|
| 1508 |
+
// <type> ::= <unqualified-name> [<template-args>]
|
| 1509 |
+
// ::= St <unqualified-name> [<template-args>] # starts with 'S'
|
| 1510 |
+
//
|
| 1511 |
+
// Drop and inline:
|
| 1512 |
+
//
|
| 1513 |
+
// <type> ::= <operator-name> [<template-args>] # starts with lowercase
|
| 1514 |
+
// ::= <ctor-dtor-name> [<template-args>] # starts with 'C' or 'D'
|
| 1515 |
+
// ::= <source-name> [<template-args>] # starts with digit
|
| 1516 |
+
// ::= <local-source-name> [<template-args>]
|
| 1517 |
+
// ::= <unnamed-type-name> [<template-args>] # starts with 'U'
|
| 1518 |
+
//
|
| 1519 |
+
// One more time:
|
| 1520 |
+
//
|
| 1521 |
+
// <type> ::= L <source-name> [<template-args>]
|
| 1522 |
+
//
|
| 1523 |
+
// Likewise with <expr-primary>:
|
| 1524 |
+
//
|
| 1525 |
+
// <expr-primary> ::= L <type> <expr-cast-value> E
|
| 1526 |
+
// ::= LZ <encoding> E # cannot overlap; drop
|
| 1527 |
+
// ::= L <mangled_name> E # cannot overlap; drop
|
| 1528 |
+
//
|
| 1529 |
+
// By similar reasoning as shown above, the only <type>s starting with
|
| 1530 |
+
// <source-name> are "<source-name> [<template-args>]". Inline this.
|
| 1531 |
+
//
|
| 1532 |
+
// <expr-primary> ::= L <source-name> [<template-args>] <expr-cast-value> E
|
| 1533 |
+
//
|
| 1534 |
+
// Now inline both of these into <template-arg>:
|
| 1535 |
+
//
|
| 1536 |
+
// <template-arg> ::= L <source-name> [<template-args>]
|
| 1537 |
+
// ::= L <source-name> [<template-args>] <expr-cast-value> E
|
| 1538 |
+
//
|
| 1539 |
+
// Merge them and we're done:
|
| 1540 |
+
// <template-arg>
|
| 1541 |
+
// ::= L <source-name> [<template-args>] [<expr-cast-value> E]
|
| 1542 |
+
if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) {
|
| 1543 |
+
copy = state->parse_state;
|
| 1544 |
+
if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) {
|
| 1545 |
+
return true;
|
| 1546 |
+
}
|
| 1547 |
+
state->parse_state = copy;
|
| 1548 |
+
return true;
|
| 1549 |
+
}
|
| 1550 |
+
|
| 1551 |
+
// Now that the overlapping cases can't reach this code, we can safely call
|
| 1552 |
+
// both of these.
|
| 1553 |
+
if (ParseType(state) || ParseExprPrimary(state)) {
|
| 1554 |
+
return true;
|
| 1555 |
+
}
|
| 1556 |
+
state->parse_state = copy;
|
| 1557 |
+
|
| 1558 |
+
if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
|
| 1559 |
+
ParseOneCharToken(state, 'E')) {
|
| 1560 |
+
return true;
|
| 1561 |
+
}
|
| 1562 |
+
state->parse_state = copy;
|
| 1563 |
+
return false;
|
| 1564 |
+
}
|
| 1565 |
+
|
| 1566 |
+
// <unresolved-type> ::= <template-param> [<template-args>]
|
| 1567 |
+
// ::= <decltype>
|
| 1568 |
+
// ::= <substitution>
|
| 1569 |
+
static inline bool ParseUnresolvedType(State *state) {
|
| 1570 |
+
// No ComplexityGuard because we don't copy the state in this stack frame.
|
| 1571 |
+
return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) ||
|
| 1572 |
+
ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false);
|
| 1573 |
+
}
|
| 1574 |
+
|
| 1575 |
+
// <simple-id> ::= <source-name> [<template-args>]
|
| 1576 |
+
static inline bool ParseSimpleId(State *state) {
|
| 1577 |
+
// No ComplexityGuard because we don't copy the state in this stack frame.
|
| 1578 |
+
|
| 1579 |
+
// Note: <simple-id> cannot be followed by a parameter pack; see comment in
|
| 1580 |
+
// ParseUnresolvedType.
|
| 1581 |
+
return ParseSourceName(state) && Optional(ParseTemplateArgs(state));
|
| 1582 |
+
}
|
| 1583 |
+
|
| 1584 |
+
// <base-unresolved-name> ::= <source-name> [<template-args>]
|
| 1585 |
+
// ::= on <operator-name> [<template-args>]
|
| 1586 |
+
// ::= dn <destructor-name>
|
| 1587 |
+
static bool ParseBaseUnresolvedName(State *state) {
|
| 1588 |
+
ComplexityGuard guard(state);
|
| 1589 |
+
if (guard.IsTooComplex()) return false;
|
| 1590 |
+
|
| 1591 |
+
if (ParseSimpleId(state)) {
|
| 1592 |
+
return true;
|
| 1593 |
+
}
|
| 1594 |
+
|
| 1595 |
+
ParseState copy = state->parse_state;
|
| 1596 |
+
if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) &&
|
| 1597 |
+
Optional(ParseTemplateArgs(state))) {
|
| 1598 |
+
return true;
|
| 1599 |
+
}
|
| 1600 |
+
state->parse_state = copy;
|
| 1601 |
+
|
| 1602 |
+
if (ParseTwoCharToken(state, "dn") &&
|
| 1603 |
+
(ParseUnresolvedType(state) || ParseSimpleId(state))) {
|
| 1604 |
+
return true;
|
| 1605 |
+
}
|
| 1606 |
+
state->parse_state = copy;
|
| 1607 |
+
|
| 1608 |
+
return false;
|
| 1609 |
+
}
|
| 1610 |
+
|
| 1611 |
+
// <unresolved-name> ::= [gs] <base-unresolved-name>
|
| 1612 |
+
// ::= sr <unresolved-type> <base-unresolved-name>
|
| 1613 |
+
// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E
|
| 1614 |
+
// <base-unresolved-name>
|
| 1615 |
+
// ::= [gs] sr <unresolved-qualifier-level>+ E
|
| 1616 |
+
// <base-unresolved-name>
|
| 1617 |
+
static bool ParseUnresolvedName(State *state) {
|
| 1618 |
+
ComplexityGuard guard(state);
|
| 1619 |
+
if (guard.IsTooComplex()) return false;
|
| 1620 |
+
|
| 1621 |
+
ParseState copy = state->parse_state;
|
| 1622 |
+
if (Optional(ParseTwoCharToken(state, "gs")) &&
|
| 1623 |
+
ParseBaseUnresolvedName(state)) {
|
| 1624 |
+
return true;
|
| 1625 |
+
}
|
| 1626 |
+
state->parse_state = copy;
|
| 1627 |
+
|
| 1628 |
+
if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) &&
|
| 1629 |
+
ParseBaseUnresolvedName(state)) {
|
| 1630 |
+
return true;
|
| 1631 |
+
}
|
| 1632 |
+
state->parse_state = copy;
|
| 1633 |
+
|
| 1634 |
+
if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') &&
|
| 1635 |
+
ParseUnresolvedType(state) &&
|
| 1636 |
+
OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
|
| 1637 |
+
ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
|
| 1638 |
+
return true;
|
| 1639 |
+
}
|
| 1640 |
+
state->parse_state = copy;
|
| 1641 |
+
|
| 1642 |
+
if (Optional(ParseTwoCharToken(state, "gs")) &&
|
| 1643 |
+
ParseTwoCharToken(state, "sr") &&
|
| 1644 |
+
OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) &&
|
| 1645 |
+
ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) {
|
| 1646 |
+
return true;
|
| 1647 |
+
}
|
| 1648 |
+
state->parse_state = copy;
|
| 1649 |
+
|
| 1650 |
+
return false;
|
| 1651 |
+
}
|
| 1652 |
+
|
| 1653 |
+
// <expression> ::= <1-ary operator-name> <expression>
|
| 1654 |
+
// ::= <2-ary operator-name> <expression> <expression>
|
| 1655 |
+
// ::= <3-ary operator-name> <expression> <expression> <expression>
|
| 1656 |
+
// ::= cl <expression>+ E
|
| 1657 |
+
// ::= cp <simple-id> <expression>* E # Clang-specific.
|
| 1658 |
+
// ::= cv <type> <expression> # type (expression)
|
| 1659 |
+
// ::= cv <type> _ <expression>* E # type (expr-list)
|
| 1660 |
+
// ::= st <type>
|
| 1661 |
+
// ::= <template-param>
|
| 1662 |
+
// ::= <function-param>
|
| 1663 |
+
// ::= <expr-primary>
|
| 1664 |
+
// ::= dt <expression> <unresolved-name> # expr.name
|
| 1665 |
+
// ::= pt <expression> <unresolved-name> # expr->name
|
| 1666 |
+
// ::= sp <expression> # argument pack expansion
|
| 1667 |
+
// ::= sr <type> <unqualified-name> <template-args>
|
| 1668 |
+
// ::= sr <type> <unqualified-name>
|
| 1669 |
+
// <function-param> ::= fp <(top-level) CV-qualifiers> _
|
| 1670 |
+
// ::= fp <(top-level) CV-qualifiers> <number> _
|
| 1671 |
+
// ::= fL <number> p <(top-level) CV-qualifiers> _
|
| 1672 |
+
// ::= fL <number> p <(top-level) CV-qualifiers> <number> _
|
| 1673 |
+
static bool ParseExpression(State *state) {
|
| 1674 |
+
ComplexityGuard guard(state);
|
| 1675 |
+
if (guard.IsTooComplex()) return false;
|
| 1676 |
+
if (ParseTemplateParam(state) || ParseExprPrimary(state)) {
|
| 1677 |
+
return true;
|
| 1678 |
+
}
|
| 1679 |
+
|
| 1680 |
+
ParseState copy = state->parse_state;
|
| 1681 |
+
|
| 1682 |
+
// Object/function call expression.
|
| 1683 |
+
if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) &&
|
| 1684 |
+
ParseOneCharToken(state, 'E')) {
|
| 1685 |
+
return true;
|
| 1686 |
+
}
|
| 1687 |
+
state->parse_state = copy;
|
| 1688 |
+
|
| 1689 |
+
// Clang-specific "cp <simple-id> <expression>* E"
|
| 1690 |
+
// https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338
|
| 1691 |
+
if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) &&
|
| 1692 |
+
ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) {
|
| 1693 |
+
return true;
|
| 1694 |
+
}
|
| 1695 |
+
state->parse_state = copy;
|
| 1696 |
+
|
| 1697 |
+
// Function-param expression (level 0).
|
| 1698 |
+
if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) &&
|
| 1699 |
+
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
| 1700 |
+
return true;
|
| 1701 |
+
}
|
| 1702 |
+
state->parse_state = copy;
|
| 1703 |
+
|
| 1704 |
+
// Function-param expression (level 1+).
|
| 1705 |
+
if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) &&
|
| 1706 |
+
ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) &&
|
| 1707 |
+
Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) {
|
| 1708 |
+
return true;
|
| 1709 |
+
}
|
| 1710 |
+
state->parse_state = copy;
|
| 1711 |
+
|
| 1712 |
+
// Parse the conversion expressions jointly to avoid re-parsing the <type> in
|
| 1713 |
+
// their common prefix. Parsed as:
|
| 1714 |
+
// <expression> ::= cv <type> <conversion-args>
|
| 1715 |
+
// <conversion-args> ::= _ <expression>* E
|
| 1716 |
+
// ::= <expression>
|
| 1717 |
+
//
|
| 1718 |
+
// Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName
|
| 1719 |
+
// also needs to accept "cv <type>" in other contexts.
|
| 1720 |
+
if (ParseTwoCharToken(state, "cv")) {
|
| 1721 |
+
if (ParseType(state)) {
|
| 1722 |
+
ParseState copy2 = state->parse_state;
|
| 1723 |
+
if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) &&
|
| 1724 |
+
ParseOneCharToken(state, 'E')) {
|
| 1725 |
+
return true;
|
| 1726 |
+
}
|
| 1727 |
+
state->parse_state = copy2;
|
| 1728 |
+
if (ParseExpression(state)) {
|
| 1729 |
+
return true;
|
| 1730 |
+
}
|
| 1731 |
+
}
|
| 1732 |
+
} else {
|
| 1733 |
+
// Parse unary, binary, and ternary operator expressions jointly, taking
|
| 1734 |
+
// care not to re-parse subexpressions repeatedly. Parse like:
|
| 1735 |
+
// <expression> ::= <operator-name> <expression>
|
| 1736 |
+
// [<one-to-two-expressions>]
|
| 1737 |
+
// <one-to-two-expressions> ::= <expression> [<expression>]
|
| 1738 |
+
int arity = -1;
|
| 1739 |
+
if (ParseOperatorName(state, &arity) &&
|
| 1740 |
+
arity > 0 && // 0 arity => disabled.
|
| 1741 |
+
(arity < 3 || ParseExpression(state)) &&
|
| 1742 |
+
(arity < 2 || ParseExpression(state)) &&
|
| 1743 |
+
(arity < 1 || ParseExpression(state))) {
|
| 1744 |
+
return true;
|
| 1745 |
+
}
|
| 1746 |
+
}
|
| 1747 |
+
state->parse_state = copy;
|
| 1748 |
+
|
| 1749 |
+
// sizeof type
|
| 1750 |
+
if (ParseTwoCharToken(state, "st") && ParseType(state)) {
|
| 1751 |
+
return true;
|
| 1752 |
+
}
|
| 1753 |
+
state->parse_state = copy;
|
| 1754 |
+
|
| 1755 |
+
// Object and pointer member access expressions.
|
| 1756 |
+
if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) &&
|
| 1757 |
+
ParseExpression(state) && ParseType(state)) {
|
| 1758 |
+
return true;
|
| 1759 |
+
}
|
| 1760 |
+
state->parse_state = copy;
|
| 1761 |
+
|
| 1762 |
+
// Pointer-to-member access expressions. This parses the same as a binary
|
| 1763 |
+
// operator, but it's implemented separately because "ds" shouldn't be
|
| 1764 |
+
// accepted in other contexts that parse an operator name.
|
| 1765 |
+
if (ParseTwoCharToken(state, "ds") && ParseExpression(state) &&
|
| 1766 |
+
ParseExpression(state)) {
|
| 1767 |
+
return true;
|
| 1768 |
+
}
|
| 1769 |
+
state->parse_state = copy;
|
| 1770 |
+
|
| 1771 |
+
// Parameter pack expansion
|
| 1772 |
+
if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) {
|
| 1773 |
+
return true;
|
| 1774 |
+
}
|
| 1775 |
+
state->parse_state = copy;
|
| 1776 |
+
|
| 1777 |
+
return ParseUnresolvedName(state);
|
| 1778 |
+
}
|
| 1779 |
+
|
| 1780 |
+
// <expr-primary> ::= L <type> <(value) number> E
|
| 1781 |
+
// ::= L <type> <(value) float> E
|
| 1782 |
+
// ::= L <mangled-name> E
|
| 1783 |
+
// // A bug in g++'s C++ ABI version 2 (-fabi-version=2).
|
| 1784 |
+
// ::= LZ <encoding> E
|
| 1785 |
+
//
|
| 1786 |
+
// Warning, subtle: the "bug" LZ production above is ambiguous with the first
|
| 1787 |
+
// production where <type> starts with <local-name>, which can lead to
|
| 1788 |
+
// exponential backtracking in two scenarios:
|
| 1789 |
+
//
|
| 1790 |
+
// - When whatever follows the E in the <local-name> in the first production is
|
| 1791 |
+
// not a name, we backtrack the whole <encoding> and re-parse the whole thing.
|
| 1792 |
+
//
|
| 1793 |
+
// - When whatever follows the <local-name> in the first production is not a
|
| 1794 |
+
// number and this <expr-primary> may be followed by a name, we backtrack the
|
| 1795 |
+
// <name> and re-parse it.
|
| 1796 |
+
//
|
| 1797 |
+
// Moreover this ambiguity isn't always resolved -- for example, the following
|
| 1798 |
+
// has two different parses:
|
| 1799 |
+
//
|
| 1800 |
+
// _ZaaILZ4aoeuE1x1EvE
|
| 1801 |
+
// => operator&&<aoeu, x, E, void>
|
| 1802 |
+
// => operator&&<(aoeu::x)(1), void>
|
| 1803 |
+
//
|
| 1804 |
+
// To resolve this, we just do what GCC's demangler does, and refuse to parse
|
| 1805 |
+
// casts to <local-name> types.
|
| 1806 |
+
static bool ParseExprPrimary(State *state) {
|
| 1807 |
+
ComplexityGuard guard(state);
|
| 1808 |
+
if (guard.IsTooComplex()) return false;
|
| 1809 |
+
ParseState copy = state->parse_state;
|
| 1810 |
+
|
| 1811 |
+
// The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E"
|
| 1812 |
+
// or fail, no backtracking.
|
| 1813 |
+
if (ParseTwoCharToken(state, "LZ")) {
|
| 1814 |
+
if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) {
|
| 1815 |
+
return true;
|
| 1816 |
+
}
|
| 1817 |
+
|
| 1818 |
+
state->parse_state = copy;
|
| 1819 |
+
return false;
|
| 1820 |
+
}
|
| 1821 |
+
|
| 1822 |
+
// The merged cast production.
|
| 1823 |
+
if (ParseOneCharToken(state, 'L') && ParseType(state) &&
|
| 1824 |
+
ParseExprCastValue(state)) {
|
| 1825 |
+
return true;
|
| 1826 |
+
}
|
| 1827 |
+
state->parse_state = copy;
|
| 1828 |
+
|
| 1829 |
+
if (ParseOneCharToken(state, 'L') && ParseMangledName(state) &&
|
| 1830 |
+
ParseOneCharToken(state, 'E')) {
|
| 1831 |
+
return true;
|
| 1832 |
+
}
|
| 1833 |
+
state->parse_state = copy;
|
| 1834 |
+
|
| 1835 |
+
return false;
|
| 1836 |
+
}
|
| 1837 |
+
|
| 1838 |
+
// <number> or <float>, followed by 'E', as described above ParseExprPrimary.
|
| 1839 |
+
static bool ParseExprCastValue(State *state) {
|
| 1840 |
+
ComplexityGuard guard(state);
|
| 1841 |
+
if (guard.IsTooComplex()) return false;
|
| 1842 |
+
// We have to be able to backtrack after accepting a number because we could
|
| 1843 |
+
// have e.g. "7fffE", which will accept "7" as a number but then fail to find
|
| 1844 |
+
// the 'E'.
|
| 1845 |
+
ParseState copy = state->parse_state;
|
| 1846 |
+
if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) {
|
| 1847 |
+
return true;
|
| 1848 |
+
}
|
| 1849 |
+
state->parse_state = copy;
|
| 1850 |
+
|
| 1851 |
+
if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) {
|
| 1852 |
+
return true;
|
| 1853 |
+
}
|
| 1854 |
+
state->parse_state = copy;
|
| 1855 |
+
|
| 1856 |
+
return false;
|
| 1857 |
+
}
|
| 1858 |
+
|
| 1859 |
+
// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
|
| 1860 |
+
// ::= Z <(function) encoding> E s [<discriminator>]
|
| 1861 |
+
//
|
| 1862 |
+
// Parsing a common prefix of these two productions together avoids an
|
| 1863 |
+
// exponential blowup of backtracking. Parse like:
|
| 1864 |
+
// <local-name> := Z <encoding> E <local-name-suffix>
|
| 1865 |
+
// <local-name-suffix> ::= s [<discriminator>]
|
| 1866 |
+
// ::= <name> [<discriminator>]
|
| 1867 |
+
|
| 1868 |
+
static bool ParseLocalNameSuffix(State *state) {
|
| 1869 |
+
ComplexityGuard guard(state);
|
| 1870 |
+
if (guard.IsTooComplex()) return false;
|
| 1871 |
+
|
| 1872 |
+
if (MaybeAppend(state, "::") && ParseName(state) &&
|
| 1873 |
+
Optional(ParseDiscriminator(state))) {
|
| 1874 |
+
return true;
|
| 1875 |
+
}
|
| 1876 |
+
|
| 1877 |
+
// Since we're not going to overwrite the above "::" by re-parsing the
|
| 1878 |
+
// <encoding> (whose trailing '\0' byte was in the byte now holding the
|
| 1879 |
+
// first ':'), we have to rollback the "::" if the <name> parse failed.
|
| 1880 |
+
if (state->parse_state.append) {
|
| 1881 |
+
state->out[state->parse_state.out_cur_idx - 2] = '\0';
|
| 1882 |
+
}
|
| 1883 |
+
|
| 1884 |
+
return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state));
|
| 1885 |
+
}
|
| 1886 |
+
|
| 1887 |
+
static bool ParseLocalName(State *state) {
|
| 1888 |
+
ComplexityGuard guard(state);
|
| 1889 |
+
if (guard.IsTooComplex()) return false;
|
| 1890 |
+
ParseState copy = state->parse_state;
|
| 1891 |
+
if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
|
| 1892 |
+
ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) {
|
| 1893 |
+
return true;
|
| 1894 |
+
}
|
| 1895 |
+
state->parse_state = copy;
|
| 1896 |
+
return false;
|
| 1897 |
+
}
|
| 1898 |
+
|
| 1899 |
+
// <discriminator> := _ <(non-negative) number>
|
| 1900 |
+
static bool ParseDiscriminator(State *state) {
|
| 1901 |
+
ComplexityGuard guard(state);
|
| 1902 |
+
if (guard.IsTooComplex()) return false;
|
| 1903 |
+
ParseState copy = state->parse_state;
|
| 1904 |
+
if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) {
|
| 1905 |
+
return true;
|
| 1906 |
+
}
|
| 1907 |
+
state->parse_state = copy;
|
| 1908 |
+
return false;
|
| 1909 |
+
}
|
| 1910 |
+
|
| 1911 |
+
// <substitution> ::= S_
|
| 1912 |
+
// ::= S <seq-id> _
|
| 1913 |
+
// ::= St, etc.
|
| 1914 |
+
//
|
| 1915 |
+
// "St" is special in that it's not valid as a standalone name, and it *is*
|
| 1916 |
+
// allowed to precede a name without being wrapped in "N...E". This means that
|
| 1917 |
+
// if we accept it on its own, we can accept "St1a" and try to parse
|
| 1918 |
+
// template-args, then fail and backtrack, accept "St" on its own, then "1a" as
|
| 1919 |
+
// an unqualified name and re-parse the same template-args. To block this
|
| 1920 |
+
// exponential backtracking, we disable it with 'accept_std=false' in
|
| 1921 |
+
// problematic contexts.
|
| 1922 |
+
static bool ParseSubstitution(State *state, bool accept_std) {
|
| 1923 |
+
ComplexityGuard guard(state);
|
| 1924 |
+
if (guard.IsTooComplex()) return false;
|
| 1925 |
+
if (ParseTwoCharToken(state, "S_")) {
|
| 1926 |
+
MaybeAppend(state, "?"); // We don't support substitutions.
|
| 1927 |
+
return true;
|
| 1928 |
+
}
|
| 1929 |
+
|
| 1930 |
+
ParseState copy = state->parse_state;
|
| 1931 |
+
if (ParseOneCharToken(state, 'S') && ParseSeqId(state) &&
|
| 1932 |
+
ParseOneCharToken(state, '_')) {
|
| 1933 |
+
MaybeAppend(state, "?"); // We don't support substitutions.
|
| 1934 |
+
return true;
|
| 1935 |
+
}
|
| 1936 |
+
state->parse_state = copy;
|
| 1937 |
+
|
| 1938 |
+
// Expand abbreviations like "St" => "std".
|
| 1939 |
+
if (ParseOneCharToken(state, 'S')) {
|
| 1940 |
+
const AbbrevPair *p;
|
| 1941 |
+
for (p = kSubstitutionList; p->abbrev != nullptr; ++p) {
|
| 1942 |
+
if (RemainingInput(state)[0] == p->abbrev[1] &&
|
| 1943 |
+
(accept_std || p->abbrev[1] != 't')) {
|
| 1944 |
+
MaybeAppend(state, "std");
|
| 1945 |
+
if (p->real_name[0] != '\0') {
|
| 1946 |
+
MaybeAppend(state, "::");
|
| 1947 |
+
MaybeAppend(state, p->real_name);
|
| 1948 |
+
}
|
| 1949 |
+
++state->parse_state.mangled_idx;
|
| 1950 |
+
return true;
|
| 1951 |
+
}
|
| 1952 |
+
}
|
| 1953 |
+
}
|
| 1954 |
+
state->parse_state = copy;
|
| 1955 |
+
return false;
|
| 1956 |
+
}
|
| 1957 |
+
|
| 1958 |
+
// Parse <mangled-name>, optionally followed by either a function-clone suffix
|
| 1959 |
+
// or version suffix. Returns true only if all of "mangled_cur" was consumed.
|
| 1960 |
+
static bool ParseTopLevelMangledName(State *state) {
|
| 1961 |
+
ComplexityGuard guard(state);
|
| 1962 |
+
if (guard.IsTooComplex()) return false;
|
| 1963 |
+
if (ParseMangledName(state)) {
|
| 1964 |
+
if (RemainingInput(state)[0] != '\0') {
|
| 1965 |
+
// Drop trailing function clone suffix, if any.
|
| 1966 |
+
if (IsFunctionCloneSuffix(RemainingInput(state))) {
|
| 1967 |
+
return true;
|
| 1968 |
+
}
|
| 1969 |
+
// Append trailing version suffix if any.
|
| 1970 |
+
// ex. _Z3foo@@GLIBCXX_3.4
|
| 1971 |
+
if (RemainingInput(state)[0] == '@') {
|
| 1972 |
+
MaybeAppend(state, RemainingInput(state));
|
| 1973 |
+
return true;
|
| 1974 |
+
}
|
| 1975 |
+
return false; // Unconsumed suffix.
|
| 1976 |
+
}
|
| 1977 |
+
return true;
|
| 1978 |
+
}
|
| 1979 |
+
return false;
|
| 1980 |
+
}
|
| 1981 |
+
|
| 1982 |
+
static bool Overflowed(const State *state) {
|
| 1983 |
+
return state->parse_state.out_cur_idx >= state->out_end_idx;
|
| 1984 |
+
}
|
| 1985 |
+
|
| 1986 |
+
// The demangler entry point.
|
| 1987 |
+
bool Demangle(const char* mangled, char* out, size_t out_size) {
|
| 1988 |
+
State state;
|
| 1989 |
+
InitState(&state, mangled, out, out_size);
|
| 1990 |
+
return ParseTopLevelMangledName(&state) && !Overflowed(&state) &&
|
| 1991 |
+
state.parse_state.out_cur_idx > 0;
|
| 1992 |
+
}
|
| 1993 |
+
|
| 1994 |
+
std::string DemangleString(const char* mangled) {
|
| 1995 |
+
std::string out;
|
| 1996 |
+
int status = 0;
|
| 1997 |
+
char* demangled = nullptr;
|
| 1998 |
+
#if ABSL_INTERNAL_HAS_CXA_DEMANGLE
|
| 1999 |
+
demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status);
|
| 2000 |
+
#endif
|
| 2001 |
+
if (status == 0 && demangled != nullptr) {
|
| 2002 |
+
out.append(demangled);
|
| 2003 |
+
free(demangled);
|
| 2004 |
+
} else {
|
| 2005 |
+
out.append(mangled);
|
| 2006 |
+
}
|
| 2007 |
+
return out;
|
| 2008 |
+
}
|
| 2009 |
+
|
| 2010 |
+
} // namespace debugging_internal
|
| 2011 |
+
ABSL_NAMESPACE_END
|
| 2012 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/debugging/internal/demangle.h
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#ifndef ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
|
| 16 |
+
#define ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
|
| 17 |
+
|
| 18 |
+
#include <string>
|
| 19 |
+
#include "absl/base/config.h"
|
| 20 |
+
|
| 21 |
+
namespace absl {
|
| 22 |
+
ABSL_NAMESPACE_BEGIN
|
| 23 |
+
namespace debugging_internal {
|
| 24 |
+
|
| 25 |
+
// Demangle `mangled`. On success, return true and write the
|
| 26 |
+
// demangled symbol name to `out`. Otherwise, return false.
|
| 27 |
+
// `out` is modified even if demangling is unsuccessful.
|
| 28 |
+
//
|
| 29 |
+
// This function provides an alternative to libstdc++'s abi::__cxa_demangle,
|
| 30 |
+
// which is not async signal safe (it uses malloc internally). It's intended to
|
| 31 |
+
// be used in async signal handlers to symbolize stack traces.
|
| 32 |
+
//
|
| 33 |
+
// Note that this demangler doesn't support full demangling. More
|
| 34 |
+
// specifically, it doesn't print types of function parameters and
|
| 35 |
+
// types of template arguments. It just skips them. However, it's
|
| 36 |
+
// still very useful to extract basic information such as class,
|
| 37 |
+
// function, constructor, destructor, and operator names.
|
| 38 |
+
//
|
| 39 |
+
// See the implementation note in demangle.cc if you are interested.
|
| 40 |
+
//
|
| 41 |
+
// Example:
|
| 42 |
+
//
|
| 43 |
+
// | Mangled Name | Demangle | DemangleString
|
| 44 |
+
// |---------------|-------------|-----------------------
|
| 45 |
+
// | _Z1fv | f() | f()
|
| 46 |
+
// | _Z1fi | f() | f(int)
|
| 47 |
+
// | _Z3foo3bar | foo() | foo(bar)
|
| 48 |
+
// | _Z1fIiEvi | f<>() | void f<int>(int)
|
| 49 |
+
// | _ZN1N1fE | N::f | N::f
|
| 50 |
+
// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar()
|
| 51 |
+
// | _Zrm1XS_" | operator%() | operator%(X, X)
|
| 52 |
+
// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo()
|
| 53 |
+
// | _Z1fSs | f() | f(std::basic_string<char,
|
| 54 |
+
// | | | std::char_traits<char>,
|
| 55 |
+
// | | | std::allocator<char> >)
|
| 56 |
+
//
|
| 57 |
+
// See the unit test for more examples.
|
| 58 |
+
//
|
| 59 |
+
// Note: we might want to write demanglers for ABIs other than Itanium
|
| 60 |
+
// C++ ABI in the future.
|
| 61 |
+
bool Demangle(const char* mangled, char* out, size_t out_size);
|
| 62 |
+
|
| 63 |
+
// A wrapper around `abi::__cxa_demangle()`. On success, returns the demangled
|
| 64 |
+
// name. On failure, returns the input mangled name.
|
| 65 |
+
//
|
| 66 |
+
// This function is not async-signal-safe.
|
| 67 |
+
std::string DemangleString(const char* mangled);
|
| 68 |
+
|
| 69 |
+
} // namespace debugging_internal
|
| 70 |
+
ABSL_NAMESPACE_END
|
| 71 |
+
} // namespace absl
|
| 72 |
+
|
| 73 |
+
#endif // ABSL_DEBUGGING_INTERNAL_DEMANGLE_H_
|
weight/_dep/abseil-cpp/absl/debugging/internal/demangle_test.cc
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2018 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
#include "absl/debugging/internal/demangle.h"
|
| 16 |
+
|
| 17 |
+
#include <cstdlib>
|
| 18 |
+
#include <string>
|
| 19 |
+
|
| 20 |
+
#include "gmock/gmock.h"
|
| 21 |
+
#include "gtest/gtest.h"
|
| 22 |
+
#include "absl/base/config.h"
|
| 23 |
+
#include "absl/debugging/internal/stack_consumption.h"
|
| 24 |
+
#include "absl/log/log.h"
|
| 25 |
+
#include "absl/memory/memory.h"
|
| 26 |
+
|
| 27 |
+
namespace absl {
|
| 28 |
+
ABSL_NAMESPACE_BEGIN
|
| 29 |
+
namespace debugging_internal {
|
| 30 |
+
namespace {
|
| 31 |
+
|
| 32 |
+
using ::testing::ContainsRegex;
|
| 33 |
+
|
| 34 |
+
// Test corner cases of boundary conditions.
|
| 35 |
+
TEST(Demangle, CornerCases) {
|
| 36 |
+
char tmp[10];
|
| 37 |
+
EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp)));
|
| 38 |
+
// sizeof("foobar()") == 9
|
| 39 |
+
EXPECT_STREQ("foobar()", tmp);
|
| 40 |
+
EXPECT_TRUE(Demangle("_Z6foobarv", tmp, 9));
|
| 41 |
+
EXPECT_STREQ("foobar()", tmp);
|
| 42 |
+
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 8)); // Not enough.
|
| 43 |
+
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 1));
|
| 44 |
+
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 0));
|
| 45 |
+
EXPECT_FALSE(Demangle("_Z6foobarv", nullptr, 0)); // Should not cause SEGV.
|
| 46 |
+
EXPECT_FALSE(Demangle("_Z1000000", tmp, 9));
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
// Test handling of functions suffixed with .clone.N, which is used
|
| 50 |
+
// by GCC 4.5.x (and our locally-modified version of GCC 4.4.x), and
|
| 51 |
+
// .constprop.N and .isra.N, which are used by GCC 4.6.x. These
|
| 52 |
+
// suffixes are used to indicate functions which have been cloned
|
| 53 |
+
// during optimization. We ignore these suffixes.
|
| 54 |
+
TEST(Demangle, Clones) {
|
| 55 |
+
char tmp[20];
|
| 56 |
+
EXPECT_TRUE(Demangle("_ZL3Foov", tmp, sizeof(tmp)));
|
| 57 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 58 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.clone.3", tmp, sizeof(tmp)));
|
| 59 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 60 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.constprop.80", tmp, sizeof(tmp)));
|
| 61 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 62 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.isra.18", tmp, sizeof(tmp)));
|
| 63 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 64 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.18", tmp, sizeof(tmp)));
|
| 65 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 66 |
+
// Demangle suffixes produced by -funique-internal-linkage-names.
|
| 67 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.__uniq.12345", tmp, sizeof(tmp)));
|
| 68 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 69 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.__uniq.12345.isra.2.constprop.18", tmp,
|
| 70 |
+
sizeof(tmp)));
|
| 71 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 72 |
+
// Suffixes without the number should also demangle.
|
| 73 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp)));
|
| 74 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 75 |
+
// Suffixes with just the number should also demangle.
|
| 76 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.123", tmp, sizeof(tmp)));
|
| 77 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 78 |
+
// (.clone. followed by non-number), should also demangle.
|
| 79 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp)));
|
| 80 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 81 |
+
// (.clone. followed by multiple numbers), should also demangle.
|
| 82 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.clone.123.456", tmp, sizeof(tmp)));
|
| 83 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 84 |
+
// (a long valid suffix), should demangle.
|
| 85 |
+
EXPECT_TRUE(Demangle("_ZL3Foov.part.9.165493.constprop.775.31805", tmp,
|
| 86 |
+
sizeof(tmp)));
|
| 87 |
+
EXPECT_STREQ("Foo()", tmp);
|
| 88 |
+
// Invalid (. without anything else), should not demangle.
|
| 89 |
+
EXPECT_FALSE(Demangle("_ZL3Foov.", tmp, sizeof(tmp)));
|
| 90 |
+
// Invalid (. with mix of alpha and digits), should not demangle.
|
| 91 |
+
EXPECT_FALSE(Demangle("_ZL3Foov.abc123", tmp, sizeof(tmp)));
|
| 92 |
+
// Invalid (.clone. not followed by number), should not demangle.
|
| 93 |
+
EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
|
| 94 |
+
// Invalid (.constprop. not followed by number), should not demangle.
|
| 95 |
+
EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// Test the GNU abi_tag extension.
|
| 99 |
+
TEST(Demangle, AbiTags) {
|
| 100 |
+
char tmp[80];
|
| 101 |
+
|
| 102 |
+
// Mangled name generated via:
|
| 103 |
+
// struct [[gnu::abi_tag("abc")]] A{};
|
| 104 |
+
// A a;
|
| 105 |
+
EXPECT_TRUE(Demangle("_Z1aB3abc", tmp, sizeof(tmp)));
|
| 106 |
+
EXPECT_STREQ("a[abi:abc]", tmp);
|
| 107 |
+
|
| 108 |
+
// Mangled name generated via:
|
| 109 |
+
// struct B {
|
| 110 |
+
// B [[gnu::abi_tag("xyz")]] (){};
|
| 111 |
+
// };
|
| 112 |
+
// B b;
|
| 113 |
+
EXPECT_TRUE(Demangle("_ZN1BC2B3xyzEv", tmp, sizeof(tmp)));
|
| 114 |
+
EXPECT_STREQ("B::B[abi:xyz]()", tmp);
|
| 115 |
+
|
| 116 |
+
// Mangled name generated via:
|
| 117 |
+
// [[gnu::abi_tag("foo", "bar")]] void C() {}
|
| 118 |
+
EXPECT_TRUE(Demangle("_Z1CB3barB3foov", tmp, sizeof(tmp)));
|
| 119 |
+
EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
// Tests that verify that Demangle footprint is within some limit.
|
| 123 |
+
// They are not to be run under sanitizers as the sanitizers increase
|
| 124 |
+
// stack consumption by about 4x.
|
| 125 |
+
#if defined(ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION) && \
|
| 126 |
+
!defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
|
| 127 |
+
!defined(ABSL_HAVE_MEMORY_SANITIZER) && \
|
| 128 |
+
!defined(ABSL_HAVE_THREAD_SANITIZER)
|
| 129 |
+
|
| 130 |
+
static const char *g_mangled;
|
| 131 |
+
static char g_demangle_buffer[4096];
|
| 132 |
+
static char *g_demangle_result;
|
| 133 |
+
|
| 134 |
+
static void DemangleSignalHandler(int signo) {
|
| 135 |
+
if (Demangle(g_mangled, g_demangle_buffer, sizeof(g_demangle_buffer))) {
|
| 136 |
+
g_demangle_result = g_demangle_buffer;
|
| 137 |
+
} else {
|
| 138 |
+
g_demangle_result = nullptr;
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
// Call Demangle and figure out the stack footprint of this call.
|
| 143 |
+
static const char *DemangleStackConsumption(const char *mangled,
|
| 144 |
+
int *stack_consumed) {
|
| 145 |
+
g_mangled = mangled;
|
| 146 |
+
*stack_consumed = GetSignalHandlerStackConsumption(DemangleSignalHandler);
|
| 147 |
+
LOG(INFO) << "Stack consumption of Demangle: " << *stack_consumed;
|
| 148 |
+
return g_demangle_result;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
// Demangle stack consumption should be within 8kB for simple mangled names
|
| 152 |
+
// with some level of nesting. With alternate signal stack we have 64K,
|
| 153 |
+
// but some signal handlers run on thread stack, and could have arbitrarily
|
| 154 |
+
// little space left (so we don't want to make this number too large).
|
| 155 |
+
const int kStackConsumptionUpperLimit = 8192;
|
| 156 |
+
|
| 157 |
+
// Returns a mangled name nested to the given depth.
|
| 158 |
+
static std::string NestedMangledName(int depth) {
|
| 159 |
+
std::string mangled_name = "_Z1a";
|
| 160 |
+
if (depth > 0) {
|
| 161 |
+
mangled_name += "IXL";
|
| 162 |
+
mangled_name += NestedMangledName(depth - 1);
|
| 163 |
+
mangled_name += "EEE";
|
| 164 |
+
}
|
| 165 |
+
return mangled_name;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
TEST(Demangle, DemangleStackConsumption) {
|
| 169 |
+
// Measure stack consumption of Demangle for nested mangled names of varying
|
| 170 |
+
// depth. Since Demangle is implemented as a recursive descent parser,
|
| 171 |
+
// stack consumption will grow as the nesting depth increases. By measuring
|
| 172 |
+
// the stack consumption for increasing depths, we can see the growing
|
| 173 |
+
// impact of any stack-saving changes made to the code for Demangle.
|
| 174 |
+
int stack_consumed = 0;
|
| 175 |
+
|
| 176 |
+
const char *demangled =
|
| 177 |
+
DemangleStackConsumption("_Z6foobarv", &stack_consumed);
|
| 178 |
+
EXPECT_STREQ("foobar()", demangled);
|
| 179 |
+
EXPECT_GT(stack_consumed, 0);
|
| 180 |
+
EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
|
| 181 |
+
|
| 182 |
+
const std::string nested_mangled_name0 = NestedMangledName(0);
|
| 183 |
+
demangled = DemangleStackConsumption(nested_mangled_name0.c_str(),
|
| 184 |
+
&stack_consumed);
|
| 185 |
+
EXPECT_STREQ("a", demangled);
|
| 186 |
+
EXPECT_GT(stack_consumed, 0);
|
| 187 |
+
EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
|
| 188 |
+
|
| 189 |
+
const std::string nested_mangled_name1 = NestedMangledName(1);
|
| 190 |
+
demangled = DemangleStackConsumption(nested_mangled_name1.c_str(),
|
| 191 |
+
&stack_consumed);
|
| 192 |
+
EXPECT_STREQ("a<>", demangled);
|
| 193 |
+
EXPECT_GT(stack_consumed, 0);
|
| 194 |
+
EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
|
| 195 |
+
|
| 196 |
+
const std::string nested_mangled_name2 = NestedMangledName(2);
|
| 197 |
+
demangled = DemangleStackConsumption(nested_mangled_name2.c_str(),
|
| 198 |
+
&stack_consumed);
|
| 199 |
+
EXPECT_STREQ("a<>", demangled);
|
| 200 |
+
EXPECT_GT(stack_consumed, 0);
|
| 201 |
+
EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
|
| 202 |
+
|
| 203 |
+
const std::string nested_mangled_name3 = NestedMangledName(3);
|
| 204 |
+
demangled = DemangleStackConsumption(nested_mangled_name3.c_str(),
|
| 205 |
+
&stack_consumed);
|
| 206 |
+
EXPECT_STREQ("a<>", demangled);
|
| 207 |
+
EXPECT_GT(stack_consumed, 0);
|
| 208 |
+
EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit);
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
#endif // Stack consumption tests
|
| 212 |
+
|
| 213 |
+
static void TestOnInput(const char* input) {
|
| 214 |
+
static const int kOutSize = 1048576;
|
| 215 |
+
auto out = absl::make_unique<char[]>(kOutSize);
|
| 216 |
+
Demangle(input, out.get(), kOutSize);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
TEST(DemangleRegression, NegativeLength) {
|
| 220 |
+
TestOnInput("_ZZn4");
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
TEST(DemangleRegression, DeeplyNestedArrayType) {
|
| 224 |
+
const int depth = 100000;
|
| 225 |
+
std::string data = "_ZStI";
|
| 226 |
+
data.reserve(data.size() + 3 * depth + 1);
|
| 227 |
+
for (int i = 0; i < depth; i++) {
|
| 228 |
+
data += "A1_";
|
| 229 |
+
}
|
| 230 |
+
TestOnInput(data.c_str());
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
struct Base {
|
| 234 |
+
virtual ~Base() = default;
|
| 235 |
+
};
|
| 236 |
+
|
| 237 |
+
struct Derived : public Base {};
|
| 238 |
+
|
| 239 |
+
TEST(DemangleStringTest, SupportsSymbolNameReturnedByTypeId) {
|
| 240 |
+
EXPECT_EQ(DemangleString(typeid(int).name()), "int");
|
| 241 |
+
// We want to test that `DemangleString` can demangle the symbol names
|
| 242 |
+
// returned by `typeid`, but without hard-coding the actual demangled values
|
| 243 |
+
// (because they are platform-specific).
|
| 244 |
+
EXPECT_THAT(
|
| 245 |
+
DemangleString(typeid(Base).name()),
|
| 246 |
+
ContainsRegex("absl.*debugging_internal.*anonymous namespace.*::Base"));
|
| 247 |
+
EXPECT_THAT(DemangleString(typeid(Derived).name()),
|
| 248 |
+
ContainsRegex(
|
| 249 |
+
"absl.*debugging_internal.*anonymous namespace.*::Derived"));
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
} // namespace
|
| 253 |
+
} // namespace debugging_internal
|
| 254 |
+
ABSL_NAMESPACE_END
|
| 255 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/debugging/internal/elf_mem_image.cc
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2017 The Abseil Authors.
|
| 2 |
+
//
|
| 3 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
// you may not use this file except in compliance with the License.
|
| 5 |
+
// You may obtain a copy of the License at
|
| 6 |
+
//
|
| 7 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
//
|
| 9 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
// See the License for the specific language governing permissions and
|
| 13 |
+
// limitations under the License.
|
| 14 |
+
|
| 15 |
+
// Allow dynamic symbol lookup in an in-memory Elf image.
|
| 16 |
+
//
|
| 17 |
+
|
| 18 |
+
#include "absl/debugging/internal/elf_mem_image.h"
|
| 19 |
+
|
| 20 |
+
#ifdef ABSL_HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h
|
| 21 |
+
|
| 22 |
+
#include <string.h>
|
| 23 |
+
#include <cassert>
|
| 24 |
+
#include <cstddef>
|
| 25 |
+
#include "absl/base/config.h"
|
| 26 |
+
#include "absl/base/internal/raw_logging.h"
|
| 27 |
+
|
| 28 |
+
// From binutils/include/elf/common.h (this doesn't appear to be documented
|
| 29 |
+
// anywhere else).
|
| 30 |
+
//
|
| 31 |
+
// /* This flag appears in a Versym structure. It means that the symbol
|
| 32 |
+
// is hidden, and is only visible with an explicit version number.
|
| 33 |
+
// This is a GNU extension. */
|
| 34 |
+
// #define VERSYM_HIDDEN 0x8000
|
| 35 |
+
//
|
| 36 |
+
// /* This is the mask for the rest of the Versym information. */
|
| 37 |
+
// #define VERSYM_VERSION 0x7fff
|
| 38 |
+
|
| 39 |
+
#define VERSYM_VERSION 0x7fff
|
| 40 |
+
|
| 41 |
+
namespace absl {
|
| 42 |
+
ABSL_NAMESPACE_BEGIN
|
| 43 |
+
namespace debugging_internal {
|
| 44 |
+
|
| 45 |
+
namespace {
|
| 46 |
+
|
| 47 |
+
#if __SIZEOF_POINTER__ == 4
|
| 48 |
+
const int kElfClass = ELFCLASS32;
|
| 49 |
+
int ElfBind(const ElfW(Sym) *symbol) { return ELF32_ST_BIND(symbol->st_info); }
|
| 50 |
+
int ElfType(const ElfW(Sym) *symbol) { return ELF32_ST_TYPE(symbol->st_info); }
|
| 51 |
+
#elif __SIZEOF_POINTER__ == 8
|
| 52 |
+
const int kElfClass = ELFCLASS64;
|
| 53 |
+
int ElfBind(const ElfW(Sym) *symbol) { return ELF64_ST_BIND(symbol->st_info); }
|
| 54 |
+
int ElfType(const ElfW(Sym) *symbol) { return ELF64_ST_TYPE(symbol->st_info); }
|
| 55 |
+
#else
|
| 56 |
+
const int kElfClass = -1;
|
| 57 |
+
int ElfBind(const ElfW(Sym) *) {
|
| 58 |
+
ABSL_RAW_LOG(FATAL, "Unexpected word size");
|
| 59 |
+
return 0;
|
| 60 |
+
}
|
| 61 |
+
int ElfType(const ElfW(Sym) *) {
|
| 62 |
+
ABSL_RAW_LOG(FATAL, "Unexpected word size");
|
| 63 |
+
return 0;
|
| 64 |
+
}
|
| 65 |
+
#endif
|
| 66 |
+
|
| 67 |
+
// Extract an element from one of the ELF tables, cast it to desired type.
|
| 68 |
+
// This is just a simple arithmetic and a glorified cast.
|
| 69 |
+
// Callers are responsible for bounds checking.
|
| 70 |
+
template <typename T>
|
| 71 |
+
const T *GetTableElement(const ElfW(Ehdr) * ehdr, ElfW(Off) table_offset,
|
| 72 |
+
ElfW(Word) element_size, size_t index) {
|
| 73 |
+
return reinterpret_cast<const T*>(reinterpret_cast<const char *>(ehdr)
|
| 74 |
+
+ table_offset
|
| 75 |
+
+ index * element_size);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
} // namespace
|
| 79 |
+
|
| 80 |
+
// The value of this variable doesn't matter; it's used only for its
|
| 81 |
+
// unique address.
|
| 82 |
+
const int ElfMemImage::kInvalidBaseSentinel = 0;
|
| 83 |
+
|
| 84 |
+
ElfMemImage::ElfMemImage(const void *base) {
|
| 85 |
+
ABSL_RAW_CHECK(base != kInvalidBase, "bad pointer");
|
| 86 |
+
Init(base);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
int ElfMemImage::GetNumSymbols() const {
|
| 90 |
+
if (!hash_) {
|
| 91 |
+
return 0;
|
| 92 |
+
}
|
| 93 |
+
// See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash
|
| 94 |
+
return static_cast<int>(hash_[1]);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const {
|
| 98 |
+
ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
|
| 99 |
+
return dynsym_ + index;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const ElfW(Versym) *ElfMemImage::GetVersym(int index) const {
|
| 103 |
+
ABSL_RAW_CHECK(index < GetNumSymbols(), "index out of range");
|
| 104 |
+
return versym_ + index;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const {
|
| 108 |
+
ABSL_RAW_CHECK(index >= 0 && index < ehdr_->e_phnum, "index out of range");
|
| 109 |
+
return GetTableElement<ElfW(Phdr)>(ehdr_, ehdr_->e_phoff, ehdr_->e_phentsize,
|
| 110 |
+
static_cast<size_t>(index));
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const {
|
| 114 |
+
ABSL_RAW_CHECK(offset < strsize_, "offset out of range");
|
| 115 |
+
return dynstr_ + offset;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
const void *ElfMemImage::GetSymAddr(const ElfW(Sym) *sym) const {
|
| 119 |
+
if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE) {
|
| 120 |
+
// Symbol corresponds to "special" (e.g. SHN_ABS) section.
|
| 121 |
+
return reinterpret_cast<const void *>(sym->st_value);
|
| 122 |
+
}
|
| 123 |
+
ABSL_RAW_CHECK(link_base_ < sym->st_value, "symbol out of range");
|
| 124 |
+
return GetTableElement<char>(ehdr_, 0, 1, sym->st_value - link_base_);
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const {
|
| 128 |
+
ABSL_RAW_CHECK(0 <= index && static_cast<size_t>(index) <= verdefnum_,
|
| 129 |
+
"index out of range");
|
| 130 |
+
const ElfW(Verdef) *version_definition = verdef_;
|
| 131 |
+
while (version_definition->vd_ndx < index && version_definition->vd_next) {
|
| 132 |
+
const char *const version_definition_as_char =
|
| 133 |
+
reinterpret_cast<const char *>(version_definition);
|
| 134 |
+
version_definition =
|
| 135 |
+
reinterpret_cast<const ElfW(Verdef) *>(version_definition_as_char +
|
| 136 |
+
version_definition->vd_next);
|
| 137 |
+
}
|
| 138 |
+
return version_definition->vd_ndx == index ? version_definition : nullptr;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
const ElfW(Verdaux) *ElfMemImage::GetVerdefAux(
|
| 142 |
+
const ElfW(Verdef) *verdef) const {
|
| 143 |
+
return reinterpret_cast<const ElfW(Verdaux) *>(verdef+1);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
const char *ElfMemImage::GetVerstr(ElfW(Word) offset) const {
|
| 147 |
+
ABSL_RAW_CHECK(offset < strsize_, "offset out of range");
|
| 148 |
+
return dynstr_ + offset;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
void ElfMemImage::Init(const void *base) {
|
| 152 |
+
ehdr_ = nullptr;
|
| 153 |
+
dynsym_ = nullptr;
|
| 154 |
+
dynstr_ = nullptr;
|
| 155 |
+
versym_ = nullptr;
|
| 156 |
+
verdef_ = nullptr;
|
| 157 |
+
hash_ = nullptr;
|
| 158 |
+
strsize_ = 0;
|
| 159 |
+
verdefnum_ = 0;
|
| 160 |
+
// Sentinel: PT_LOAD .p_vaddr can't possibly be this.
|
| 161 |
+
link_base_ = ~ElfW(Addr){0}; // NOLINT(readability/braces)
|
| 162 |
+
if (!base) {
|
| 163 |
+
return;
|
| 164 |
+
}
|
| 165 |
+
const char *const base_as_char = reinterpret_cast<const char *>(base);
|
| 166 |
+
if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 ||
|
| 167 |
+
base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) {
|
| 168 |
+
assert(false);
|
| 169 |
+
return;
|
| 170 |
+
}
|
| 171 |
+
int elf_class = base_as_char[EI_CLASS];
|
| 172 |
+
if (elf_class != kElfClass) {
|
| 173 |
+
assert(false);
|
| 174 |
+
return;
|
| 175 |
+
}
|
| 176 |
+
switch (base_as_char[EI_DATA]) {
|
| 177 |
+
case ELFDATA2LSB: {
|
| 178 |
+
#ifndef ABSL_IS_LITTLE_ENDIAN
|
| 179 |
+
assert(false);
|
| 180 |
+
return;
|
| 181 |
+
#endif
|
| 182 |
+
break;
|
| 183 |
+
}
|
| 184 |
+
case ELFDATA2MSB: {
|
| 185 |
+
#ifndef ABSL_IS_BIG_ENDIAN
|
| 186 |
+
assert(false);
|
| 187 |
+
return;
|
| 188 |
+
#endif
|
| 189 |
+
break;
|
| 190 |
+
}
|
| 191 |
+
default: {
|
| 192 |
+
assert(false);
|
| 193 |
+
return;
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
ehdr_ = reinterpret_cast<const ElfW(Ehdr) *>(base);
|
| 198 |
+
const ElfW(Phdr) *dynamic_program_header = nullptr;
|
| 199 |
+
for (int i = 0; i < ehdr_->e_phnum; ++i) {
|
| 200 |
+
const ElfW(Phdr) *const program_header = GetPhdr(i);
|
| 201 |
+
switch (program_header->p_type) {
|
| 202 |
+
case PT_LOAD:
|
| 203 |
+
if (!~link_base_) {
|
| 204 |
+
link_base_ = program_header->p_vaddr;
|
| 205 |
+
}
|
| 206 |
+
break;
|
| 207 |
+
case PT_DYNAMIC:
|
| 208 |
+
dynamic_program_header = program_header;
|
| 209 |
+
break;
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
if (!~link_base_ || !dynamic_program_header) {
|
| 213 |
+
assert(false);
|
| 214 |
+
// Mark this image as not present. Can not recur infinitely.
|
| 215 |
+
Init(nullptr);
|
| 216 |
+
return;
|
| 217 |
+
}
|
| 218 |
+
ptrdiff_t relocation =
|
| 219 |
+
base_as_char - reinterpret_cast<const char *>(link_base_);
|
| 220 |
+
ElfW(Dyn)* dynamic_entry = reinterpret_cast<ElfW(Dyn)*>(
|
| 221 |
+
static_cast<intptr_t>(dynamic_program_header->p_vaddr) + relocation);
|
| 222 |
+
for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
|
| 223 |
+
const auto value =
|
| 224 |
+
static_cast<intptr_t>(dynamic_entry->d_un.d_val) + relocation;
|
| 225 |
+
switch (dynamic_entry->d_tag) {
|
| 226 |
+
case DT_HASH:
|
| 227 |
+
hash_ = reinterpret_cast<ElfW(Word) *>(value);
|
| 228 |
+
break;
|
| 229 |
+
case DT_SYMTAB:
|
| 230 |
+
dynsym_ = reinterpret_cast<ElfW(Sym) *>(value);
|
| 231 |
+
break;
|
| 232 |
+
case DT_STRTAB:
|
| 233 |
+
dynstr_ = reinterpret_cast<const char *>(value);
|
| 234 |
+
break;
|
| 235 |
+
case DT_VERSYM:
|
| 236 |
+
versym_ = reinterpret_cast<ElfW(Versym) *>(value);
|
| 237 |
+
break;
|
| 238 |
+
case DT_VERDEF:
|
| 239 |
+
verdef_ = reinterpret_cast<ElfW(Verdef) *>(value);
|
| 240 |
+
break;
|
| 241 |
+
case DT_VERDEFNUM:
|
| 242 |
+
verdefnum_ = static_cast<size_t>(dynamic_entry->d_un.d_val);
|
| 243 |
+
break;
|
| 244 |
+
case DT_STRSZ:
|
| 245 |
+
strsize_ = static_cast<size_t>(dynamic_entry->d_un.d_val);
|
| 246 |
+
break;
|
| 247 |
+
default:
|
| 248 |
+
// Unrecognized entries explicitly ignored.
|
| 249 |
+
break;
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
if (!hash_ || !dynsym_ || !dynstr_ || !versym_ ||
|
| 253 |
+
!verdef_ || !verdefnum_ || !strsize_) {
|
| 254 |
+
assert(false); // invalid VDSO
|
| 255 |
+
// Mark this image as not present. Can not recur infinitely.
|
| 256 |
+
Init(nullptr);
|
| 257 |
+
return;
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
bool ElfMemImage::LookupSymbol(const char *name,
|
| 262 |
+
const char *version,
|
| 263 |
+
int type,
|
| 264 |
+
SymbolInfo *info_out) const {
|
| 265 |
+
for (const SymbolInfo& info : *this) {
|
| 266 |
+
if (strcmp(info.name, name) == 0 && strcmp(info.version, version) == 0 &&
|
| 267 |
+
ElfType(info.symbol) == type) {
|
| 268 |
+
if (info_out) {
|
| 269 |
+
*info_out = info;
|
| 270 |
+
}
|
| 271 |
+
return true;
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
return false;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
bool ElfMemImage::LookupSymbolByAddress(const void *address,
|
| 278 |
+
SymbolInfo *info_out) const {
|
| 279 |
+
for (const SymbolInfo& info : *this) {
|
| 280 |
+
const char *const symbol_start =
|
| 281 |
+
reinterpret_cast<const char *>(info.address);
|
| 282 |
+
const char *const symbol_end = symbol_start + info.symbol->st_size;
|
| 283 |
+
if (symbol_start <= address && address < symbol_end) {
|
| 284 |
+
if (info_out) {
|
| 285 |
+
// Client wants to know details for that symbol (the usual case).
|
| 286 |
+
if (ElfBind(info.symbol) == STB_GLOBAL) {
|
| 287 |
+
// Strong symbol; just return it.
|
| 288 |
+
*info_out = info;
|
| 289 |
+
return true;
|
| 290 |
+
} else {
|
| 291 |
+
// Weak or local. Record it, but keep looking for a strong one.
|
| 292 |
+
*info_out = info;
|
| 293 |
+
}
|
| 294 |
+
} else {
|
| 295 |
+
// Client only cares if there is an overlapping symbol.
|
| 296 |
+
return true;
|
| 297 |
+
}
|
| 298 |
+
}
|
| 299 |
+
}
|
| 300 |
+
return false;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index)
|
| 304 |
+
: index_(index), image_(image) {
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const {
|
| 308 |
+
return &info_;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
const ElfMemImage::SymbolInfo& ElfMemImage::SymbolIterator::operator*() const {
|
| 312 |
+
return info_;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
bool ElfMemImage::SymbolIterator::operator==(const SymbolIterator &rhs) const {
|
| 316 |
+
return this->image_ == rhs.image_ && this->index_ == rhs.index_;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
bool ElfMemImage::SymbolIterator::operator!=(const SymbolIterator &rhs) const {
|
| 320 |
+
return !(*this == rhs);
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
ElfMemImage::SymbolIterator &ElfMemImage::SymbolIterator::operator++() {
|
| 324 |
+
this->Update(1);
|
| 325 |
+
return *this;
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
ElfMemImage::SymbolIterator ElfMemImage::begin() const {
|
| 329 |
+
SymbolIterator it(this, 0);
|
| 330 |
+
it.Update(0);
|
| 331 |
+
return it;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
ElfMemImage::SymbolIterator ElfMemImage::end() const {
|
| 335 |
+
return SymbolIterator(this, GetNumSymbols());
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
void ElfMemImage::SymbolIterator::Update(int increment) {
|
| 339 |
+
const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_);
|
| 340 |
+
ABSL_RAW_CHECK(image->IsPresent() || increment == 0, "");
|
| 341 |
+
if (!image->IsPresent()) {
|
| 342 |
+
return;
|
| 343 |
+
}
|
| 344 |
+
index_ += increment;
|
| 345 |
+
if (index_ >= image->GetNumSymbols()) {
|
| 346 |
+
index_ = image->GetNumSymbols();
|
| 347 |
+
return;
|
| 348 |
+
}
|
| 349 |
+
const ElfW(Sym) *symbol = image->GetDynsym(index_);
|
| 350 |
+
const ElfW(Versym) *version_symbol = image->GetVersym(index_);
|
| 351 |
+
ABSL_RAW_CHECK(symbol && version_symbol, "");
|
| 352 |
+
const char *const symbol_name = image->GetDynstr(symbol->st_name);
|
| 353 |
+
#if defined(__NetBSD__)
|
| 354 |
+
const int version_index = version_symbol->vs_vers & VERSYM_VERSION;
|
| 355 |
+
#else
|
| 356 |
+
const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION;
|
| 357 |
+
#endif
|
| 358 |
+
const ElfW(Verdef) *version_definition = nullptr;
|
| 359 |
+
const char *version_name = "";
|
| 360 |
+
if (symbol->st_shndx == SHN_UNDEF) {
|
| 361 |
+
// Undefined symbols reference DT_VERNEED, not DT_VERDEF, and
|
| 362 |
+
// version_index could well be greater than verdefnum_, so calling
|
| 363 |
+
// GetVerdef(version_index) may trigger assertion.
|
| 364 |
+
} else {
|
| 365 |
+
version_definition = image->GetVerdef(version_index);
|
| 366 |
+
}
|
| 367 |
+
if (version_definition) {
|
| 368 |
+
// I am expecting 1 or 2 auxiliary entries: 1 for the version itself,
|
| 369 |
+
// optional 2nd if the version has a parent.
|
| 370 |
+
ABSL_RAW_CHECK(
|
| 371 |
+
version_definition->vd_cnt == 1 || version_definition->vd_cnt == 2,
|
| 372 |
+
"wrong number of entries");
|
| 373 |
+
const ElfW(Verdaux) *version_aux = image->GetVerdefAux(version_definition);
|
| 374 |
+
version_name = image->GetVerstr(version_aux->vda_name);
|
| 375 |
+
}
|
| 376 |
+
info_.name = symbol_name;
|
| 377 |
+
info_.version = version_name;
|
| 378 |
+
info_.address = image->GetSymAddr(symbol);
|
| 379 |
+
info_.symbol = symbol;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
} // namespace debugging_internal
|
| 383 |
+
ABSL_NAMESPACE_END
|
| 384 |
+
} // namespace absl
|
| 385 |
+
|
| 386 |
+
#endif // ABSL_HAVE_ELF_MEM_IMAGE
|
weight/_dep/abseil-cpp/absl/debugging/internal/elf_mem_image.h
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright 2017 The Abseil Authors.
|
| 3 |
+
*
|
| 4 |
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
* you may not use this file except in compliance with the License.
|
| 6 |
+
* You may obtain a copy of the License at
|
| 7 |
+
*
|
| 8 |
+
* https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
*
|
| 10 |
+
* Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
* See the License for the specific language governing permissions and
|
| 14 |
+
* limitations under the License.
|
| 15 |
+
*/
|
| 16 |
+
|
| 17 |
+
// Allow dynamic symbol lookup for in-memory Elf images.
|
| 18 |
+
|
| 19 |
+
#ifndef ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
|
| 20 |
+
#define ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
|
| 21 |
+
|
| 22 |
+
// Including this will define the __GLIBC__ macro if glibc is being
|
| 23 |
+
// used.
|
| 24 |
+
#include <climits>
|
| 25 |
+
|
| 26 |
+
#include "absl/base/config.h"
|
| 27 |
+
|
| 28 |
+
// Maybe one day we can rewrite this file not to require the elf
|
| 29 |
+
// symbol extensions in glibc, but for right now we need them.
|
| 30 |
+
#ifdef ABSL_HAVE_ELF_MEM_IMAGE
|
| 31 |
+
#error ABSL_HAVE_ELF_MEM_IMAGE cannot be directly set
|
| 32 |
+
#endif
|
| 33 |
+
|
| 34 |
+
#if defined(__ELF__) && !defined(__OpenBSD__) && !defined(__QNX__) && \
|
| 35 |
+
!defined(__native_client__) && !defined(__asmjs__) && \
|
| 36 |
+
!defined(__wasm__) && !defined(__HAIKU__) && !defined(__sun) && \
|
| 37 |
+
!defined(__VXWORKS__) && !defined(__hexagon__)
|
| 38 |
+
#define ABSL_HAVE_ELF_MEM_IMAGE 1
|
| 39 |
+
#endif
|
| 40 |
+
|
| 41 |
+
#ifdef ABSL_HAVE_ELF_MEM_IMAGE
|
| 42 |
+
|
| 43 |
+
#include <link.h> // for ElfW
|
| 44 |
+
|
| 45 |
+
#if defined(__FreeBSD__) && !defined(ElfW)
|
| 46 |
+
#define ElfW(x) __ElfN(x)
|
| 47 |
+
#endif
|
| 48 |
+
|
| 49 |
+
namespace absl {
|
| 50 |
+
ABSL_NAMESPACE_BEGIN
|
| 51 |
+
namespace debugging_internal {
|
| 52 |
+
|
| 53 |
+
// An in-memory ELF image (may not exist on disk).
|
| 54 |
+
class ElfMemImage {
|
| 55 |
+
private:
|
| 56 |
+
// Sentinel: there could never be an elf image at &kInvalidBaseSentinel.
|
| 57 |
+
static const int kInvalidBaseSentinel;
|
| 58 |
+
|
| 59 |
+
public:
|
| 60 |
+
// Sentinel: there could never be an elf image at this address.
|
| 61 |
+
static constexpr const void *const kInvalidBase =
|
| 62 |
+
static_cast<const void*>(&kInvalidBaseSentinel);
|
| 63 |
+
|
| 64 |
+
// Information about a single vdso symbol.
|
| 65 |
+
// All pointers are into .dynsym, .dynstr, or .text of the VDSO.
|
| 66 |
+
// Do not free() them or modify through them.
|
| 67 |
+
struct SymbolInfo {
|
| 68 |
+
const char *name; // E.g. "__vdso_getcpu"
|
| 69 |
+
const char *version; // E.g. "LINUX_2.6", could be ""
|
| 70 |
+
// for unversioned symbol.
|
| 71 |
+
const void *address; // Relocated symbol address.
|
| 72 |
+
const ElfW(Sym) *symbol; // Symbol in the dynamic symbol table.
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
// Supports iteration over all dynamic symbols.
|
| 76 |
+
class SymbolIterator {
|
| 77 |
+
public:
|
| 78 |
+
friend class ElfMemImage;
|
| 79 |
+
const SymbolInfo *operator->() const;
|
| 80 |
+
const SymbolInfo &operator*() const;
|
| 81 |
+
SymbolIterator& operator++();
|
| 82 |
+
bool operator!=(const SymbolIterator &rhs) const;
|
| 83 |
+
bool operator==(const SymbolIterator &rhs) const;
|
| 84 |
+
private:
|
| 85 |
+
SymbolIterator(const void *const image, int index);
|
| 86 |
+
void Update(int incr);
|
| 87 |
+
SymbolInfo info_;
|
| 88 |
+
int index_;
|
| 89 |
+
const void *const image_;
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
explicit ElfMemImage(const void *base);
|
| 94 |
+
void Init(const void *base);
|
| 95 |
+
bool IsPresent() const { return ehdr_ != nullptr; }
|
| 96 |
+
const ElfW(Phdr)* GetPhdr(int index) const;
|
| 97 |
+
const ElfW(Sym)* GetDynsym(int index) const;
|
| 98 |
+
const ElfW(Versym)* GetVersym(int index) const;
|
| 99 |
+
const ElfW(Verdef)* GetVerdef(int index) const;
|
| 100 |
+
const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const;
|
| 101 |
+
const char* GetDynstr(ElfW(Word) offset) const;
|
| 102 |
+
const void* GetSymAddr(const ElfW(Sym) *sym) const;
|
| 103 |
+
const char* GetVerstr(ElfW(Word) offset) const;
|
| 104 |
+
int GetNumSymbols() const;
|
| 105 |
+
|
| 106 |
+
SymbolIterator begin() const;
|
| 107 |
+
SymbolIterator end() const;
|
| 108 |
+
|
| 109 |
+
// Look up versioned dynamic symbol in the image.
|
| 110 |
+
// Returns false if image is not present, or doesn't contain given
|
| 111 |
+
// symbol/version/type combination.
|
| 112 |
+
// If info_out is non-null, additional details are filled in.
|
| 113 |
+
bool LookupSymbol(const char *name, const char *version,
|
| 114 |
+
int symbol_type, SymbolInfo *info_out) const;
|
| 115 |
+
|
| 116 |
+
// Find info about symbol (if any) which overlaps given address.
|
| 117 |
+
// Returns true if symbol was found; false if image isn't present
|
| 118 |
+
// or doesn't have a symbol overlapping given address.
|
| 119 |
+
// If info_out is non-null, additional details are filled in.
|
| 120 |
+
bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
|
| 121 |
+
|
| 122 |
+
private:
|
| 123 |
+
const ElfW(Ehdr) *ehdr_;
|
| 124 |
+
const ElfW(Sym) *dynsym_;
|
| 125 |
+
const ElfW(Versym) *versym_;
|
| 126 |
+
const ElfW(Verdef) *verdef_;
|
| 127 |
+
const ElfW(Word) *hash_;
|
| 128 |
+
const char *dynstr_;
|
| 129 |
+
size_t strsize_;
|
| 130 |
+
size_t verdefnum_;
|
| 131 |
+
ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD).
|
| 132 |
+
};
|
| 133 |
+
|
| 134 |
+
} // namespace debugging_internal
|
| 135 |
+
ABSL_NAMESPACE_END
|
| 136 |
+
} // namespace absl
|
| 137 |
+
|
| 138 |
+
#endif // ABSL_HAVE_ELF_MEM_IMAGE
|
| 139 |
+
|
| 140 |
+
#endif // ABSL_DEBUGGING_INTERNAL_ELF_MEM_IMAGE_H_
|
weight/_dep/abseil-cpp/absl/debugging/internal/examine_stack.cc
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
//
|
| 16 |
+
|
| 17 |
+
#include "absl/debugging/internal/examine_stack.h"
|
| 18 |
+
|
| 19 |
+
#ifndef _WIN32
|
| 20 |
+
#include <unistd.h>
|
| 21 |
+
#endif
|
| 22 |
+
|
| 23 |
+
#include "absl/base/config.h"
|
| 24 |
+
|
| 25 |
+
#ifdef ABSL_HAVE_MMAP
|
| 26 |
+
#include <sys/mman.h>
|
| 27 |
+
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
|
| 28 |
+
#define MAP_ANONYMOUS MAP_ANON
|
| 29 |
+
#endif
|
| 30 |
+
#endif
|
| 31 |
+
|
| 32 |
+
#if defined(__linux__) || defined(__APPLE__)
|
| 33 |
+
#include <sys/ucontext.h>
|
| 34 |
+
#endif
|
| 35 |
+
|
| 36 |
+
#include <csignal>
|
| 37 |
+
#include <cstdio>
|
| 38 |
+
|
| 39 |
+
#include "absl/base/attributes.h"
|
| 40 |
+
#include "absl/base/internal/raw_logging.h"
|
| 41 |
+
#include "absl/base/macros.h"
|
| 42 |
+
#include "absl/debugging/stacktrace.h"
|
| 43 |
+
#include "absl/debugging/symbolize.h"
|
| 44 |
+
|
| 45 |
+
namespace absl {
|
| 46 |
+
ABSL_NAMESPACE_BEGIN
|
| 47 |
+
namespace debugging_internal {
|
| 48 |
+
|
| 49 |
+
namespace {
|
| 50 |
+
constexpr int kDefaultDumpStackFramesLimit = 64;
|
| 51 |
+
// The %p field width for printf() functions is two characters per byte,
|
| 52 |
+
// and two extra for the leading "0x".
|
| 53 |
+
constexpr int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
|
| 54 |
+
|
| 55 |
+
ABSL_CONST_INIT SymbolizeUrlEmitter debug_stack_trace_hook = nullptr;
|
| 56 |
+
|
| 57 |
+
// Async-signal safe mmap allocator.
|
| 58 |
+
void* Allocate(size_t num_bytes) {
|
| 59 |
+
#ifdef ABSL_HAVE_MMAP
|
| 60 |
+
void* p = ::mmap(nullptr, num_bytes, PROT_READ | PROT_WRITE,
|
| 61 |
+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
| 62 |
+
return p == MAP_FAILED ? nullptr : p;
|
| 63 |
+
#else
|
| 64 |
+
(void)num_bytes;
|
| 65 |
+
return nullptr;
|
| 66 |
+
#endif // ABSL_HAVE_MMAP
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
void Deallocate(void* p, size_t size) {
|
| 70 |
+
#ifdef ABSL_HAVE_MMAP
|
| 71 |
+
::munmap(p, size);
|
| 72 |
+
#else
|
| 73 |
+
(void)p;
|
| 74 |
+
(void)size;
|
| 75 |
+
#endif // ABSL_HAVE_MMAP
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
// Print a program counter only.
|
| 79 |
+
void DumpPC(OutputWriter* writer, void* writer_arg, void* const pc,
|
| 80 |
+
const char* const prefix) {
|
| 81 |
+
char buf[100];
|
| 82 |
+
snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, pc);
|
| 83 |
+
writer(buf, writer_arg);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// Print a program counter and the corresponding stack frame size.
|
| 87 |
+
void DumpPCAndFrameSize(OutputWriter* writer, void* writer_arg, void* const pc,
|
| 88 |
+
int framesize, const char* const prefix) {
|
| 89 |
+
char buf[100];
|
| 90 |
+
if (framesize <= 0) {
|
| 91 |
+
snprintf(buf, sizeof(buf), "%s@ %*p (unknown)\n", prefix,
|
| 92 |
+
kPrintfPointerFieldWidth, pc);
|
| 93 |
+
} else {
|
| 94 |
+
snprintf(buf, sizeof(buf), "%s@ %*p %9d\n", prefix,
|
| 95 |
+
kPrintfPointerFieldWidth, pc, framesize);
|
| 96 |
+
}
|
| 97 |
+
writer(buf, writer_arg);
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// Print a program counter and the corresponding symbol.
|
| 101 |
+
void DumpPCAndSymbol(OutputWriter* writer, void* writer_arg, void* const pc,
|
| 102 |
+
const char* const prefix) {
|
| 103 |
+
char tmp[1024];
|
| 104 |
+
const char* symbol = "(unknown)";
|
| 105 |
+
// Symbolizes the previous address of pc because pc may be in the
|
| 106 |
+
// next function. The overrun happens when the function ends with
|
| 107 |
+
// a call to a function annotated noreturn (e.g. CHECK).
|
| 108 |
+
// If symbolization of pc-1 fails, also try pc on the off-chance
|
| 109 |
+
// that we crashed on the first instruction of a function (that
|
| 110 |
+
// actually happens very often for e.g. __restore_rt).
|
| 111 |
+
const uintptr_t prev_pc = reinterpret_cast<uintptr_t>(pc) - 1;
|
| 112 |
+
if (absl::Symbolize(reinterpret_cast<const char*>(prev_pc), tmp,
|
| 113 |
+
sizeof(tmp)) ||
|
| 114 |
+
absl::Symbolize(pc, tmp, sizeof(tmp))) {
|
| 115 |
+
symbol = tmp;
|
| 116 |
+
}
|
| 117 |
+
char buf[1024];
|
| 118 |
+
snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, kPrintfPointerFieldWidth,
|
| 119 |
+
pc, symbol);
|
| 120 |
+
writer(buf, writer_arg);
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
// Print a program counter, its stack frame size, and its symbol name.
|
| 124 |
+
// Note that there is a separate symbolize_pc argument. Return addresses may be
|
| 125 |
+
// at the end of the function, and this allows the caller to back up from pc if
|
| 126 |
+
// appropriate.
|
| 127 |
+
void DumpPCAndFrameSizeAndSymbol(OutputWriter* writer, void* writer_arg,
|
| 128 |
+
void* const pc, void* const symbolize_pc,
|
| 129 |
+
int framesize, const char* const prefix) {
|
| 130 |
+
char tmp[1024];
|
| 131 |
+
const char* symbol = "(unknown)";
|
| 132 |
+
if (absl::Symbolize(symbolize_pc, tmp, sizeof(tmp))) {
|
| 133 |
+
symbol = tmp;
|
| 134 |
+
}
|
| 135 |
+
char buf[1024];
|
| 136 |
+
if (framesize <= 0) {
|
| 137 |
+
snprintf(buf, sizeof(buf), "%s@ %*p (unknown) %s\n", prefix,
|
| 138 |
+
kPrintfPointerFieldWidth, pc, symbol);
|
| 139 |
+
} else {
|
| 140 |
+
snprintf(buf, sizeof(buf), "%s@ %*p %9d %s\n", prefix,
|
| 141 |
+
kPrintfPointerFieldWidth, pc, framesize, symbol);
|
| 142 |
+
}
|
| 143 |
+
writer(buf, writer_arg);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
} // namespace
|
| 147 |
+
|
| 148 |
+
void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook) {
|
| 149 |
+
debug_stack_trace_hook = hook;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
SymbolizeUrlEmitter GetDebugStackTraceHook() { return debug_stack_trace_hook; }
|
| 153 |
+
|
| 154 |
+
// Returns the program counter from signal context, nullptr if
|
| 155 |
+
// unknown. vuc is a ucontext_t*. We use void* to avoid the use of
|
| 156 |
+
// ucontext_t on non-POSIX systems.
|
| 157 |
+
void* GetProgramCounter(void* const vuc) {
|
| 158 |
+
#ifdef __linux__
|
| 159 |
+
if (vuc != nullptr) {
|
| 160 |
+
ucontext_t* context = reinterpret_cast<ucontext_t*>(vuc);
|
| 161 |
+
#if defined(__aarch64__)
|
| 162 |
+
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
| 163 |
+
#elif defined(__alpha__)
|
| 164 |
+
return reinterpret_cast<void*>(context->uc_mcontext.sc_pc);
|
| 165 |
+
#elif defined(__arm__)
|
| 166 |
+
return reinterpret_cast<void*>(context->uc_mcontext.arm_pc);
|
| 167 |
+
#elif defined(__hppa__)
|
| 168 |
+
return reinterpret_cast<void*>(context->uc_mcontext.sc_iaoq[0]);
|
| 169 |
+
#elif defined(__i386__)
|
| 170 |
+
if (14 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
|
| 171 |
+
return reinterpret_cast<void*>(context->uc_mcontext.gregs[14]);
|
| 172 |
+
#elif defined(__ia64__)
|
| 173 |
+
return reinterpret_cast<void*>(context->uc_mcontext.sc_ip);
|
| 174 |
+
#elif defined(__m68k__)
|
| 175 |
+
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
|
| 176 |
+
#elif defined(__mips__)
|
| 177 |
+
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
| 178 |
+
#elif defined(__powerpc64__)
|
| 179 |
+
return reinterpret_cast<void*>(context->uc_mcontext.gp_regs[32]);
|
| 180 |
+
#elif defined(__powerpc__)
|
| 181 |
+
return reinterpret_cast<void*>(context->uc_mcontext.uc_regs->gregs[32]);
|
| 182 |
+
#elif defined(__riscv)
|
| 183 |
+
return reinterpret_cast<void*>(context->uc_mcontext.__gregs[REG_PC]);
|
| 184 |
+
#elif defined(__s390__) && !defined(__s390x__)
|
| 185 |
+
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr & 0x7fffffff);
|
| 186 |
+
#elif defined(__s390__) && defined(__s390x__)
|
| 187 |
+
return reinterpret_cast<void*>(context->uc_mcontext.psw.addr);
|
| 188 |
+
#elif defined(__sh__)
|
| 189 |
+
return reinterpret_cast<void*>(context->uc_mcontext.pc);
|
| 190 |
+
#elif defined(__sparc__) && !defined(__arch64__)
|
| 191 |
+
return reinterpret_cast<void*>(context->uc_mcontext.gregs[19]);
|
| 192 |
+
#elif defined(__sparc__) && defined(__arch64__)
|
| 193 |
+
return reinterpret_cast<void*>(context->uc_mcontext.mc_gregs[19]);
|
| 194 |
+
#elif defined(__x86_64__)
|
| 195 |
+
if (16 < ABSL_ARRAYSIZE(context->uc_mcontext.gregs))
|
| 196 |
+
return reinterpret_cast<void*>(context->uc_mcontext.gregs[16]);
|
| 197 |
+
#elif defined(__e2k__)
|
| 198 |
+
return reinterpret_cast<void*>(context->uc_mcontext.cr0_hi);
|
| 199 |
+
#elif defined(__loongarch__)
|
| 200 |
+
return reinterpret_cast<void*>(context->uc_mcontext.__pc);
|
| 201 |
+
#else
|
| 202 |
+
#error "Undefined Architecture."
|
| 203 |
+
#endif
|
| 204 |
+
}
|
| 205 |
+
#elif defined(__APPLE__)
|
| 206 |
+
if (vuc != nullptr) {
|
| 207 |
+
ucontext_t* signal_ucontext = reinterpret_cast<ucontext_t*>(vuc);
|
| 208 |
+
#if defined(__aarch64__)
|
| 209 |
+
return reinterpret_cast<void*>(
|
| 210 |
+
__darwin_arm_thread_state64_get_pc(signal_ucontext->uc_mcontext->__ss));
|
| 211 |
+
#elif defined(__arm__)
|
| 212 |
+
#if __DARWIN_UNIX03
|
| 213 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__pc);
|
| 214 |
+
#else
|
| 215 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.pc);
|
| 216 |
+
#endif
|
| 217 |
+
#elif defined(__i386__)
|
| 218 |
+
#if __DARWIN_UNIX03
|
| 219 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__eip);
|
| 220 |
+
#else
|
| 221 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.eip);
|
| 222 |
+
#endif
|
| 223 |
+
#elif defined(__x86_64__)
|
| 224 |
+
#if __DARWIN_UNIX03
|
| 225 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->__ss.__rip);
|
| 226 |
+
#else
|
| 227 |
+
return reinterpret_cast<void*>(signal_ucontext->uc_mcontext->ss.rip);
|
| 228 |
+
#endif
|
| 229 |
+
#endif
|
| 230 |
+
}
|
| 231 |
+
#elif defined(__akaros__)
|
| 232 |
+
auto* ctx = reinterpret_cast<struct user_context*>(vuc);
|
| 233 |
+
return reinterpret_cast<void*>(get_user_ctx_pc(ctx));
|
| 234 |
+
#endif
|
| 235 |
+
static_cast<void>(vuc);
|
| 236 |
+
return nullptr;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
|
| 240 |
+
int frame_sizes[], int depth,
|
| 241 |
+
int min_dropped_frames,
|
| 242 |
+
bool symbolize_stacktrace,
|
| 243 |
+
OutputWriter* writer, void* writer_arg) {
|
| 244 |
+
if (pc != nullptr) {
|
| 245 |
+
// We don't know the stack frame size for PC, use 0.
|
| 246 |
+
if (symbolize_stacktrace) {
|
| 247 |
+
DumpPCAndFrameSizeAndSymbol(writer, writer_arg, pc, pc, 0, "PC: ");
|
| 248 |
+
} else {
|
| 249 |
+
DumpPCAndFrameSize(writer, writer_arg, pc, 0, "PC: ");
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
for (int i = 0; i < depth; i++) {
|
| 253 |
+
if (symbolize_stacktrace) {
|
| 254 |
+
// Pass the previous address of pc as the symbol address because pc is a
|
| 255 |
+
// return address, and an overrun may occur when the function ends with a
|
| 256 |
+
// call to a function annotated noreturn (e.g. CHECK). Note that we don't
|
| 257 |
+
// do this for pc above, as the adjustment is only correct for return
|
| 258 |
+
// addresses.
|
| 259 |
+
DumpPCAndFrameSizeAndSymbol(writer, writer_arg, stack[i],
|
| 260 |
+
reinterpret_cast<char*>(stack[i]) - 1,
|
| 261 |
+
frame_sizes[i], " ");
|
| 262 |
+
} else {
|
| 263 |
+
DumpPCAndFrameSize(writer, writer_arg, stack[i], frame_sizes[i], " ");
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
if (min_dropped_frames > 0) {
|
| 267 |
+
char buf[100];
|
| 268 |
+
snprintf(buf, sizeof(buf), " @ ... and at least %d more frames\n",
|
| 269 |
+
min_dropped_frames);
|
| 270 |
+
writer(buf, writer_arg);
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
// Dump current stack trace as directed by writer.
|
| 275 |
+
// Make sure this function is not inlined to avoid skipping too many top frames.
|
| 276 |
+
ABSL_ATTRIBUTE_NOINLINE
|
| 277 |
+
void DumpStackTrace(int min_dropped_frames, int max_num_frames,
|
| 278 |
+
bool symbolize_stacktrace, OutputWriter* writer,
|
| 279 |
+
void* writer_arg) {
|
| 280 |
+
// Print stack trace
|
| 281 |
+
void* stack_buf[kDefaultDumpStackFramesLimit];
|
| 282 |
+
void** stack = stack_buf;
|
| 283 |
+
int num_stack = kDefaultDumpStackFramesLimit;
|
| 284 |
+
size_t allocated_bytes = 0;
|
| 285 |
+
|
| 286 |
+
if (num_stack >= max_num_frames) {
|
| 287 |
+
// User requested fewer frames than we already have space for.
|
| 288 |
+
num_stack = max_num_frames;
|
| 289 |
+
} else {
|
| 290 |
+
const size_t needed_bytes =
|
| 291 |
+
static_cast<size_t>(max_num_frames) * sizeof(stack[0]);
|
| 292 |
+
void* p = Allocate(needed_bytes);
|
| 293 |
+
if (p != nullptr) { // We got the space.
|
| 294 |
+
num_stack = max_num_frames;
|
| 295 |
+
stack = reinterpret_cast<void**>(p);
|
| 296 |
+
allocated_bytes = needed_bytes;
|
| 297 |
+
}
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
int depth = absl::GetStackTrace(stack, num_stack, min_dropped_frames + 1);
|
| 301 |
+
for (int i = 0; i < depth; i++) {
|
| 302 |
+
if (symbolize_stacktrace) {
|
| 303 |
+
DumpPCAndSymbol(writer, writer_arg, stack[static_cast<size_t>(i)],
|
| 304 |
+
" ");
|
| 305 |
+
} else {
|
| 306 |
+
DumpPC(writer, writer_arg, stack[static_cast<size_t>(i)], " ");
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
auto hook = GetDebugStackTraceHook();
|
| 311 |
+
if (hook != nullptr) {
|
| 312 |
+
(*hook)(stack, depth, writer, writer_arg);
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
if (allocated_bytes != 0) Deallocate(stack, allocated_bytes);
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
} // namespace debugging_internal
|
| 319 |
+
ABSL_NAMESPACE_END
|
| 320 |
+
} // namespace absl
|
weight/_dep/abseil-cpp/absl/debugging/internal/examine_stack.h
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
//
|
| 16 |
+
|
| 17 |
+
#ifndef ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
| 18 |
+
#define ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
| 19 |
+
|
| 20 |
+
#include "absl/base/config.h"
|
| 21 |
+
|
| 22 |
+
namespace absl {
|
| 23 |
+
ABSL_NAMESPACE_BEGIN
|
| 24 |
+
namespace debugging_internal {
|
| 25 |
+
|
| 26 |
+
// Type of function used for printing in stack trace dumping, etc.
|
| 27 |
+
// We avoid closures to keep things simple.
|
| 28 |
+
typedef void OutputWriter(const char*, void*);
|
| 29 |
+
|
| 30 |
+
// RegisterDebugStackTraceHook() allows to register a single routine
|
| 31 |
+
// `hook` that is called each time DumpStackTrace() is called.
|
| 32 |
+
// `hook` may be called from a signal handler.
|
| 33 |
+
typedef void (*SymbolizeUrlEmitter)(void* const stack[], int depth,
|
| 34 |
+
OutputWriter* writer, void* writer_arg);
|
| 35 |
+
|
| 36 |
+
// Registration of SymbolizeUrlEmitter for use inside of a signal handler.
|
| 37 |
+
// This is inherently unsafe and must be signal safe code.
|
| 38 |
+
void RegisterDebugStackTraceHook(SymbolizeUrlEmitter hook);
|
| 39 |
+
SymbolizeUrlEmitter GetDebugStackTraceHook();
|
| 40 |
+
|
| 41 |
+
// Returns the program counter from signal context, or nullptr if
|
| 42 |
+
// unknown. `vuc` is a ucontext_t*. We use void* to avoid the use of
|
| 43 |
+
// ucontext_t on non-POSIX systems.
|
| 44 |
+
void* GetProgramCounter(void* const vuc);
|
| 45 |
+
|
| 46 |
+
// Uses `writer` to dump the program counter, stack trace, and stack
|
| 47 |
+
// frame sizes.
|
| 48 |
+
void DumpPCAndFrameSizesAndStackTrace(void* const pc, void* const stack[],
|
| 49 |
+
int frame_sizes[], int depth,
|
| 50 |
+
int min_dropped_frames,
|
| 51 |
+
bool symbolize_stacktrace,
|
| 52 |
+
OutputWriter* writer, void* writer_arg);
|
| 53 |
+
|
| 54 |
+
// Dump current stack trace omitting the topmost `min_dropped_frames` stack
|
| 55 |
+
// frames.
|
| 56 |
+
void DumpStackTrace(int min_dropped_frames, int max_num_frames,
|
| 57 |
+
bool symbolize_stacktrace, OutputWriter* writer,
|
| 58 |
+
void* writer_arg);
|
| 59 |
+
|
| 60 |
+
} // namespace debugging_internal
|
| 61 |
+
ABSL_NAMESPACE_END
|
| 62 |
+
} // namespace absl
|
| 63 |
+
|
| 64 |
+
#endif // ABSL_DEBUGGING_INTERNAL_EXAMINE_STACK_H_
|
weight/_dep/abseil-cpp/absl/debugging/internal/stack_consumption.cc
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
|
| 16 |
+
#include "absl/debugging/internal/stack_consumption.h"
|
| 17 |
+
|
| 18 |
+
#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
|
| 19 |
+
|
| 20 |
+
#include <signal.h>
|
| 21 |
+
#include <string.h>
|
| 22 |
+
#include <sys/mman.h>
|
| 23 |
+
#include <unistd.h>
|
| 24 |
+
|
| 25 |
+
#include "absl/base/attributes.h"
|
| 26 |
+
#include "absl/base/internal/raw_logging.h"
|
| 27 |
+
|
| 28 |
+
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
|
| 29 |
+
#define MAP_ANONYMOUS MAP_ANON
|
| 30 |
+
#endif
|
| 31 |
+
|
| 32 |
+
namespace absl {
|
| 33 |
+
ABSL_NAMESPACE_BEGIN
|
| 34 |
+
namespace debugging_internal {
|
| 35 |
+
namespace {
|
| 36 |
+
|
| 37 |
+
// This code requires that we know the direction in which the stack
|
| 38 |
+
// grows. It is commonly believed that this can be detected by putting
|
| 39 |
+
// a variable on the stack and then passing its address to a function
|
| 40 |
+
// that compares the address of this variable to the address of a
|
| 41 |
+
// variable on the function's own stack. However, this is unspecified
|
| 42 |
+
// behavior in C++: If two pointers p and q of the same type point to
|
| 43 |
+
// different objects that are not members of the same object or
|
| 44 |
+
// elements of the same array or to different functions, or if only
|
| 45 |
+
// one of them is null, the results of p<q, p>q, p<=q, and p>=q are
|
| 46 |
+
// unspecified. Therefore, instead we hardcode the direction of the
|
| 47 |
+
// stack on platforms we know about.
|
| 48 |
+
#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
|
| 49 |
+
defined(__aarch64__) || defined(__riscv)
|
| 50 |
+
constexpr bool kStackGrowsDown = true;
|
| 51 |
+
#else
|
| 52 |
+
#error Need to define kStackGrowsDown
|
| 53 |
+
#endif
|
| 54 |
+
|
| 55 |
+
// To measure the stack footprint of some code, we create a signal handler
|
| 56 |
+
// (for SIGUSR2 say) that exercises this code on an alternate stack. This
|
| 57 |
+
// alternate stack is initialized to some known pattern (0x55, 0x55, 0x55,
|
| 58 |
+
// ...). We then self-send this signal, and after the signal handler returns,
|
| 59 |
+
// look at the alternate stack buffer to see what portion has been touched.
|
| 60 |
+
//
|
| 61 |
+
// This trick gives us the the stack footprint of the signal handler. But the
|
| 62 |
+
// signal handler, even before the code for it is exercised, consumes some
|
| 63 |
+
// stack already. We however only want the stack usage of the code inside the
|
| 64 |
+
// signal handler. To measure this accurately, we install two signal handlers:
|
| 65 |
+
// one that does nothing and just returns, and the user-provided signal
|
| 66 |
+
// handler. The difference between the stack consumption of these two signals
|
| 67 |
+
// handlers should give us the stack foorprint of interest.
|
| 68 |
+
|
| 69 |
+
void EmptySignalHandler(int) {}
|
| 70 |
+
|
| 71 |
+
// This is arbitrary value, and could be increase further, at the cost of
|
| 72 |
+
// memset()ting it all to known sentinel value.
|
| 73 |
+
constexpr int kAlternateStackSize = 64 << 10; // 64KiB
|
| 74 |
+
|
| 75 |
+
constexpr int kSafetyMargin = 32;
|
| 76 |
+
constexpr char kAlternateStackFillValue = 0x55;
|
| 77 |
+
|
| 78 |
+
// These helper functions look at the alternate stack buffer, and figure
|
| 79 |
+
// out what portion of this buffer has been touched - this is the stack
|
| 80 |
+
// consumption of the signal handler running on this alternate stack.
|
| 81 |
+
// This function will return -1 if the alternate stack buffer has not been
|
| 82 |
+
// touched. It will abort the program if the buffer has overflowed or is about
|
| 83 |
+
// to overflow.
|
| 84 |
+
int GetStackConsumption(const void* const altstack) {
|
| 85 |
+
const char* begin;
|
| 86 |
+
int increment;
|
| 87 |
+
if (kStackGrowsDown) {
|
| 88 |
+
begin = reinterpret_cast<const char*>(altstack);
|
| 89 |
+
increment = 1;
|
| 90 |
+
} else {
|
| 91 |
+
begin = reinterpret_cast<const char*>(altstack) + kAlternateStackSize - 1;
|
| 92 |
+
increment = -1;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
for (int usage_count = kAlternateStackSize; usage_count > 0; --usage_count) {
|
| 96 |
+
if (*begin != kAlternateStackFillValue) {
|
| 97 |
+
ABSL_RAW_CHECK(usage_count <= kAlternateStackSize - kSafetyMargin,
|
| 98 |
+
"Buffer has overflowed or is about to overflow");
|
| 99 |
+
return usage_count;
|
| 100 |
+
}
|
| 101 |
+
begin += increment;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
ABSL_RAW_LOG(FATAL, "Unreachable code");
|
| 105 |
+
return -1;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
} // namespace
|
| 109 |
+
|
| 110 |
+
int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) {
|
| 111 |
+
// The alt-signal-stack cannot be heap allocated because there is a
|
| 112 |
+
// bug in glibc-2.2 where some signal handler setup code looks at the
|
| 113 |
+
// current stack pointer to figure out what thread is currently running.
|
| 114 |
+
// Therefore, the alternate stack must be allocated from the main stack
|
| 115 |
+
// itself.
|
| 116 |
+
void* altstack = mmap(nullptr, kAlternateStackSize, PROT_READ | PROT_WRITE,
|
| 117 |
+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
| 118 |
+
ABSL_RAW_CHECK(altstack != MAP_FAILED, "mmap() failed");
|
| 119 |
+
|
| 120 |
+
// Set up the alt-signal-stack (and save the older one).
|
| 121 |
+
stack_t sigstk;
|
| 122 |
+
memset(&sigstk, 0, sizeof(sigstk));
|
| 123 |
+
sigstk.ss_sp = altstack;
|
| 124 |
+
sigstk.ss_size = kAlternateStackSize;
|
| 125 |
+
sigstk.ss_flags = 0;
|
| 126 |
+
stack_t old_sigstk;
|
| 127 |
+
memset(&old_sigstk, 0, sizeof(old_sigstk));
|
| 128 |
+
ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0,
|
| 129 |
+
"sigaltstack() failed");
|
| 130 |
+
|
| 131 |
+
// Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
|
| 132 |
+
struct sigaction sa;
|
| 133 |
+
memset(&sa, 0, sizeof(sa));
|
| 134 |
+
struct sigaction old_sa1, old_sa2;
|
| 135 |
+
sigemptyset(&sa.sa_mask);
|
| 136 |
+
sa.sa_flags = SA_ONSTACK;
|
| 137 |
+
|
| 138 |
+
// SIGUSR1 maps to EmptySignalHandler.
|
| 139 |
+
sa.sa_handler = EmptySignalHandler;
|
| 140 |
+
ABSL_RAW_CHECK(sigaction(SIGUSR1, &sa, &old_sa1) == 0, "sigaction() failed");
|
| 141 |
+
|
| 142 |
+
// SIGUSR2 maps to signal_handler.
|
| 143 |
+
sa.sa_handler = signal_handler;
|
| 144 |
+
ABSL_RAW_CHECK(sigaction(SIGUSR2, &sa, &old_sa2) == 0, "sigaction() failed");
|
| 145 |
+
|
| 146 |
+
// Send SIGUSR1 signal and measure the stack consumption of the empty
|
| 147 |
+
// signal handler.
|
| 148 |
+
// The first signal might use more stack space. Run once and ignore the
|
| 149 |
+
// results to get that out of the way.
|
| 150 |
+
ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
|
| 151 |
+
|
| 152 |
+
memset(altstack, kAlternateStackFillValue, kAlternateStackSize);
|
| 153 |
+
ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed");
|
| 154 |
+
int base_stack_consumption = GetStackConsumption(altstack);
|
| 155 |
+
|
| 156 |
+
// Send SIGUSR2 signal and measure the stack consumption of signal_handler.
|
| 157 |
+
ABSL_RAW_CHECK(kill(getpid(), SIGUSR2) == 0, "kill() failed");
|
| 158 |
+
int signal_handler_stack_consumption = GetStackConsumption(altstack);
|
| 159 |
+
|
| 160 |
+
// Now restore the old alt-signal-stack and signal handlers.
|
| 161 |
+
if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 &&
|
| 162 |
+
(old_sigstk.ss_flags & SS_DISABLE)) {
|
| 163 |
+
// https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070
|
| 164 |
+
// The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some
|
| 165 |
+
// versions of musl have a bug that rejects ss_size==0. Work around this by
|
| 166 |
+
// setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel
|
| 167 |
+
// when SS_DISABLE is set.
|
| 168 |
+
old_sigstk.ss_size = static_cast<size_t>(MINSIGSTKSZ);
|
| 169 |
+
}
|
| 170 |
+
ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0,
|
| 171 |
+
"sigaltstack() failed");
|
| 172 |
+
ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0,
|
| 173 |
+
"sigaction() failed");
|
| 174 |
+
ABSL_RAW_CHECK(sigaction(SIGUSR2, &old_sa2, nullptr) == 0,
|
| 175 |
+
"sigaction() failed");
|
| 176 |
+
|
| 177 |
+
ABSL_RAW_CHECK(munmap(altstack, kAlternateStackSize) == 0, "munmap() failed");
|
| 178 |
+
if (signal_handler_stack_consumption != -1 && base_stack_consumption != -1) {
|
| 179 |
+
return signal_handler_stack_consumption - base_stack_consumption;
|
| 180 |
+
}
|
| 181 |
+
return -1;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
} // namespace debugging_internal
|
| 185 |
+
ABSL_NAMESPACE_END
|
| 186 |
+
} // namespace absl
|
| 187 |
+
|
| 188 |
+
#else
|
| 189 |
+
|
| 190 |
+
// https://github.com/abseil/abseil-cpp/issues/1465
|
| 191 |
+
// CMake builds on Apple platforms error when libraries are empty.
|
| 192 |
+
// Our CMake configuration can avoid this error on header-only libraries,
|
| 193 |
+
// but since this library is conditionally empty, including a single
|
| 194 |
+
// variable is an easy workaround.
|
| 195 |
+
#ifdef __APPLE__
|
| 196 |
+
namespace absl {
|
| 197 |
+
ABSL_NAMESPACE_BEGIN
|
| 198 |
+
namespace debugging_internal {
|
| 199 |
+
extern const char kAvoidEmptyStackConsumptionLibraryWarning;
|
| 200 |
+
const char kAvoidEmptyStackConsumptionLibraryWarning = 0;
|
| 201 |
+
} // namespace debugging_internal
|
| 202 |
+
ABSL_NAMESPACE_END
|
| 203 |
+
} // namespace absl
|
| 204 |
+
#endif // __APPLE__
|
| 205 |
+
|
| 206 |
+
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
|
weight/_dep/abseil-cpp/absl/debugging/internal/stack_consumption.h
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2018 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
|
| 16 |
+
// Helper function for measuring stack consumption of signal handlers.
|
| 17 |
+
|
| 18 |
+
#ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
|
| 19 |
+
#define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
|
| 20 |
+
|
| 21 |
+
#include "absl/base/config.h"
|
| 22 |
+
|
| 23 |
+
// The code in this module is not portable.
|
| 24 |
+
// Use this feature test macro to detect its availability.
|
| 25 |
+
#ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
|
| 26 |
+
#error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly
|
| 27 |
+
#elif !defined(__APPLE__) && !defined(_WIN32) && \
|
| 28 |
+
(defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || \
|
| 29 |
+
defined(__aarch64__) || defined(__riscv))
|
| 30 |
+
#define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1
|
| 31 |
+
|
| 32 |
+
namespace absl {
|
| 33 |
+
ABSL_NAMESPACE_BEGIN
|
| 34 |
+
namespace debugging_internal {
|
| 35 |
+
|
| 36 |
+
// Returns the stack consumption in bytes for the code exercised by
|
| 37 |
+
// signal_handler. To measure stack consumption, signal_handler is registered
|
| 38 |
+
// as a signal handler, so the code that it exercises must be async-signal
|
| 39 |
+
// safe. The argument of signal_handler is an implementation detail of signal
|
| 40 |
+
// handlers and should ignored by the code for signal_handler. Use global
|
| 41 |
+
// variables to pass information between your test code and signal_handler.
|
| 42 |
+
int GetSignalHandlerStackConsumption(void (*signal_handler)(int));
|
| 43 |
+
|
| 44 |
+
} // namespace debugging_internal
|
| 45 |
+
ABSL_NAMESPACE_END
|
| 46 |
+
} // namespace absl
|
| 47 |
+
|
| 48 |
+
#endif // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
|
| 49 |
+
|
| 50 |
+
#endif // ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
|