| @@ -1,6 +1,8 @@ | |||
| tclap/** linguist-vendored | |||
| * text=auto | |||
| tclap/** linguist-vendored | |||
| spdlog/** linguist-vendored | |||
| grpc/** linguist-vendored | |||
| proto/** linguist-generated | |||
| @@ -499,3 +499,4 @@ CTestTestfile.cmake | |||
| _deps | |||
| /build/ | |||
| /lib/ | |||
| @@ -90,10 +90,15 @@ | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <LanguageStandard>stdcpp17</LanguageStandard> | |||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | |||
| <AdditionalIncludeDirectories>..\spdlog\include;..\tclap\include;..\grpc\include;..\proto;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions> | |||
| <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Console</SubSystem> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| <AdditionalLibraryDirectories>..\lib\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | |||
| <AdditionalDependencies>absl_bad_any_cast_impl.lib;absl_bad_optional_access.lib;absl_bad_variant_access.lib;absl_base.lib;absl_city.lib;absl_civil_time.lib;absl_cord.lib;absl_cordz_functions.lib;absl_cordz_handle.lib;absl_cordz_info.lib;absl_cordz_sample_token.lib;absl_cord_internal.lib;absl_debugging_internal.lib;absl_demangle_internal.lib;absl_examine_stack.lib;absl_exponential_biased.lib;absl_failure_signal_handler.lib;absl_flags.lib;absl_flags_commandlineflag.lib;absl_flags_commandlineflag_internal.lib;absl_flags_config.lib;absl_flags_internal.lib;absl_flags_marshalling.lib;absl_flags_parse.lib;absl_flags_private_handle_accessor.lib;absl_flags_program_name.lib;absl_flags_reflection.lib;absl_flags_usage.lib;absl_flags_usage_internal.lib;absl_graphcycles_internal.lib;absl_hash.lib;absl_hashtablez_sampler.lib;absl_int128.lib;absl_leak_check.lib;absl_log_severity.lib;absl_low_level_hash.lib;absl_malloc_internal.lib;absl_periodic_sampler.lib;absl_random_distributions.lib;absl_random_internal_distribution_test_util.lib;absl_random_internal_platform.lib;absl_random_internal_pool_urbg.lib;absl_random_internal_randen.lib;absl_random_internal_randen_hwaes.lib;absl_random_internal_randen_hwaes_impl.lib;absl_random_internal_randen_slow.lib;absl_random_internal_seed_material.lib;absl_random_seed_gen_exception.lib;absl_random_seed_sequences.lib;absl_raw_hash_set.lib;absl_raw_logging_internal.lib;absl_scoped_set_env.lib;absl_spinlock_wait.lib;absl_stacktrace.lib;absl_status.lib;absl_statusor.lib;absl_strerror.lib;absl_strings.lib;absl_strings_internal.lib;absl_str_format_internal.lib;absl_symbolize.lib;absl_synchronization.lib;absl_throw_delegate.lib;absl_time.lib;absl_time_zone.lib;address_sorting.lib;cares.lib;descriptor_upb_proto.lib;gpr.lib;grpc++.lib;grpc++_alts.lib;grpc++_error_details.lib;grpc++_unsecure.lib;grpc.lib;grpc_plugin_support.lib;grpc_unsecure.lib;libcrypto.lib;libprotobuf-lited.lib;libprotobufd.lib;libprotocd.lib;libssl.lib;re2.lib;upb.lib;upb_collections.lib;upb_extension_registry.lib;upb_fastdecode.lib;upb_json.lib;upb_mini_table.lib;upb_reflection.lib;upb_textformat.lib;upb_utf8_range.lib;zlibd.lib;Ws2_32.lib;Crypt32.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | |||
| @@ -106,12 +111,17 @@ | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <LanguageStandard>stdcpp17</LanguageStandard> | |||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | |||
| <AdditionalIncludeDirectories>..\spdlog\include;..\tclap\include;..\grpc\include;..\proto;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions> | |||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Console</SubSystem> | |||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
| <OptimizeReferences>true</OptimizeReferences> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| <AdditionalDependencies>absl_bad_any_cast_impl.lib;absl_bad_optional_access.lib;absl_bad_variant_access.lib;absl_base.lib;absl_city.lib;absl_civil_time.lib;absl_cord.lib;absl_cordz_functions.lib;absl_cordz_handle.lib;absl_cordz_info.lib;absl_cordz_sample_token.lib;absl_cord_internal.lib;absl_debugging_internal.lib;absl_demangle_internal.lib;absl_examine_stack.lib;absl_exponential_biased.lib;absl_failure_signal_handler.lib;absl_flags.lib;absl_flags_commandlineflag.lib;absl_flags_commandlineflag_internal.lib;absl_flags_config.lib;absl_flags_internal.lib;absl_flags_marshalling.lib;absl_flags_parse.lib;absl_flags_private_handle_accessor.lib;absl_flags_program_name.lib;absl_flags_reflection.lib;absl_flags_usage.lib;absl_flags_usage_internal.lib;absl_graphcycles_internal.lib;absl_hash.lib;absl_hashtablez_sampler.lib;absl_int128.lib;absl_leak_check.lib;absl_log_severity.lib;absl_low_level_hash.lib;absl_malloc_internal.lib;absl_periodic_sampler.lib;absl_random_distributions.lib;absl_random_internal_distribution_test_util.lib;absl_random_internal_platform.lib;absl_random_internal_pool_urbg.lib;absl_random_internal_randen.lib;absl_random_internal_randen_hwaes.lib;absl_random_internal_randen_hwaes_impl.lib;absl_random_internal_randen_slow.lib;absl_random_internal_seed_material.lib;absl_random_seed_gen_exception.lib;absl_random_seed_sequences.lib;absl_raw_hash_set.lib;absl_raw_logging_internal.lib;absl_scoped_set_env.lib;absl_spinlock_wait.lib;absl_stacktrace.lib;absl_status.lib;absl_statusor.lib;absl_strerror.lib;absl_strings.lib;absl_strings_internal.lib;absl_str_format_internal.lib;absl_symbolize.lib;absl_synchronization.lib;absl_throw_delegate.lib;absl_time.lib;absl_time_zone.lib;address_sorting.lib;cares.lib;descriptor_upb_proto.lib;gpr.lib;grpc++.lib;grpc++_alts.lib;grpc++_error_details.lib;grpc++_unsecure.lib;grpc.lib;grpc_plugin_support.lib;grpc_unsecure.lib;libcrypto.lib;libprotobuf-lite.lib;libprotobuf.lib;libprotoc.lib;libssl.lib;re2.lib;upb.lib;upb_collections.lib;upb_extension_registry.lib;upb_fastdecode.lib;upb_json.lib;upb_mini_table.lib;upb_reflection.lib;upb_textformat.lib;upb_utf8_range.lib;zlib.lib;Ws2_32.lib;Crypt32.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> | |||
| <AdditionalLibraryDirectories>..\lib\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
| @@ -122,10 +132,15 @@ | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <LanguageStandard>stdcpp17</LanguageStandard> | |||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | |||
| <AdditionalIncludeDirectories>..\spdlog\include;..\tclap\include;..\grpc\include;..\proto;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions> | |||
| <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Console</SubSystem> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| <AdditionalLibraryDirectories>..\lib\debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | |||
| <AdditionalDependencies>absl_bad_any_cast_impl.lib;absl_bad_optional_access.lib;absl_bad_variant_access.lib;absl_base.lib;absl_city.lib;absl_civil_time.lib;absl_cord.lib;absl_cordz_functions.lib;absl_cordz_handle.lib;absl_cordz_info.lib;absl_cordz_sample_token.lib;absl_cord_internal.lib;absl_debugging_internal.lib;absl_demangle_internal.lib;absl_examine_stack.lib;absl_exponential_biased.lib;absl_failure_signal_handler.lib;absl_flags.lib;absl_flags_commandlineflag.lib;absl_flags_commandlineflag_internal.lib;absl_flags_config.lib;absl_flags_internal.lib;absl_flags_marshalling.lib;absl_flags_parse.lib;absl_flags_private_handle_accessor.lib;absl_flags_program_name.lib;absl_flags_reflection.lib;absl_flags_usage.lib;absl_flags_usage_internal.lib;absl_graphcycles_internal.lib;absl_hash.lib;absl_hashtablez_sampler.lib;absl_int128.lib;absl_leak_check.lib;absl_log_severity.lib;absl_low_level_hash.lib;absl_malloc_internal.lib;absl_periodic_sampler.lib;absl_random_distributions.lib;absl_random_internal_distribution_test_util.lib;absl_random_internal_platform.lib;absl_random_internal_pool_urbg.lib;absl_random_internal_randen.lib;absl_random_internal_randen_hwaes.lib;absl_random_internal_randen_hwaes_impl.lib;absl_random_internal_randen_slow.lib;absl_random_internal_seed_material.lib;absl_random_seed_gen_exception.lib;absl_random_seed_sequences.lib;absl_raw_hash_set.lib;absl_raw_logging_internal.lib;absl_scoped_set_env.lib;absl_spinlock_wait.lib;absl_stacktrace.lib;absl_status.lib;absl_statusor.lib;absl_strerror.lib;absl_strings.lib;absl_strings_internal.lib;absl_str_format_internal.lib;absl_symbolize.lib;absl_synchronization.lib;absl_throw_delegate.lib;absl_time.lib;absl_time_zone.lib;address_sorting.lib;cares.lib;descriptor_upb_proto.lib;gpr.lib;grpc++.lib;grpc++_alts.lib;grpc++_error_details.lib;grpc++_unsecure.lib;grpc.lib;grpc_plugin_support.lib;grpc_unsecure.lib;libcrypto.lib;libprotobuf-lited.lib;libprotobufd.lib;libprotocd.lib;libssl.lib;re2.lib;upb.lib;upb_collections.lib;upb_extension_registry.lib;upb_fastdecode.lib;upb_json.lib;upb_mini_table.lib;upb_reflection.lib;upb_textformat.lib;upb_utf8_range.lib;zlibd.lib;Ws2_32.lib;Crypt32.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
| @@ -138,15 +153,47 @@ | |||
| <ConformanceMode>true</ConformanceMode> | |||
| <LanguageStandard>stdcpp17</LanguageStandard> | |||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | |||
| <AdditionalIncludeDirectories>..\spdlog\include;..\tclap\include;..\grpc\include;..\proto;include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <AdditionalOptions>/source-charset:utf-8 %(AdditionalOptions)</AdditionalOptions> | |||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | |||
| </ClCompile> | |||
| <Link> | |||
| <SubSystem>Console</SubSystem> | |||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
| <OptimizeReferences>true</OptimizeReferences> | |||
| <GenerateDebugInformation>true</GenerateDebugInformation> | |||
| <AdditionalDependencies>absl_bad_any_cast_impl.lib;absl_bad_optional_access.lib;absl_bad_variant_access.lib;absl_base.lib;absl_city.lib;absl_civil_time.lib;absl_cord.lib;absl_cordz_functions.lib;absl_cordz_handle.lib;absl_cordz_info.lib;absl_cordz_sample_token.lib;absl_cord_internal.lib;absl_debugging_internal.lib;absl_demangle_internal.lib;absl_examine_stack.lib;absl_exponential_biased.lib;absl_failure_signal_handler.lib;absl_flags.lib;absl_flags_commandlineflag.lib;absl_flags_commandlineflag_internal.lib;absl_flags_config.lib;absl_flags_internal.lib;absl_flags_marshalling.lib;absl_flags_parse.lib;absl_flags_private_handle_accessor.lib;absl_flags_program_name.lib;absl_flags_reflection.lib;absl_flags_usage.lib;absl_flags_usage_internal.lib;absl_graphcycles_internal.lib;absl_hash.lib;absl_hashtablez_sampler.lib;absl_int128.lib;absl_leak_check.lib;absl_log_severity.lib;absl_low_level_hash.lib;absl_malloc_internal.lib;absl_periodic_sampler.lib;absl_random_distributions.lib;absl_random_internal_distribution_test_util.lib;absl_random_internal_platform.lib;absl_random_internal_pool_urbg.lib;absl_random_internal_randen.lib;absl_random_internal_randen_hwaes.lib;absl_random_internal_randen_hwaes_impl.lib;absl_random_internal_randen_slow.lib;absl_random_internal_seed_material.lib;absl_random_seed_gen_exception.lib;absl_random_seed_sequences.lib;absl_raw_hash_set.lib;absl_raw_logging_internal.lib;absl_scoped_set_env.lib;absl_spinlock_wait.lib;absl_stacktrace.lib;absl_status.lib;absl_statusor.lib;absl_strerror.lib;absl_strings.lib;absl_strings_internal.lib;absl_str_format_internal.lib;absl_symbolize.lib;absl_synchronization.lib;absl_throw_delegate.lib;absl_time.lib;absl_time_zone.lib;address_sorting.lib;cares.lib;descriptor_upb_proto.lib;gpr.lib;grpc++.lib;grpc++_alts.lib;grpc++_error_details.lib;grpc++_unsecure.lib;grpc.lib;grpc_plugin_support.lib;grpc_unsecure.lib;libcrypto.lib;libprotobuf-lite.lib;libprotobuf.lib;libprotoc.lib;libssl.lib;re2.lib;upb.lib;upb_collections.lib;upb_extension_registry.lib;upb_fastdecode.lib;upb_json.lib;upb_mini_table.lib;upb_reflection.lib;upb_textformat.lib;upb_utf8_range.lib;zlib.lib;Ws2_32.lib;Crypt32.lib;Iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> | |||
| <AdditionalLibraryDirectories>..\lib\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> | |||
| </Link> | |||
| </ItemDefinitionGroup> | |||
| <ItemGroup> | |||
| <ClCompile Include="..\proto\Message2Clients.pb.cc" /> | |||
| <ClCompile Include="..\proto\Message2Server.pb.cc" /> | |||
| <ClCompile Include="..\proto\MessageType.pb.cc" /> | |||
| <ClCompile Include="..\proto\Services.grpc.pb.cc" /> | |||
| <ClCompile Include="..\proto\Services.pb.cc" /> | |||
| <ClCompile Include="src\AI.cpp" /> | |||
| <ClCompile Include="src\API.cpp" /> | |||
| <ClCompile Include="src\Communication.cpp" /> | |||
| <ClCompile Include="src\DebugAPI.cpp" /> | |||
| <ClCompile Include="src\logic.cpp" /> | |||
| <ClCompile Include="src\main.cpp" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="..\proto\Message2Clients.pb.h" /> | |||
| <ClInclude Include="..\proto\Message2Server.pb.h" /> | |||
| <ClInclude Include="..\proto\MessageType.pb.h" /> | |||
| <ClInclude Include="..\proto\Services.grpc.pb.h" /> | |||
| <ClInclude Include="..\proto\Services.pb.h" /> | |||
| <ClInclude Include="include\AI.h" /> | |||
| <ClInclude Include="include\API.h" /> | |||
| <ClInclude Include="include\Communication.h" /> | |||
| <ClInclude Include="include\ConcurrentQueue.hpp" /> | |||
| <ClInclude Include="include\constants.h" /> | |||
| <ClInclude Include="include\logic.h" /> | |||
| <ClInclude Include="include\state.h" /> | |||
| <ClInclude Include="include\structures.h" /> | |||
| <ClInclude Include="include\utils.hpp" /> | |||
| </ItemGroup> | |||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
| <ImportGroup Label="ExtensionTargets"> | |||
| @@ -13,5 +13,90 @@ | |||
| <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | |||
| <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | |||
| </Filter> | |||
| <Filter Include="源文件\proto"> | |||
| <UniqueIdentifier>{6aeaa732-c471-4ec4-9de4-1e6f8acc7e92}</UniqueIdentifier> | |||
| </Filter> | |||
| <Filter Include="头文件\proto"> | |||
| <UniqueIdentifier>{3de9b3fe-8273-490b-b433-5fde647f1a60}</UniqueIdentifier> | |||
| </Filter> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClCompile Include="src\AI.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="src\API.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="src\Communication.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="src\DebugAPI.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="src\logic.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="src\main.cpp"> | |||
| <Filter>源文件</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\proto\Message2Clients.pb.cc"> | |||
| <Filter>源文件\proto</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\proto\Message2Server.pb.cc"> | |||
| <Filter>源文件\proto</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\proto\MessageType.pb.cc"> | |||
| <Filter>源文件\proto</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\proto\Services.grpc.pb.cc"> | |||
| <Filter>源文件\proto</Filter> | |||
| </ClCompile> | |||
| <ClCompile Include="..\proto\Services.pb.cc"> | |||
| <Filter>源文件\proto</Filter> | |||
| </ClCompile> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ClInclude Include="include\AI.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\API.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\Communication.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\ConcurrentQueue.hpp"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\constants.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\logic.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\state.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\structures.h"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="include\utils.hpp"> | |||
| <Filter>头文件</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\proto\Message2Clients.pb.h"> | |||
| <Filter>头文件\proto</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\proto\Message2Server.pb.h"> | |||
| <Filter>头文件\proto</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\proto\MessageType.pb.h"> | |||
| <Filter>头文件\proto</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\proto\Services.grpc.pb.h"> | |||
| <Filter>头文件\proto</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\proto\Services.pb.h"> | |||
| <Filter>头文件\proto</Filter> | |||
| </ClInclude> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -44,7 +44,7 @@ namespace Constants | |||
| SCCI int basicStudentAlertnessRadius = 15 * numOfGridPerCell; | |||
| SCCI int basicTrickerAlertnessRadius = 17 * numOfGridPerCell; | |||
| SCCI int basicStudentViewRange = 10 * numOfGridPerCell; | |||
| SCCI int basicTrickerViewRange = 15 * numOfGridPerCell; | |||
| SCCI int basicTrickerViewRange = 13 * numOfGridPerCell; | |||
| SCCI int PinningDownRange = 5 * numOfGridPerCell; | |||
| SCCI int maxNumOfProp = 3; // 人物道具栏容量 | |||
| @@ -218,8 +218,13 @@ namespace Constants | |||
| SCCI int timeOfStunnedWhenJumpyDumpty = 3070; | |||
| SCCI double addedTimeOfSpeedWhenInspire = 0.6; | |||
| SCCI double addedTimeOfSpeedWhenInspire = 1.6; | |||
| SCCI int timeOfAddingSpeedWhenInspire = 6000; | |||
| SCCI int addHpWhenEncourage = basicHp / 4; | |||
| SCCI int checkIntervalWhenShowTime = 200; | |||
| SCCI int addAddictionPer100msWhenShowTime = 300; | |||
| struct CanBeginToCharge | |||
| { | |||
| SCCI int skillCD = commonSkillCD * 2; | |||
| @@ -42,12 +42,12 @@ private: | |||
| std::unique_ptr<Communication> pComm; | |||
| // ID、阵营记录 | |||
| int64_t playerID; | |||
| THUAI6::PlayerType playerType; | |||
| int64_t playerID; | |||
| // 类型记录 | |||
| THUAI6::StudentType studentType; | |||
| THUAI6::TrickerType trickerType; | |||
| THUAI6::StudentType studentType; | |||
| // GUID信息 | |||
| std::vector<int64_t> playerGUIDs; | |||
| @@ -5,6 +5,7 @@ | |||
| #include <vector> | |||
| #include <array> | |||
| #include <map> | |||
| #include <memory> | |||
| #include "structures.h" | |||
| @@ -5,6 +5,8 @@ | |||
| #include <cstdint> | |||
| #include <array> | |||
| #include <map> | |||
| #include <vector> | |||
| #include <string> | |||
| namespace THUAI6 | |||
| { | |||
| @@ -175,7 +177,7 @@ namespace THUAI6 | |||
| int32_t viewRange; // 视野范围 | |||
| int64_t playerID; // 玩家ID | |||
| int64_t guid; // 全局唯一ID | |||
| int16_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||
| int32_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||
| int32_t score; // 分数 | |||
| double facingDirection; // 朝向 | |||
| @@ -5,6 +5,8 @@ | |||
| #include <cstdint> | |||
| #include <cmath> | |||
| #include <map> | |||
| #include <vector> | |||
| #include "Message2Clients.pb.h" | |||
| #include "Message2Server.pb.h" | |||
| #include "MessageType.pb.h" | |||
| @@ -21,6 +23,11 @@ namespace AssistFunction | |||
| return grid / numOfGridPerCell; | |||
| } | |||
| [[nodiscard]] constexpr inline int GridToCell(double grid) noexcept | |||
| { | |||
| return int(grid) / numOfGridPerCell; | |||
| } | |||
| inline bool HaveView(int viewRange, int x, int y, int newX, int newY, std::vector<std::vector<THUAI6::PlaceType>>& map) | |||
| { | |||
| int deltaX = newX - x; | |||
| @@ -417,7 +424,7 @@ namespace THUAI62Proto | |||
| return playerMsg; | |||
| } | |||
| inline protobuf::IDMsg THUAI62ProtobufID(int playerID) | |||
| inline protobuf::IDMsg THUAI62ProtobufID(int64_t playerID) | |||
| { | |||
| protobuf::IDMsg idMsg; | |||
| idMsg.set_player_id(playerID); | |||
| @@ -19,7 +19,6 @@ bool Communication::Move(int64_t time, double angle, int64_t playerID) | |||
| protobuf::MoveRes moveResult; | |||
| ClientContext context; | |||
| auto request = THUAI62Proto::THUAI62ProtobufMove(time, angle, playerID); | |||
| std::cout << "Move request sent" << std::endl; | |||
| auto status = THUAI6Stub->Move(&context, request, &moveResult); | |||
| if (status.ok()) | |||
| return moveResult.act_success(); | |||
| @@ -84,7 +84,7 @@ std::vector<std::vector<THUAI6::PlaceType>> Logic::GetFullMap() const | |||
| THUAI6::PlaceType Logic::GetPlaceType(int32_t cellX, int32_t cellY) const | |||
| { | |||
| std::unique_lock<std::mutex> lock(mtxState); | |||
| if (cellX < 0 || cellX >= currentState->gameMap.size() || cellY < 0 || cellY >= currentState->gameMap[0].size()) | |||
| if (cellX < 0 || uint64_t(cellX) >= currentState->gameMap.size() || cellY < 0 || uint64_t(cellY) >= currentState->gameMap[0].size()) | |||
| { | |||
| logger->warn("Invalid position!"); | |||
| return THUAI6::PlaceType::NullPlaceType; | |||
| @@ -69,7 +69,6 @@ int THUAI6Main(int argc, char** argv, CreateAIFunc AIBuilder) | |||
| std::cerr << "Parsing error: " << e.error() << " for arg " << e.argId() << std::endl; | |||
| return 1; | |||
| } | |||
| std::cout << file << std::endl; | |||
| try | |||
| { | |||
| Logic logic(playerType, pID, trickerType, studentType); | |||
| @@ -0,0 +1,31 @@ | |||
| | |||
| Microsoft Visual Studio Solution File, Format Version 12.00 | |||
| # Visual Studio Version 17 | |||
| VisualStudioVersion = 17.5.33424.131 | |||
| MinimumVisualStudioVersion = 10.0.40219.1 | |||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "API", "API\API.vcxproj", "{E1983CDE-00FA-474B-92DA-F4964660D7BA}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|x64 = Debug|x64 | |||
| Debug|x86 = Debug|x86 | |||
| Release|x64 = Release|x64 | |||
| Release|x86 = Release|x86 | |||
| EndGlobalSection | |||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Debug|x64.ActiveCfg = Debug|x64 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Debug|x64.Build.0 = Debug|x64 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Debug|x86.ActiveCfg = Debug|Win32 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Debug|x86.Build.0 = Debug|Win32 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Release|x64.ActiveCfg = Release|x64 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Release|x64.Build.0 = Release|x64 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Release|x86.ActiveCfg = Release|Win32 | |||
| {E1983CDE-00FA-474B-92DA-F4964660D7BA}.Release|x86.Build.0 = Release|Win32 | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| EndGlobalSection | |||
| GlobalSection(ExtensibilityGlobals) = postSolution | |||
| SolutionGuid = {5F032F3C-43BF-4F8F-B7F8-72A7354CF4A9} | |||
| EndGlobalSection | |||
| EndGlobal | |||
| @@ -0,0 +1,610 @@ | |||
| Apache License | |||
| Version 2.0, January 2004 | |||
| http://www.apache.org/licenses/ | |||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
| 1. Definitions. | |||
| "License" shall mean the terms and conditions for use, reproduction, | |||
| and distribution as defined by Sections 1 through 9 of this document. | |||
| "Licensor" shall mean the copyright owner or entity authorized by | |||
| the copyright owner that is granting the License. | |||
| "Legal Entity" shall mean the union of the acting entity and all | |||
| other entities that control, are controlled by, or are under common | |||
| control with that entity. For the purposes of this definition, | |||
| "control" means (i) the power, direct or indirect, to cause the | |||
| direction or management of such entity, whether by contract or | |||
| otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
| outstanding shares, or (iii) beneficial ownership of such entity. | |||
| "You" (or "Your") shall mean an individual or Legal Entity | |||
| exercising permissions granted by this License. | |||
| "Source" form shall mean the preferred form for making modifications, | |||
| including but not limited to software source code, documentation | |||
| source, and configuration files. | |||
| "Object" form shall mean any form resulting from mechanical | |||
| transformation or translation of a Source form, including but | |||
| not limited to compiled object code, generated documentation, | |||
| and conversions to other media types. | |||
| "Work" shall mean the work of authorship, whether in Source or | |||
| Object form, made available under the License, as indicated by a | |||
| copyright notice that is included in or attached to the work | |||
| (an example is provided in the Appendix below). | |||
| "Derivative Works" shall mean any work, whether in Source or Object | |||
| form, that is based on (or derived from) the Work and for which the | |||
| editorial revisions, annotations, elaborations, or other modifications | |||
| represent, as a whole, an original work of authorship. For the purposes | |||
| of this License, Derivative Works shall not include works that remain | |||
| separable from, or merely link (or bind by name) to the interfaces of, | |||
| the Work and Derivative Works thereof. | |||
| "Contribution" shall mean any work of authorship, including | |||
| the original version of the Work and any modifications or additions | |||
| to that Work or Derivative Works thereof, that is intentionally | |||
| submitted to Licensor for inclusion in the Work by the copyright owner | |||
| or by an individual or Legal Entity authorized to submit on behalf of | |||
| the copyright owner. For the purposes of this definition, "submitted" | |||
| means any form of electronic, verbal, or written communication sent | |||
| to the Licensor or its representatives, including but not limited to | |||
| communication on electronic mailing lists, source code control systems, | |||
| and issue tracking systems that are managed by, or on behalf of, the | |||
| Licensor for the purpose of discussing and improving the Work, but | |||
| excluding communication that is conspicuously marked or otherwise | |||
| designated in writing by the copyright owner as "Not a Contribution." | |||
| "Contributor" shall mean Licensor and any individual or Legal Entity | |||
| on behalf of whom a Contribution has been received by Licensor and | |||
| subsequently incorporated within the Work. | |||
| 2. Grant of Copyright License. Subject to the terms and conditions of | |||
| this License, each Contributor hereby grants to You a perpetual, | |||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
| copyright license to reproduce, prepare Derivative Works of, | |||
| publicly display, publicly perform, sublicense, and distribute the | |||
| Work and such Derivative Works in Source or Object form. | |||
| 3. Grant of Patent License. Subject to the terms and conditions of | |||
| this License, each Contributor hereby grants to You a perpetual, | |||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
| (except as stated in this section) patent license to make, have made, | |||
| use, offer to sell, sell, import, and otherwise transfer the Work, | |||
| where such license applies only to those patent claims licensable | |||
| by such Contributor that are necessarily infringed by their | |||
| Contribution(s) alone or by combination of their Contribution(s) | |||
| with the Work to which such Contribution(s) was submitted. If You | |||
| institute patent litigation against any entity (including a | |||
| cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
| or a Contribution incorporated within the Work constitutes direct | |||
| or contributory patent infringement, then any patent licenses | |||
| granted to You under this License for that Work shall terminate | |||
| as of the date such litigation is filed. | |||
| 4. Redistribution. You may reproduce and distribute copies of the | |||
| Work or Derivative Works thereof in any medium, with or without | |||
| modifications, and in Source or Object form, provided that You | |||
| meet the following conditions: | |||
| (a) You must give any other recipients of the Work or | |||
| Derivative Works a copy of this License; and | |||
| (b) You must cause any modified files to carry prominent notices | |||
| stating that You changed the files; and | |||
| (c) You must retain, in the Source form of any Derivative Works | |||
| that You distribute, all copyright, patent, trademark, and | |||
| attribution notices from the Source form of the Work, | |||
| excluding those notices that do not pertain to any part of | |||
| the Derivative Works; and | |||
| (d) If the Work includes a "NOTICE" text file as part of its | |||
| distribution, then any Derivative Works that You distribute must | |||
| include a readable copy of the attribution notices contained | |||
| within such NOTICE file, excluding those notices that do not | |||
| pertain to any part of the Derivative Works, in at least one | |||
| of the following places: within a NOTICE text file distributed | |||
| as part of the Derivative Works; within the Source form or | |||
| documentation, if provided along with the Derivative Works; or, | |||
| within a display generated by the Derivative Works, if and | |||
| wherever such third-party notices normally appear. The contents | |||
| of the NOTICE file are for informational purposes only and | |||
| do not modify the License. You may add Your own attribution | |||
| notices within Derivative Works that You distribute, alongside | |||
| or as an addendum to the NOTICE text from the Work, provided | |||
| that such additional attribution notices cannot be construed | |||
| as modifying the License. | |||
| You may add Your own copyright statement to Your modifications and | |||
| may provide additional or different license terms and conditions | |||
| for use, reproduction, or distribution of Your modifications, or | |||
| for any such Derivative Works as a whole, provided Your use, | |||
| reproduction, and distribution of the Work otherwise complies with | |||
| the conditions stated in this License. | |||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | |||
| any Contribution intentionally submitted for inclusion in the Work | |||
| by You to the Licensor shall be under the terms and conditions of | |||
| this License, without any additional terms or conditions. | |||
| Notwithstanding the above, nothing herein shall supersede or modify | |||
| the terms of any separate license agreement you may have executed | |||
| with Licensor regarding such Contributions. | |||
| 6. Trademarks. This License does not grant permission to use the trade | |||
| names, trademarks, service marks, or product names of the Licensor, | |||
| except as required for reasonable and customary use in describing the | |||
| origin of the Work and reproducing the content of the NOTICE file. | |||
| 7. Disclaimer of Warranty. Unless required by applicable law or | |||
| agreed to in writing, Licensor provides the Work (and each | |||
| Contributor provides its Contributions) on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
| implied, including, without limitation, any warranties or conditions | |||
| of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
| PARTICULAR PURPOSE. You are solely responsible for determining the | |||
| appropriateness of using or redistributing the Work and assume any | |||
| risks associated with Your exercise of permissions under this License. | |||
| 8. Limitation of Liability. In no event and under no legal theory, | |||
| whether in tort (including negligence), contract, or otherwise, | |||
| unless required by applicable law (such as deliberate and grossly | |||
| negligent acts) or agreed to in writing, shall any Contributor be | |||
| liable to You for damages, including any direct, indirect, special, | |||
| incidental, or consequential damages of any character arising as a | |||
| result of this License or out of the use or inability to use the | |||
| Work (including but not limited to damages for loss of goodwill, | |||
| work stoppage, computer failure or malfunction, or any and all | |||
| other commercial damages or losses), even if such Contributor | |||
| has been advised of the possibility of such damages. | |||
| 9. Accepting Warranty or Additional Liability. While redistributing | |||
| the Work or Derivative Works thereof, You may choose to offer, | |||
| and charge a fee for, acceptance of support, warranty, indemnity, | |||
| or other liability obligations and/or rights consistent with this | |||
| License. However, in accepting such obligations, You may act only | |||
| on Your own behalf and on Your sole responsibility, not on behalf | |||
| of any other Contributor, and only if You agree to indemnify, | |||
| defend, and hold each Contributor harmless for any liability | |||
| incurred by, or claims asserted against, such Contributor by reason | |||
| of your accepting any such warranty or additional liability. | |||
| END OF TERMS AND CONDITIONS | |||
| APPENDIX: How to apply the Apache License to your work. | |||
| To apply the Apache License to your work, attach the following | |||
| boilerplate notice, with the fields enclosed by brackets "[]" | |||
| replaced with your own identifying information. (Don't include | |||
| the brackets!) The text should be enclosed in the appropriate | |||
| comment syntax for the file format. We also recommend that a | |||
| file or class name and description of purpose be included on the | |||
| same "printed page" as the copyright notice for easier | |||
| identification within third-party archives. | |||
| Copyright [yyyy] [name of copyright owner] | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| ----------------------------------------------------------- | |||
| BSD 3-Clause License | |||
| Copyright 2016, Google Inc. | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, are permitted provided that the following conditions are met: | |||
| 1. Redistributions of source code must retain the above copyright notice, | |||
| this list of conditions and the following disclaimer. | |||
| 2. Redistributions in binary form must reproduce the above copyright notice, | |||
| this list of conditions and the following disclaimer in the documentation | |||
| and/or other materials provided with the distribution. | |||
| 3. Neither the name of the copyright holder nor the names of its | |||
| contributors may be used to endorse or promote products derived from this | |||
| software without specific prior written permission. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
| ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |||
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |||
| THE POSSIBILITY OF SUCH DAMAGE. | |||
| ----------------------------------------------------------- | |||
| Mozilla Public License Version 2.0 | |||
| ================================== | |||
| 1. Definitions | |||
| -------------- | |||
| 1.1. "Contributor" | |||
| means each individual or legal entity that creates, contributes to | |||
| the creation of, or owns Covered Software. | |||
| 1.2. "Contributor Version" | |||
| means the combination of the Contributions of others (if any) used | |||
| by a Contributor and that particular Contributor's Contribution. | |||
| 1.3. "Contribution" | |||
| means Covered Software of a particular Contributor. | |||
| 1.4. "Covered Software" | |||
| means Source Code Form to which the initial Contributor has attached | |||
| the notice in Exhibit A, the Executable Form of such Source Code | |||
| Form, and Modifications of such Source Code Form, in each case | |||
| including portions thereof. | |||
| 1.5. "Incompatible With Secondary Licenses" | |||
| means | |||
| (a) that the initial Contributor has attached the notice described | |||
| in Exhibit B to the Covered Software; or | |||
| (b) that the Covered Software was made available under the terms of | |||
| version 1.1 or earlier of the License, but not also under the | |||
| terms of a Secondary License. | |||
| 1.6. "Executable Form" | |||
| means any form of the work other than Source Code Form. | |||
| 1.7. "Larger Work" | |||
| means a work that combines Covered Software with other material, in | |||
| a separate file or files, that is not Covered Software. | |||
| 1.8. "License" | |||
| means this document. | |||
| 1.9. "Licensable" | |||
| means having the right to grant, to the maximum extent possible, | |||
| whether at the time of the initial grant or subsequently, any and | |||
| all of the rights conveyed by this License. | |||
| 1.10. "Modifications" | |||
| means any of the following: | |||
| (a) any file in Source Code Form that results from an addition to, | |||
| deletion from, or modification of the contents of Covered | |||
| Software; or | |||
| (b) any new file in Source Code Form that contains any Covered | |||
| Software. | |||
| 1.11. "Patent Claims" of a Contributor | |||
| means any patent claim(s), including without limitation, method, | |||
| process, and apparatus claims, in any patent Licensable by such | |||
| Contributor that would be infringed, but for the grant of the | |||
| License, by the making, using, selling, offering for sale, having | |||
| made, import, or transfer of either its Contributions or its | |||
| Contributor Version. | |||
| 1.12. "Secondary License" | |||
| means either the GNU General Public License, Version 2.0, the GNU | |||
| Lesser General Public License, Version 2.1, the GNU Affero General | |||
| Public License, Version 3.0, or any later versions of those | |||
| licenses. | |||
| 1.13. "Source Code Form" | |||
| means the form of the work preferred for making modifications. | |||
| 1.14. "You" (or "Your") | |||
| means an individual or a legal entity exercising rights under this | |||
| License. For legal entities, "You" includes any entity that | |||
| controls, is controlled by, or is under common control with You. For | |||
| purposes of this definition, "control" means (a) the power, direct | |||
| or indirect, to cause the direction or management of such entity, | |||
| whether by contract or otherwise, or (b) ownership of more than | |||
| fifty percent (50%) of the outstanding shares or beneficial | |||
| ownership of such entity. | |||
| 2. License Grants and Conditions | |||
| -------------------------------- | |||
| 2.1. Grants | |||
| Each Contributor hereby grants You a world-wide, royalty-free, | |||
| non-exclusive license: | |||
| (a) under intellectual property rights (other than patent or trademark) | |||
| Licensable by such Contributor to use, reproduce, make available, | |||
| modify, display, perform, distribute, and otherwise exploit its | |||
| Contributions, either on an unmodified basis, with Modifications, or | |||
| as part of a Larger Work; and | |||
| (b) under Patent Claims of such Contributor to make, use, sell, offer | |||
| for sale, have made, import, and otherwise transfer either its | |||
| Contributions or its Contributor Version. | |||
| 2.2. Effective Date | |||
| The licenses granted in Section 2.1 with respect to any Contribution | |||
| become effective for each Contribution on the date the Contributor first | |||
| distributes such Contribution. | |||
| 2.3. Limitations on Grant Scope | |||
| The licenses granted in this Section 2 are the only rights granted under | |||
| this License. No additional rights or licenses will be implied from the | |||
| distribution or licensing of Covered Software under this License. | |||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
| Contributor: | |||
| (a) for any code that a Contributor has removed from Covered Software; | |||
| or | |||
| (b) for infringements caused by: (i) Your and any other third party's | |||
| modifications of Covered Software, or (ii) the combination of its | |||
| Contributions with other software (except as part of its Contributor | |||
| Version); or | |||
| (c) under Patent Claims infringed by Covered Software in the absence of | |||
| its Contributions. | |||
| This License does not grant any rights in the trademarks, service marks, | |||
| or logos of any Contributor (except as may be necessary to comply with | |||
| the notice requirements in Section 3.4). | |||
| 2.4. Subsequent Licenses | |||
| No Contributor makes additional grants as a result of Your choice to | |||
| distribute the Covered Software under a subsequent version of this | |||
| License (see Section 10.2) or under the terms of a Secondary License (if | |||
| permitted under the terms of Section 3.3). | |||
| 2.5. Representation | |||
| Each Contributor represents that the Contributor believes its | |||
| Contributions are its original creation(s) or it has sufficient rights | |||
| to grant the rights to its Contributions conveyed by this License. | |||
| 2.6. Fair Use | |||
| This License is not intended to limit any rights You have under | |||
| applicable copyright doctrines of fair use, fair dealing, or other | |||
| equivalents. | |||
| 2.7. Conditions | |||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | |||
| in Section 2.1. | |||
| 3. Responsibilities | |||
| ------------------- | |||
| 3.1. Distribution of Source Form | |||
| All distribution of Covered Software in Source Code Form, including any | |||
| Modifications that You create or to which You contribute, must be under | |||
| the terms of this License. You must inform recipients that the Source | |||
| Code Form of the Covered Software is governed by the terms of this | |||
| License, and how they can obtain a copy of this License. You may not | |||
| attempt to alter or restrict the recipients' rights in the Source Code | |||
| Form. | |||
| 3.2. Distribution of Executable Form | |||
| If You distribute Covered Software in Executable Form then: | |||
| (a) such Covered Software must also be made available in Source Code | |||
| Form, as described in Section 3.1, and You must inform recipients of | |||
| the Executable Form how they can obtain a copy of such Source Code | |||
| Form by reasonable means in a timely manner, at a charge no more | |||
| than the cost of distribution to the recipient; and | |||
| (b) You may distribute such Executable Form under the terms of this | |||
| License, or sublicense it under different terms, provided that the | |||
| license for the Executable Form does not attempt to limit or alter | |||
| the recipients' rights in the Source Code Form under this License. | |||
| 3.3. Distribution of a Larger Work | |||
| You may create and distribute a Larger Work under terms of Your choice, | |||
| provided that You also comply with the requirements of this License for | |||
| the Covered Software. If the Larger Work is a combination of Covered | |||
| Software with a work governed by one or more Secondary Licenses, and the | |||
| Covered Software is not Incompatible With Secondary Licenses, this | |||
| License permits You to additionally distribute such Covered Software | |||
| under the terms of such Secondary License(s), so that the recipient of | |||
| the Larger Work may, at their option, further distribute the Covered | |||
| Software under the terms of either this License or such Secondary | |||
| License(s). | |||
| 3.4. Notices | |||
| You may not remove or alter the substance of any license notices | |||
| (including copyright notices, patent notices, disclaimers of warranty, | |||
| or limitations of liability) contained within the Source Code Form of | |||
| the Covered Software, except that You may alter any license notices to | |||
| the extent required to remedy known factual inaccuracies. | |||
| 3.5. Application of Additional Terms | |||
| You may choose to offer, and to charge a fee for, warranty, support, | |||
| indemnity or liability obligations to one or more recipients of Covered | |||
| Software. However, You may do so only on Your own behalf, and not on | |||
| behalf of any Contributor. You must make it absolutely clear that any | |||
| such warranty, support, indemnity, or liability obligation is offered by | |||
| You alone, and You hereby agree to indemnify every Contributor for any | |||
| liability incurred by such Contributor as a result of warranty, support, | |||
| indemnity or liability terms You offer. You may include additional | |||
| disclaimers of warranty and limitations of liability specific to any | |||
| jurisdiction. | |||
| 4. Inability to Comply Due to Statute or Regulation | |||
| --------------------------------------------------- | |||
| If it is impossible for You to comply with any of the terms of this | |||
| License with respect to some or all of the Covered Software due to | |||
| statute, judicial order, or regulation then You must: (a) comply with | |||
| the terms of this License to the maximum extent possible; and (b) | |||
| describe the limitations and the code they affect. Such description must | |||
| be placed in a text file included with all distributions of the Covered | |||
| Software under this License. Except to the extent prohibited by statute | |||
| or regulation, such description must be sufficiently detailed for a | |||
| recipient of ordinary skill to be able to understand it. | |||
| 5. Termination | |||
| -------------- | |||
| 5.1. The rights granted under this License will terminate automatically | |||
| if You fail to comply with any of its terms. However, if You become | |||
| compliant, then the rights granted under this License from a particular | |||
| Contributor are reinstated (a) provisionally, unless and until such | |||
| Contributor explicitly and finally terminates Your grants, and (b) on an | |||
| ongoing basis, if such Contributor fails to notify You of the | |||
| non-compliance by some reasonable means prior to 60 days after You have | |||
| come back into compliance. Moreover, Your grants from a particular | |||
| Contributor are reinstated on an ongoing basis if such Contributor | |||
| notifies You of the non-compliance by some reasonable means, this is the | |||
| first time You have received notice of non-compliance with this License | |||
| from such Contributor, and You become compliant prior to 30 days after | |||
| Your receipt of the notice. | |||
| 5.2. If You initiate litigation against any entity by asserting a patent | |||
| infringement claim (excluding declaratory judgment actions, | |||
| counter-claims, and cross-claims) alleging that a Contributor Version | |||
| directly or indirectly infringes any patent, then the rights granted to | |||
| You by any and all Contributors for the Covered Software under Section | |||
| 2.1 of this License shall terminate. | |||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | |||
| end user license agreements (excluding distributors and resellers) which | |||
| have been validly granted by You or Your distributors under this License | |||
| prior to termination shall survive termination. | |||
| ************************************************************************ | |||
| * * | |||
| * 6. Disclaimer of Warranty * | |||
| * ------------------------- * | |||
| * * | |||
| * Covered Software is provided under this License on an "as is" * | |||
| * basis, without warranty of any kind, either expressed, implied, or * | |||
| * statutory, including, without limitation, warranties that the * | |||
| * Covered Software is free of defects, merchantable, fit for a * | |||
| * particular purpose or non-infringing. The entire risk as to the * | |||
| * quality and performance of the Covered Software is with You. * | |||
| * Should any Covered Software prove defective in any respect, You * | |||
| * (not any Contributor) assume the cost of any necessary servicing, * | |||
| * repair, or correction. This disclaimer of warranty constitutes an * | |||
| * essential part of this License. No use of any Covered Software is * | |||
| * authorized under this License except under this disclaimer. * | |||
| * * | |||
| ************************************************************************ | |||
| ************************************************************************ | |||
| * * | |||
| * 7. Limitation of Liability * | |||
| * -------------------------- * | |||
| * * | |||
| * Under no circumstances and under no legal theory, whether tort * | |||
| * (including negligence), contract, or otherwise, shall any * | |||
| * Contributor, or anyone who distributes Covered Software as * | |||
| * permitted above, be liable to You for any direct, indirect, * | |||
| * special, incidental, or consequential damages of any character * | |||
| * including, without limitation, damages for lost profits, loss of * | |||
| * goodwill, work stoppage, computer failure or malfunction, or any * | |||
| * and all other commercial damages or losses, even if such party * | |||
| * shall have been informed of the possibility of such damages. This * | |||
| * limitation of liability shall not apply to liability for death or * | |||
| * personal injury resulting from such party's negligence to the * | |||
| * extent applicable law prohibits such limitation. Some * | |||
| * jurisdictions do not allow the exclusion or limitation of * | |||
| * incidental or consequential damages, so this exclusion and * | |||
| * limitation may not apply to You. * | |||
| * * | |||
| ************************************************************************ | |||
| 8. Litigation | |||
| ------------- | |||
| Any litigation relating to this License may be brought only in the | |||
| courts of a jurisdiction where the defendant maintains its principal | |||
| place of business and such litigation shall be governed by laws of that | |||
| jurisdiction, without reference to its conflict-of-law provisions. | |||
| Nothing in this Section shall prevent a party's ability to bring | |||
| cross-claims or counter-claims. | |||
| 9. Miscellaneous | |||
| ---------------- | |||
| This License represents the complete agreement concerning the subject | |||
| matter hereof. If any provision of this License is held to be | |||
| unenforceable, such provision shall be reformed only to the extent | |||
| necessary to make it enforceable. Any law or regulation which provides | |||
| that the language of a contract shall be construed against the drafter | |||
| shall not be used to construe this License against a Contributor. | |||
| 10. Versions of the License | |||
| --------------------------- | |||
| 10.1. New Versions | |||
| Mozilla Foundation is the license steward. Except as provided in Section | |||
| 10.3, no one other than the license steward has the right to modify or | |||
| publish new versions of this License. Each version will be given a | |||
| distinguishing version number. | |||
| 10.2. Effect of New Versions | |||
| You may distribute the Covered Software under the terms of the version | |||
| of the License under which You originally received the Covered Software, | |||
| or under the terms of any subsequent version published by the license | |||
| steward. | |||
| 10.3. Modified Versions | |||
| If you create software not governed by this License, and you want to | |||
| create a new license for such software, you may create and use a | |||
| modified version of this License if you rename the license and remove | |||
| any references to the name of the license steward (except to note that | |||
| such modified license differs from this License). | |||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
| Licenses | |||
| If You choose to distribute Source Code Form that is Incompatible With | |||
| Secondary Licenses under the terms of this version of the License, the | |||
| notice described in Exhibit B of this License must be attached. | |||
| Exhibit A - Source Code Form License Notice | |||
| ------------------------------------------- | |||
| This Source Code Form is subject to the terms of the Mozilla Public | |||
| License, v. 2.0. If a copy of the MPL was not distributed with this | |||
| file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
| If it is not possible or desirable to put the notice in a particular | |||
| file, then You may include the notice in a location (such as a LICENSE | |||
| file in a relevant directory) where a recipient would be likely to look | |||
| for such a notice. | |||
| You may add additional accurate notices of copyright ownership. | |||
| Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
| --------------------------------------------------------- | |||
| This Source Code Form is "Incompatible With Secondary Licenses", as | |||
| defined by the Mozilla Public License, v. 2.0. | |||
| @@ -0,0 +1,162 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: algorithm.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file contains Google extensions to the standard <algorithm> C++ | |||
| // header. | |||
| #ifndef ABSL_ALGORITHM_ALGORITHM_H_ | |||
| #define ABSL_ALGORITHM_ALGORITHM_H_ | |||
| #include <algorithm> | |||
| #include <iterator> | |||
| #include <type_traits> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace algorithm_internal | |||
| { | |||
| // Performs comparisons with operator==, similar to C++14's `std::equal_to<>`. | |||
| struct EqualTo | |||
| { | |||
| template<typename T, typename U> | |||
| bool operator()(const T& a, const U& b) const | |||
| { | |||
| return a == b; | |||
| } | |||
| }; | |||
| template<typename InputIter1, typename InputIter2, typename Pred> | |||
| bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, Pred pred, std::input_iterator_tag, std::input_iterator_tag) | |||
| { | |||
| while (true) | |||
| { | |||
| if (first1 == last1) | |||
| return first2 == last2; | |||
| if (first2 == last2) | |||
| return false; | |||
| if (!pred(*first1, *first2)) | |||
| return false; | |||
| ++first1; | |||
| ++first2; | |||
| } | |||
| } | |||
| template<typename InputIter1, typename InputIter2, typename Pred> | |||
| bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, Pred&& pred, std::random_access_iterator_tag, std::random_access_iterator_tag) | |||
| { | |||
| return (last1 - first1 == last2 - first2) && | |||
| std::equal(first1, last1, first2, std::forward<Pred>(pred)); | |||
| } | |||
| // When we are using our own internal predicate that just applies operator==, we | |||
| // forward to the non-predicate form of std::equal. This enables an optimization | |||
| // in libstdc++ that can result in std::memcmp being used for integer types. | |||
| template<typename InputIter1, typename InputIter2> | |||
| bool EqualImpl(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, algorithm_internal::EqualTo /* unused */, std::random_access_iterator_tag, std::random_access_iterator_tag) | |||
| { | |||
| return (last1 - first1 == last2 - first2) && | |||
| std::equal(first1, last1, first2); | |||
| } | |||
| template<typename It> | |||
| It RotateImpl(It first, It middle, It last, std::true_type) | |||
| { | |||
| return std::rotate(first, middle, last); | |||
| } | |||
| template<typename It> | |||
| It RotateImpl(It first, It middle, It last, std::false_type) | |||
| { | |||
| std::rotate(first, middle, last); | |||
| return std::next(first, std::distance(middle, last)); | |||
| } | |||
| } // namespace algorithm_internal | |||
| // equal() | |||
| // | |||
| // Compares the equality of two ranges specified by pairs of iterators, using | |||
| // the given predicate, returning true iff for each corresponding iterator i1 | |||
| // and i2 in the first and second range respectively, pred(*i1, *i2) == true | |||
| // | |||
| // This comparison takes at most min(`last1` - `first1`, `last2` - `first2`) | |||
| // invocations of the predicate. Additionally, if InputIter1 and InputIter2 are | |||
| // both random-access iterators, and `last1` - `first1` != `last2` - `first2`, | |||
| // then the predicate is never invoked and the function returns false. | |||
| // | |||
| // This is a C++11-compatible implementation of C++14 `std::equal`. See | |||
| // https://en.cppreference.com/w/cpp/algorithm/equal for more information. | |||
| template<typename InputIter1, typename InputIter2, typename Pred> | |||
| bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2, Pred&& pred) | |||
| { | |||
| return algorithm_internal::EqualImpl( | |||
| first1, last1, first2, last2, std::forward<Pred>(pred), typename std::iterator_traits<InputIter1>::iterator_category{}, typename std::iterator_traits<InputIter2>::iterator_category{} | |||
| ); | |||
| } | |||
| // Overload of equal() that performs comparison of two ranges specified by pairs | |||
| // of iterators using operator==. | |||
| template<typename InputIter1, typename InputIter2> | |||
| bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2) | |||
| { | |||
| return absl::equal(first1, last1, first2, last2, algorithm_internal::EqualTo{}); | |||
| } | |||
| // linear_search() | |||
| // | |||
| // Performs a linear search for `value` using the iterator `first` up to | |||
| // but not including `last`, returning true if [`first`, `last`) contains an | |||
| // element equal to `value`. | |||
| // | |||
| // A linear search is of O(n) complexity which is guaranteed to make at most | |||
| // n = (`last` - `first`) comparisons. A linear search over short containers | |||
| // may be faster than a binary search, even when the container is sorted. | |||
| template<typename InputIterator, typename EqualityComparable> | |||
| bool linear_search(InputIterator first, InputIterator last, const EqualityComparable& value) | |||
| { | |||
| return std::find(first, last, value) != last; | |||
| } | |||
| // rotate() | |||
| // | |||
| // Performs a left rotation on a range of elements (`first`, `last`) such that | |||
| // `middle` is now the first element. `rotate()` returns an iterator pointing to | |||
| // the first element before rotation. This function is exactly the same as | |||
| // `std::rotate`, but fixes a bug in gcc | |||
| // <= 4.9 where `std::rotate` returns `void` instead of an iterator. | |||
| // | |||
| // The complexity of this algorithm is the same as that of `std::rotate`, but if | |||
| // `ForwardIterator` is not a random-access iterator, then `absl::rotate` | |||
| // performs an additional pass over the range to construct the return value. | |||
| template<typename ForwardIterator> | |||
| ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) | |||
| { | |||
| return algorithm_internal::RotateImpl( | |||
| first, middle, last, std::is_same<decltype(std::rotate(first, middle, last)), ForwardIterator>() | |||
| ); | |||
| } | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_ALGORITHM_ALGORITHM_H_ | |||
| @@ -0,0 +1,761 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This header file defines macros for declaring attributes for functions, | |||
| // types, and variables. | |||
| // | |||
| // These macros are used within Abseil and allow the compiler to optimize, where | |||
| // applicable, certain function calls. | |||
| // | |||
| // Most macros here are exposing GCC or Clang features, and are stubbed out for | |||
| // other compilers. | |||
| // | |||
| // GCC attributes documentation: | |||
| // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html | |||
| // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html | |||
| // https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Type-Attributes.html | |||
| // | |||
| // Most attributes in this file are already supported by GCC 4.7. However, some | |||
| // of them are not supported in older version of Clang. Thus, we check | |||
| // `__has_attribute()` first. If the check fails, we check if we are on GCC and | |||
| // assume the attribute exists on GCC (which is verified on GCC 4.7). | |||
| #ifndef ABSL_BASE_ATTRIBUTES_H_ | |||
| #define ABSL_BASE_ATTRIBUTES_H_ | |||
| #include "absl/base/config.h" | |||
| // ABSL_HAVE_ATTRIBUTE | |||
| // | |||
| // A function-like feature checking macro that is a wrapper around | |||
| // `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a | |||
| // nonzero constant integer if the attribute is supported or 0 if not. | |||
| // | |||
| // It evaluates to zero if `__has_attribute` is not defined by the compiler. | |||
| // | |||
| // GCC: https://gcc.gnu.org/gcc-5/changes.html | |||
| // Clang: https://clang.llvm.org/docs/LanguageExtensions.html | |||
| #ifdef __has_attribute | |||
| #define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) | |||
| #else | |||
| #define ABSL_HAVE_ATTRIBUTE(x) 0 | |||
| #endif | |||
| // ABSL_HAVE_CPP_ATTRIBUTE | |||
| // | |||
| // A function-like feature checking macro that accepts C++11 style attributes. | |||
| // It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6 | |||
| // (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't | |||
| // find `__has_cpp_attribute`, will evaluate to 0. | |||
| #if defined(__cplusplus) && defined(__has_cpp_attribute) | |||
| // NOTE: requiring __cplusplus above should not be necessary, but | |||
| // works around https://bugs.llvm.org/show_bug.cgi?id=23435. | |||
| #define ABSL_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) | |||
| #else | |||
| #define ABSL_HAVE_CPP_ATTRIBUTE(x) 0 | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Function Attributes | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html | |||
| // Clang: https://clang.llvm.org/docs/AttributeReference.html | |||
| // ABSL_PRINTF_ATTRIBUTE | |||
| // ABSL_SCANF_ATTRIBUTE | |||
| // | |||
| // Tells the compiler to perform `printf` format string checking if the | |||
| // compiler supports it; see the 'format' attribute in | |||
| // <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html>. | |||
| // | |||
| // Note: As the GCC manual states, "[s]ince non-static C++ methods | |||
| // have an implicit 'this' argument, the arguments of such methods | |||
| // should be counted from two, not one." | |||
| #if ABSL_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) \ | |||
| __attribute__((__format__(__printf__, string_index, first_to_check))) | |||
| #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) \ | |||
| __attribute__((__format__(__scanf__, string_index, first_to_check))) | |||
| #else | |||
| #define ABSL_PRINTF_ATTRIBUTE(string_index, first_to_check) | |||
| #define ABSL_SCANF_ATTRIBUTE(string_index, first_to_check) | |||
| #endif | |||
| // ABSL_ATTRIBUTE_ALWAYS_INLINE | |||
| // ABSL_ATTRIBUTE_NOINLINE | |||
| // | |||
| // Forces functions to either inline or not inline. Introduced in gcc 3.1. | |||
| #if ABSL_HAVE_ATTRIBUTE(always_inline) || \ | |||
| (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) | |||
| #define ABSL_HAVE_ATTRIBUTE_ALWAYS_INLINE 1 | |||
| #else | |||
| #define ABSL_ATTRIBUTE_ALWAYS_INLINE | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_NOINLINE __attribute__((noinline)) | |||
| #define ABSL_HAVE_ATTRIBUTE_NOINLINE 1 | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NOINLINE | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_TAIL_CALL | |||
| // | |||
| // Prevents the compiler from optimizing away stack frames for functions which | |||
| // end in a call to another function. | |||
| #if ABSL_HAVE_ATTRIBUTE(disable_tail_calls) | |||
| #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 | |||
| #define ABSL_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls)) | |||
| #elif defined(__GNUC__) && !defined(__clang__) && !defined(__e2k__) | |||
| #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 1 | |||
| #define ABSL_ATTRIBUTE_NO_TAIL_CALL \ | |||
| __attribute__((optimize("no-optimize-sibling-calls"))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_TAIL_CALL | |||
| #define ABSL_HAVE_ATTRIBUTE_NO_TAIL_CALL 0 | |||
| #endif | |||
| // ABSL_ATTRIBUTE_WEAK | |||
| // | |||
| // Tags a function as weak for the purposes of compilation and linking. | |||
| // Weak attributes did not work properly in LLVM's Windows backend before | |||
| // 9.0.0, so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 | |||
| // for further information. | |||
| // The MinGW compiler doesn't complain about the weak attribute until the link | |||
| // step, presumably because Windows doesn't use ELF binaries. | |||
| #if (ABSL_HAVE_ATTRIBUTE(weak) || (defined(__GNUC__) && !defined(__clang__))) && \ | |||
| (!defined(_WIN32) || (defined(__clang__) && __clang_major__ >= 9)) && \ | |||
| !defined(__MINGW32__) | |||
| #undef ABSL_ATTRIBUTE_WEAK | |||
| #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) | |||
| #define ABSL_HAVE_ATTRIBUTE_WEAK 1 | |||
| #else | |||
| #define ABSL_ATTRIBUTE_WEAK | |||
| #define ABSL_HAVE_ATTRIBUTE_WEAK 0 | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NONNULL | |||
| // | |||
| // Tells the compiler either (a) that a particular function parameter | |||
| // should be a non-null pointer, or (b) that all pointer arguments should | |||
| // be non-null. | |||
| // | |||
| // Note: As the GCC manual states, "[s]ince non-static C++ methods | |||
| // have an implicit 'this' argument, the arguments of such methods | |||
| // should be counted from two, not one." | |||
| // | |||
| // Args are indexed starting at 1. | |||
| // | |||
| // For non-static class member functions, the implicit `this` argument | |||
| // is arg 1, and the first explicit argument is arg 2. For static class member | |||
| // functions, there is no implicit `this`, and the first explicit argument is | |||
| // arg 1. | |||
| // | |||
| // Example: | |||
| // | |||
| // /* arg_a cannot be null, but arg_b can */ | |||
| // void Function(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(1); | |||
| // | |||
| // class C { | |||
| // /* arg_a cannot be null, but arg_b can */ | |||
| // void Method(void* arg_a, void* arg_b) ABSL_ATTRIBUTE_NONNULL(2); | |||
| // | |||
| // /* arg_a cannot be null, but arg_b can */ | |||
| // static void StaticMethod(void* arg_a, void* arg_b) | |||
| // ABSL_ATTRIBUTE_NONNULL(1); | |||
| // }; | |||
| // | |||
| // If no arguments are provided, then all pointer arguments should be non-null. | |||
| // | |||
| // /* No pointer arguments may be null. */ | |||
| // void Function(void* arg_a, void* arg_b, int arg_c) ABSL_ATTRIBUTE_NONNULL(); | |||
| // | |||
| // NOTE: The GCC nonnull attribute actually accepts a list of arguments, but | |||
| // ABSL_ATTRIBUTE_NONNULL does not. | |||
| #if ABSL_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NONNULL(...) | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NORETURN | |||
| // | |||
| // Tells the compiler that a given function never returns. | |||
| #if ABSL_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_NORETURN __attribute__((noreturn)) | |||
| #elif defined(_MSC_VER) | |||
| #define ABSL_ATTRIBUTE_NORETURN __declspec(noreturn) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NORETURN | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS | |||
| // | |||
| // Tells the AddressSanitizer (or other memory testing tools) to ignore a given | |||
| // function. Useful for cases when a function reads random locations on stack, | |||
| // calls _exit from a cloned subprocess, deliberately accesses buffer | |||
| // out of bounds or does other scary things with memory. | |||
| // NOTE: GCC supports AddressSanitizer(asan) since 4.8. | |||
| // https://gcc.gnu.org/gcc-4.8/changes.html | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize_address) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) | |||
| #elif defined(_MSC_VER) && _MSC_VER >= 1928 | |||
| // https://docs.microsoft.com/en-us/cpp/cpp/no-sanitize-address | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS __declspec(no_sanitize_address) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY | |||
| // | |||
| // Tells the MemorySanitizer to relax the handling of a given function. All "Use | |||
| // of uninitialized value" warnings from such functions will be suppressed, and | |||
| // all values loaded from memory will be considered fully initialized. This | |||
| // attribute is similar to the ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS attribute | |||
| // above, but deals with initialized-ness rather than addressability issues. | |||
| // NOTE: MemorySanitizer(msan) is supported by Clang but not GCC. | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize_memory) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_THREAD | |||
| // | |||
| // Tells the ThreadSanitizer to not instrument a given function. | |||
| // NOTE: GCC supports ThreadSanitizer(tsan) since 4.8. | |||
| // https://gcc.gnu.org/gcc-4.8/changes.html | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize_thread) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_THREAD | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED | |||
| // | |||
| // Tells the UndefinedSanitizer to ignore a given function. Useful for cases | |||
| // where certain behavior (eg. division by zero) is being used intentionally. | |||
| // NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9. | |||
| // https://gcc.gnu.org/gcc-4.9/changes.html | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize_undefined) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ | |||
| __attribute__((no_sanitize_undefined)) | |||
| #elif ABSL_HAVE_ATTRIBUTE(no_sanitize) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED \ | |||
| __attribute__((no_sanitize("undefined"))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_CFI | |||
| // | |||
| // Tells the ControlFlowIntegrity sanitizer to not instrument a given function. | |||
| // See https://clang.llvm.org/docs/ControlFlowIntegrity.html for details. | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi"))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_CFI | |||
| #endif | |||
| // ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK | |||
| // | |||
| // Tells the SafeStack to not instrument a given function. | |||
| // See https://clang.llvm.org/docs/SafeStack.html for details. | |||
| #if ABSL_HAVE_ATTRIBUTE(no_sanitize) | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK \ | |||
| __attribute__((no_sanitize("safe-stack"))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_NO_SANITIZE_SAFESTACK | |||
| #endif | |||
| // ABSL_ATTRIBUTE_RETURNS_NONNULL | |||
| // | |||
| // Tells the compiler that a particular function never returns a null pointer. | |||
| #if ABSL_HAVE_ATTRIBUTE(returns_nonnull) | |||
| #define ABSL_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_RETURNS_NONNULL | |||
| #endif | |||
| // ABSL_HAVE_ATTRIBUTE_SECTION | |||
| // | |||
| // Indicates whether labeled sections are supported. Weak symbol support is | |||
| // a prerequisite. Labeled sections are not supported on Darwin/iOS. | |||
| #ifdef ABSL_HAVE_ATTRIBUTE_SECTION | |||
| #error ABSL_HAVE_ATTRIBUTE_SECTION cannot be directly set | |||
| #elif (ABSL_HAVE_ATTRIBUTE(section) || (defined(__GNUC__) && !defined(__clang__))) && \ | |||
| !defined(__APPLE__) && ABSL_HAVE_ATTRIBUTE_WEAK | |||
| #define ABSL_HAVE_ATTRIBUTE_SECTION 1 | |||
| // ABSL_ATTRIBUTE_SECTION | |||
| // | |||
| // Tells the compiler/linker to put a given function into a section and define | |||
| // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. | |||
| // This functionality is supported by GNU linker. Any function annotated with | |||
| // `ABSL_ATTRIBUTE_SECTION` must not be inlined, or it will be placed into | |||
| // whatever section its caller is placed into. | |||
| // | |||
| #ifndef ABSL_ATTRIBUTE_SECTION | |||
| #define ABSL_ATTRIBUTE_SECTION(name) \ | |||
| __attribute__((section(#name))) __attribute__((noinline)) | |||
| #endif | |||
| // ABSL_ATTRIBUTE_SECTION_VARIABLE | |||
| // | |||
| // Tells the compiler/linker to put a given variable into a section and define | |||
| // `__start_ ## name` and `__stop_ ## name` symbols to bracket the section. | |||
| // This functionality is supported by GNU linker. | |||
| #ifndef ABSL_ATTRIBUTE_SECTION_VARIABLE | |||
| #ifdef _AIX | |||
| // __attribute__((section(#name))) on AIX is achived by using the `.csect` psudo | |||
| // op which includes an additional integer as part of its syntax indcating | |||
| // alignment. If data fall under different alignments then you might get a | |||
| // compilation error indicating a `Section type conflict`. | |||
| #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name))) | |||
| #endif | |||
| #endif | |||
| // ABSL_DECLARE_ATTRIBUTE_SECTION_VARS | |||
| // | |||
| // A weak section declaration to be used as a global declaration | |||
| // for ABSL_ATTRIBUTE_SECTION_START|STOP(name) to compile and link | |||
| // even without functions with ABSL_ATTRIBUTE_SECTION(name). | |||
| // ABSL_DEFINE_ATTRIBUTE_SECTION should be in the exactly one file; it's | |||
| // a no-op on ELF but not on Mach-O. | |||
| // | |||
| #ifndef ABSL_DECLARE_ATTRIBUTE_SECTION_VARS | |||
| #define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) \ | |||
| extern char __start_##name[] ABSL_ATTRIBUTE_WEAK; \ | |||
| extern char __stop_##name[] ABSL_ATTRIBUTE_WEAK | |||
| #endif | |||
| #ifndef ABSL_DEFINE_ATTRIBUTE_SECTION_VARS | |||
| #define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) | |||
| #define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) | |||
| #endif | |||
| // ABSL_ATTRIBUTE_SECTION_START | |||
| // | |||
| // Returns `void*` pointers to start/end of a section of code with | |||
| // functions having ABSL_ATTRIBUTE_SECTION(name). | |||
| // Returns 0 if no such functions exist. | |||
| // One must ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) for this to compile and | |||
| // link. | |||
| // | |||
| #define ABSL_ATTRIBUTE_SECTION_START(name) \ | |||
| (reinterpret_cast<void*>(__start_##name)) | |||
| #define ABSL_ATTRIBUTE_SECTION_STOP(name) \ | |||
| (reinterpret_cast<void*>(__stop_##name)) | |||
| #else // !ABSL_HAVE_ATTRIBUTE_SECTION | |||
| #define ABSL_HAVE_ATTRIBUTE_SECTION 0 | |||
| // provide dummy definitions | |||
| #define ABSL_ATTRIBUTE_SECTION(name) | |||
| #define ABSL_ATTRIBUTE_SECTION_VARIABLE(name) | |||
| #define ABSL_INIT_ATTRIBUTE_SECTION_VARS(name) | |||
| #define ABSL_DEFINE_ATTRIBUTE_SECTION_VARS(name) | |||
| #define ABSL_DECLARE_ATTRIBUTE_SECTION_VARS(name) | |||
| #define ABSL_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(0)) | |||
| #define ABSL_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(0)) | |||
| #endif // ABSL_ATTRIBUTE_SECTION | |||
| // ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC | |||
| // | |||
| // Support for aligning the stack on 32-bit x86. | |||
| #if ABSL_HAVE_ATTRIBUTE(force_align_arg_pointer) || \ | |||
| (defined(__GNUC__) && !defined(__clang__)) | |||
| #if defined(__i386__) | |||
| #define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \ | |||
| __attribute__((force_align_arg_pointer)) | |||
| #define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) | |||
| #elif defined(__x86_64__) | |||
| #define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (1) | |||
| #define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC | |||
| #else // !__i386__ && !__x86_64 | |||
| #define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) | |||
| #define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC | |||
| #endif // __i386__ | |||
| #else | |||
| #define ABSL_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC | |||
| #define ABSL_REQUIRE_STACK_ALIGN_TRAMPOLINE (0) | |||
| #endif | |||
| // ABSL_MUST_USE_RESULT | |||
| // | |||
| // Tells the compiler to warn about unused results. | |||
| // | |||
| // For code or headers that are assured to only build with C++17 and up, prefer | |||
| // just using the standard `[[nodiscard]]` directly over this macro. | |||
| // | |||
| // When annotating a function, it must appear as the first part of the | |||
| // declaration or definition. The compiler will warn if the return value from | |||
| // such a function is unused: | |||
| // | |||
| // ABSL_MUST_USE_RESULT Sprocket* AllocateSprocket(); | |||
| // AllocateSprocket(); // Triggers a warning. | |||
| // | |||
| // When annotating a class, it is equivalent to annotating every function which | |||
| // returns an instance. | |||
| // | |||
| // class ABSL_MUST_USE_RESULT Sprocket {}; | |||
| // Sprocket(); // Triggers a warning. | |||
| // | |||
| // Sprocket MakeSprocket(); | |||
| // MakeSprocket(); // Triggers a warning. | |||
| // | |||
| // Note that references and pointers are not instances: | |||
| // | |||
| // Sprocket* SprocketPointer(); | |||
| // SprocketPointer(); // Does *not* trigger a warning. | |||
| // | |||
| // ABSL_MUST_USE_RESULT allows using cast-to-void to suppress the unused result | |||
| // warning. For that, warn_unused_result is used only for clang but not for gcc. | |||
| // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425 | |||
| // | |||
| // Note: past advice was to place the macro after the argument list. | |||
| // | |||
| // TODO(b/176172494): Use ABSL_HAVE_CPP_ATTRIBUTE(nodiscard) when all code is | |||
| // compliant with the stricter [[nodiscard]]. | |||
| #if defined(__clang__) && ABSL_HAVE_ATTRIBUTE(warn_unused_result) | |||
| #define ABSL_MUST_USE_RESULT __attribute__((warn_unused_result)) | |||
| #else | |||
| #define ABSL_MUST_USE_RESULT | |||
| #endif | |||
| // ABSL_ATTRIBUTE_HOT, ABSL_ATTRIBUTE_COLD | |||
| // | |||
| // Tells GCC that a function is hot or cold. GCC can use this information to | |||
| // improve static analysis, i.e. a conditional branch to a cold function | |||
| // is likely to be not-taken. | |||
| // This annotation is used for function declarations. | |||
| // | |||
| // Example: | |||
| // | |||
| // int foo() ABSL_ATTRIBUTE_HOT; | |||
| #if ABSL_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_HOT __attribute__((hot)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_HOT | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_COLD __attribute__((cold)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_COLD | |||
| #endif | |||
| // ABSL_XRAY_ALWAYS_INSTRUMENT, ABSL_XRAY_NEVER_INSTRUMENT, ABSL_XRAY_LOG_ARGS | |||
| // | |||
| // We define the ABSL_XRAY_ALWAYS_INSTRUMENT and ABSL_XRAY_NEVER_INSTRUMENT | |||
| // macro used as an attribute to mark functions that must always or never be | |||
| // instrumented by XRay. Currently, this is only supported in Clang/LLVM. | |||
| // | |||
| // For reference on the LLVM XRay instrumentation, see | |||
| // http://llvm.org/docs/XRay.html. | |||
| // | |||
| // A function with the XRAY_ALWAYS_INSTRUMENT macro attribute in its declaration | |||
| // will always get the XRay instrumentation sleds. These sleds may introduce | |||
| // some binary size and runtime overhead and must be used sparingly. | |||
| // | |||
| // These attributes only take effect when the following conditions are met: | |||
| // | |||
| // * The file/target is built in at least C++11 mode, with a Clang compiler | |||
| // that supports XRay attributes. | |||
| // * The file/target is built with the -fxray-instrument flag set for the | |||
| // Clang/LLVM compiler. | |||
| // * The function is defined in the translation unit (the compiler honors the | |||
| // attribute in either the definition or the declaration, and must match). | |||
| // | |||
| // There are cases when, even when building with XRay instrumentation, users | |||
| // might want to control specifically which functions are instrumented for a | |||
| // particular build using special-case lists provided to the compiler. These | |||
| // special case lists are provided to Clang via the | |||
| // -fxray-always-instrument=... and -fxray-never-instrument=... flags. The | |||
| // attributes in source take precedence over these special-case lists. | |||
| // | |||
| // To disable the XRay attributes at build-time, users may define | |||
| // ABSL_NO_XRAY_ATTRIBUTES. Do NOT define ABSL_NO_XRAY_ATTRIBUTES on specific | |||
| // packages/targets, as this may lead to conflicting definitions of functions at | |||
| // link-time. | |||
| // | |||
| // XRay isn't currently supported on Android: | |||
| // https://github.com/android/ndk/issues/368 | |||
| #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_always_instrument) && \ | |||
| !defined(ABSL_NO_XRAY_ATTRIBUTES) && !defined(__ANDROID__) | |||
| #define ABSL_XRAY_ALWAYS_INSTRUMENT [[clang::xray_always_instrument]] | |||
| #define ABSL_XRAY_NEVER_INSTRUMENT [[clang::xray_never_instrument]] | |||
| #if ABSL_HAVE_CPP_ATTRIBUTE(clang::xray_log_args) | |||
| #define ABSL_XRAY_LOG_ARGS(N) \ | |||
| [[clang::xray_always_instrument, clang::xray_log_args(N)]] | |||
| #else | |||
| #define ABSL_XRAY_LOG_ARGS(N) [[clang::xray_always_instrument]] | |||
| #endif | |||
| #else | |||
| #define ABSL_XRAY_ALWAYS_INSTRUMENT | |||
| #define ABSL_XRAY_NEVER_INSTRUMENT | |||
| #define ABSL_XRAY_LOG_ARGS(N) | |||
| #endif | |||
| // ABSL_ATTRIBUTE_REINITIALIZES | |||
| // | |||
| // Indicates that a member function reinitializes the entire object to a known | |||
| // state, independent of the previous state of the object. | |||
| // | |||
| // The clang-tidy check bugprone-use-after-move allows member functions marked | |||
| // with this attribute to be called on objects that have been moved from; | |||
| // without the attribute, this would result in a use-after-move warning. | |||
| #if ABSL_HAVE_CPP_ATTRIBUTE(clang::reinitializes) | |||
| #define ABSL_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] | |||
| #else | |||
| #define ABSL_ATTRIBUTE_REINITIALIZES | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Variable Attributes | |||
| // ----------------------------------------------------------------------------- | |||
| // ABSL_ATTRIBUTE_UNUSED | |||
| // | |||
| // Prevents the compiler from complaining about variables that appear unused. | |||
| // | |||
| // For code or headers that are assured to only build with C++17 and up, prefer | |||
| // just using the standard '[[maybe_unused]]' directly over this macro. | |||
| // | |||
| // Due to differences in positioning requirements between the old, compiler | |||
| // specific __attribute__ syntax and the now standard [[maybe_unused]], this | |||
| // macro does not attempt to take advantage of '[[maybe_unused]]'. | |||
| #if ABSL_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #undef ABSL_ATTRIBUTE_UNUSED | |||
| #define ABSL_ATTRIBUTE_UNUSED __attribute__((__unused__)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_UNUSED | |||
| #endif | |||
| // ABSL_ATTRIBUTE_INITIAL_EXEC | |||
| // | |||
| // Tells the compiler to use "initial-exec" mode for a thread-local variable. | |||
| // See http://people.redhat.com/drepper/tls.pdf for the gory details. | |||
| #if ABSL_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec"))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_INITIAL_EXEC | |||
| #endif | |||
| // ABSL_ATTRIBUTE_PACKED | |||
| // | |||
| // Instructs the compiler not to use natural alignment for a tagged data | |||
| // structure, but instead to reduce its alignment to 1. | |||
| // | |||
| // Therefore, DO NOT APPLY THIS ATTRIBUTE TO STRUCTS CONTAINING ATOMICS. Doing | |||
| // so can cause atomic variables to be mis-aligned and silently violate | |||
| // atomicity on x86. | |||
| // | |||
| // This attribute can either be applied to members of a structure or to a | |||
| // structure in its entirety. Applying this attribute (judiciously) to a | |||
| // structure in its entirety to optimize the memory footprint of very | |||
| // commonly-used structs is fine. Do not apply this attribute to a structure in | |||
| // its entirety if the purpose is to control the offsets of the members in the | |||
| // structure. Instead, apply this attribute only to structure members that need | |||
| // it. | |||
| // | |||
| // When applying ABSL_ATTRIBUTE_PACKED only to specific structure members the | |||
| // natural alignment of structure members not annotated is preserved. Aligned | |||
| // member accesses are faster than non-aligned member accesses even if the | |||
| // targeted microprocessor supports non-aligned accesses. | |||
| #if ABSL_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_PACKED __attribute__((__packed__)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_PACKED | |||
| #endif | |||
| // ABSL_ATTRIBUTE_FUNC_ALIGN | |||
| // | |||
| // Tells the compiler to align the function start at least to certain | |||
| // alignment boundary | |||
| #if ABSL_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes))) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_FUNC_ALIGN(bytes) | |||
| #endif | |||
| // ABSL_FALLTHROUGH_INTENDED | |||
| // | |||
| // Annotates implicit fall-through between switch labels, allowing a case to | |||
| // indicate intentional fallthrough and turn off warnings about any lack of a | |||
| // `break` statement. The ABSL_FALLTHROUGH_INTENDED macro should be followed by | |||
| // a semicolon and can be used in most places where `break` can, provided that | |||
| // no statements exist between it and the next switch label. | |||
| // | |||
| // Example: | |||
| // | |||
| // switch (x) { | |||
| // case 40: | |||
| // case 41: | |||
| // if (truth_is_out_there) { | |||
| // ++x; | |||
| // ABSL_FALLTHROUGH_INTENDED; // Use instead of/along with annotations | |||
| // // in comments | |||
| // } else { | |||
| // return x; | |||
| // } | |||
| // case 42: | |||
| // ... | |||
| // | |||
| // Notes: When supported, GCC and Clang can issue a warning on switch labels | |||
| // with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See | |||
| // clang documentation on language extensions for details: | |||
| // https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough | |||
| // | |||
| // When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has | |||
| // no effect on diagnostics. In any case this macro has no effect on runtime | |||
| // behavior and performance of code. | |||
| #ifdef ABSL_FALLTHROUGH_INTENDED | |||
| #error "ABSL_FALLTHROUGH_INTENDED should not be defined." | |||
| #elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough) | |||
| #define ABSL_FALLTHROUGH_INTENDED [[fallthrough]] | |||
| #elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough) | |||
| #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]] | |||
| #elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough) | |||
| #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]] | |||
| #else | |||
| #define ABSL_FALLTHROUGH_INTENDED \ | |||
| do \ | |||
| { \ | |||
| } while (0) | |||
| #endif | |||
| // ABSL_DEPRECATED() | |||
| // | |||
| // Marks a deprecated class, struct, enum, function, method and variable | |||
| // declarations. The macro argument is used as a custom diagnostic message (e.g. | |||
| // suggestion of a better alternative). | |||
| // | |||
| // For code or headers that are assured to only build with C++14 and up, prefer | |||
| // just using the standard `[[deprecated("message")]]` directly over this macro. | |||
| // | |||
| // Examples: | |||
| // | |||
| // class ABSL_DEPRECATED("Use Bar instead") Foo {...}; | |||
| // | |||
| // ABSL_DEPRECATED("Use Baz() instead") void Bar() {...} | |||
| // | |||
| // template <typename T> | |||
| // ABSL_DEPRECATED("Use DoThat() instead") | |||
| // void DoThis(); | |||
| // | |||
| // enum FooEnum { | |||
| // kBar ABSL_DEPRECATED("Use kBaz instead"), | |||
| // }; | |||
| // | |||
| // Every usage of a deprecated entity will trigger a warning when compiled with | |||
| // GCC/Clang's `-Wdeprecated-declarations` option. Google's production toolchain | |||
| // turns this warning off by default, instead relying on clang-tidy to report | |||
| // new uses of deprecated code. | |||
| #if ABSL_HAVE_ATTRIBUTE(deprecated) | |||
| #define ABSL_DEPRECATED(message) __attribute__((deprecated(message))) | |||
| #else | |||
| #define ABSL_DEPRECATED(message) | |||
| #endif | |||
| // ABSL_CONST_INIT | |||
| // | |||
| // A variable declaration annotated with the `ABSL_CONST_INIT` attribute will | |||
| // not compile (on supported platforms) unless the variable has a constant | |||
| // initializer. This is useful for variables with static and thread storage | |||
| // duration, because it guarantees that they will not suffer from the so-called | |||
| // "static init order fiasco". | |||
| // | |||
| // This attribute must be placed on the initializing declaration of the | |||
| // variable. Some compilers will give a -Wmissing-constinit warning when this | |||
| // attribute is placed on some other declaration but missing from the | |||
| // initializing declaration. | |||
| // | |||
| // In some cases (notably with thread_local variables), `ABSL_CONST_INIT` can | |||
| // also be used in a non-initializing declaration to tell the compiler that a | |||
| // variable is already initialized, reducing overhead that would otherwise be | |||
| // incurred by a hidden guard variable. Thus annotating all declarations with | |||
| // this attribute is recommended to potentially enhance optimization. | |||
| // | |||
| // Example: | |||
| // | |||
| // class MyClass { | |||
| // public: | |||
| // ABSL_CONST_INIT static MyType my_var; | |||
| // }; | |||
| // | |||
| // ABSL_CONST_INIT MyType MyClass::my_var = MakeMyType(...); | |||
| // | |||
| // For code or headers that are assured to only build with C++20 and up, prefer | |||
| // just using the standard `constinit` keyword directly over this macro. | |||
| // | |||
| // Note that this attribute is redundant if the variable is declared constexpr. | |||
| #if defined(__cpp_constinit) && __cpp_constinit >= 201907L | |||
| #define ABSL_CONST_INIT constinit | |||
| #elif ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) | |||
| #define ABSL_CONST_INIT [[clang::require_constant_initialization]] | |||
| #else | |||
| #define ABSL_CONST_INIT | |||
| #endif | |||
| // ABSL_ATTRIBUTE_PURE_FUNCTION | |||
| // | |||
| // ABSL_ATTRIBUTE_PURE_FUNCTION is used to annotate declarations of "pure" | |||
| // functions. A function is pure if its return value is only a function of its | |||
| // arguments. The pure attribute prohibits a function from modifying the state | |||
| // of the program that is observable by means other than inspecting the | |||
| // function's return value. Declaring such functions with the pure attribute | |||
| // allows the compiler to avoid emitting some calls in repeated invocations of | |||
| // the function with the same argument values. | |||
| // | |||
| // Example: | |||
| // | |||
| // ABSL_ATTRIBUTE_PURE_FUNCTION int64_t ToInt64Milliseconds(Duration d); | |||
| #if ABSL_HAVE_CPP_ATTRIBUTE(gnu::pure) | |||
| #define ABSL_ATTRIBUTE_PURE_FUNCTION [[gnu::pure]] | |||
| #elif ABSL_HAVE_ATTRIBUTE(pure) | |||
| #define ABSL_ATTRIBUTE_PURE_FUNCTION __attribute__((pure)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_PURE_FUNCTION | |||
| #endif | |||
| // ABSL_ATTRIBUTE_LIFETIME_BOUND indicates that a resource owned by a function | |||
| // parameter or implicit object parameter is retained by the return value of the | |||
| // annotated function (or, for a parameter of a constructor, in the value of the | |||
| // constructed object). This attribute causes warnings to be produced if a | |||
| // temporary object does not live long enough. | |||
| // | |||
| // When applied to a reference parameter, the referenced object is assumed to be | |||
| // retained by the return value of the function. When applied to a non-reference | |||
| // parameter (for example, a pointer or a class type), all temporaries | |||
| // referenced by the parameter are assumed to be retained by the return value of | |||
| // the function. | |||
| // | |||
| // See also the upstream documentation: | |||
| // https://clang.llvm.org/docs/AttributeReference.html#lifetimebound | |||
| #if ABSL_HAVE_CPP_ATTRIBUTE(clang::lifetimebound) | |||
| #define ABSL_ATTRIBUTE_LIFETIME_BOUND [[clang::lifetimebound]] | |||
| #elif ABSL_HAVE_ATTRIBUTE(lifetimebound) | |||
| #define ABSL_ATTRIBUTE_LIFETIME_BOUND __attribute__((lifetimebound)) | |||
| #else | |||
| #define ABSL_ATTRIBUTE_LIFETIME_BOUND | |||
| #endif | |||
| #endif // ABSL_BASE_ATTRIBUTES_H_ | |||
| @@ -0,0 +1,234 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: call_once.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file provides an Abseil version of `std::call_once` for invoking | |||
| // a given function at most once, across all threads. This Abseil version is | |||
| // faster than the C++11 version and incorporates the C++17 argument-passing | |||
| // fix, so that (for example) non-const references may be passed to the invoked | |||
| // function. | |||
| #ifndef ABSL_BASE_CALL_ONCE_H_ | |||
| #define ABSL_BASE_CALL_ONCE_H_ | |||
| #include <algorithm> | |||
| #include <atomic> | |||
| #include <cstdint> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/base/internal/invoke.h" | |||
| #include "absl/base/internal/low_level_scheduling.h" | |||
| #include "absl/base/internal/raw_logging.h" | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| #include "absl/base/internal/spinlock_wait.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/base/port.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| class once_flag; | |||
| namespace base_internal | |||
| { | |||
| std::atomic<uint32_t>* ControlWord(absl::once_flag* flag); | |||
| } // namespace base_internal | |||
| // call_once() | |||
| // | |||
| // For all invocations using a given `once_flag`, invokes a given `fn` exactly | |||
| // once across all threads. The first call to `call_once()` with a particular | |||
| // `once_flag` argument (that does not throw an exception) will run the | |||
| // specified function with the provided `args`; other calls with the same | |||
| // `once_flag` argument will not run the function, but will wait | |||
| // for the provided function to finish running (if it is still running). | |||
| // | |||
| // This mechanism provides a safe, simple, and fast mechanism for one-time | |||
| // initialization in a multi-threaded process. | |||
| // | |||
| // Example: | |||
| // | |||
| // class MyInitClass { | |||
| // public: | |||
| // ... | |||
| // mutable absl::once_flag once_; | |||
| // | |||
| // MyInitClass* init() const { | |||
| // absl::call_once(once_, &MyInitClass::Init, this); | |||
| // return ptr_; | |||
| // } | |||
| // | |||
| template<typename Callable, typename... Args> | |||
| void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args); | |||
| // once_flag | |||
| // | |||
| // Objects of this type are used to distinguish calls to `call_once()` and | |||
| // ensure the provided function is only invoked once across all threads. This | |||
| // type is not copyable or movable. However, it has a `constexpr` | |||
| // constructor, and is safe to use as a namespace-scoped global variable. | |||
| class once_flag | |||
| { | |||
| public: | |||
| constexpr once_flag() : | |||
| control_(0) | |||
| { | |||
| } | |||
| once_flag(const once_flag&) = delete; | |||
| once_flag& operator=(const once_flag&) = delete; | |||
| private: | |||
| friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag); | |||
| std::atomic<uint32_t> control_; | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // End of public interfaces. | |||
| // Implementation details follow. | |||
| //------------------------------------------------------------------------------ | |||
| namespace base_internal | |||
| { | |||
| // Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to | |||
| // initialize entities used by the scheduler implementation. | |||
| template<typename Callable, typename... Args> | |||
| void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args); | |||
| // Disables scheduling while on stack when scheduling mode is non-cooperative. | |||
| // No effect for cooperative scheduling modes. | |||
| class SchedulingHelper | |||
| { | |||
| public: | |||
| explicit SchedulingHelper(base_internal::SchedulingMode mode) : | |||
| mode_(mode) | |||
| { | |||
| if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) | |||
| { | |||
| guard_result_ = base_internal::SchedulingGuard::DisableRescheduling(); | |||
| } | |||
| } | |||
| ~SchedulingHelper() | |||
| { | |||
| if (mode_ == base_internal::SCHEDULE_KERNEL_ONLY) | |||
| { | |||
| base_internal::SchedulingGuard::EnableRescheduling(guard_result_); | |||
| } | |||
| } | |||
| private: | |||
| base_internal::SchedulingMode mode_; | |||
| bool guard_result_; | |||
| }; | |||
| // Bit patterns for call_once state machine values. Internal implementation | |||
| // detail, not for use by clients. | |||
| // | |||
| // The bit patterns are arbitrarily chosen from unlikely values, to aid in | |||
| // debugging. However, kOnceInit must be 0, so that a zero-initialized | |||
| // once_flag will be valid for immediate use. | |||
| enum | |||
| { | |||
| kOnceInit = 0, | |||
| kOnceRunning = 0x65C2937B, | |||
| kOnceWaiter = 0x05A308D2, | |||
| // A very small constant is chosen for kOnceDone so that it fit in a single | |||
| // compare with immediate instruction for most common ISAs. This is verified | |||
| // for x86, POWER and ARM. | |||
| kOnceDone = 221, // Random Number | |||
| }; | |||
| template<typename Callable, typename... Args> | |||
| ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl(std::atomic<uint32_t>* control, base_internal::SchedulingMode scheduling_mode, Callable&& fn, Args&&... args) | |||
| { | |||
| #ifndef NDEBUG | |||
| { | |||
| uint32_t old_control = control->load(std::memory_order_relaxed); | |||
| if (old_control != kOnceInit && | |||
| old_control != kOnceRunning && | |||
| old_control != kOnceWaiter && | |||
| old_control != kOnceDone) | |||
| { | |||
| ABSL_RAW_LOG(FATAL, "Unexpected value for control word: 0x%lx", | |||
| static_cast<unsigned long>(old_control)); // NOLINT | |||
| } | |||
| } | |||
| #endif // NDEBUG | |||
| static const base_internal::SpinLockWaitTransition trans[] = { | |||
| {kOnceInit, kOnceRunning, true}, | |||
| {kOnceRunning, kOnceWaiter, false}, | |||
| {kOnceDone, kOnceDone, true}}; | |||
| // Must do this before potentially modifying control word's state. | |||
| base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); | |||
| // Short circuit the simplest case to avoid procedure call overhead. | |||
| // The base_internal::SpinLockWait() call returns either kOnceInit or | |||
| // kOnceDone. If it returns kOnceDone, it must have loaded the control word | |||
| // with std::memory_order_acquire and seen a value of kOnceDone. | |||
| uint32_t old_control = kOnceInit; | |||
| if (control->compare_exchange_strong(old_control, kOnceRunning, std::memory_order_relaxed) || | |||
| base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, scheduling_mode) == kOnceInit) | |||
| { | |||
| base_internal::invoke(std::forward<Callable>(fn), std::forward<Args>(args)...); | |||
| old_control = | |||
| control->exchange(base_internal::kOnceDone, std::memory_order_release); | |||
| if (old_control == base_internal::kOnceWaiter) | |||
| { | |||
| base_internal::SpinLockWake(control, true); | |||
| } | |||
| } // else *control is already kOnceDone | |||
| } | |||
| inline std::atomic<uint32_t>* ControlWord(once_flag* flag) | |||
| { | |||
| return &flag->control_; | |||
| } | |||
| template<typename Callable, typename... Args> | |||
| void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) | |||
| { | |||
| std::atomic<uint32_t>* once = base_internal::ControlWord(flag); | |||
| uint32_t s = once->load(std::memory_order_acquire); | |||
| if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) | |||
| { | |||
| base_internal::CallOnceImpl(once, base_internal::SCHEDULE_KERNEL_ONLY, std::forward<Callable>(fn), std::forward<Args>(args)...); | |||
| } | |||
| } | |||
| } // namespace base_internal | |||
| template<typename Callable, typename... Args> | |||
| void call_once(absl::once_flag& flag, Callable&& fn, Args&&... args) | |||
| { | |||
| std::atomic<uint32_t>* once = base_internal::ControlWord(&flag); | |||
| uint32_t s = once->load(std::memory_order_acquire); | |||
| if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) | |||
| { | |||
| base_internal::CallOnceImpl( | |||
| once, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL, std::forward<Callable>(fn), std::forward<Args>(args)... | |||
| ); | |||
| } | |||
| } | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_CALL_ONCE_H_ | |||
| @@ -0,0 +1,179 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: casts.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines casting templates to fit use cases not covered by | |||
| // the standard casts provided in the C++ standard. As with all cast operations, | |||
| // use these with caution and only if alternatives do not exist. | |||
| #ifndef ABSL_BASE_CASTS_H_ | |||
| #define ABSL_BASE_CASTS_H_ | |||
| #include <cstring> | |||
| #include <memory> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L | |||
| #include <bit> // For std::bit_cast. | |||
| #endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L | |||
| #include "absl/base/internal/identity.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/meta/type_traits.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| // implicit_cast() | |||
| // | |||
| // Performs an implicit conversion between types following the language | |||
| // rules for implicit conversion; if an implicit conversion is otherwise | |||
| // allowed by the language in the given context, this function performs such an | |||
| // implicit conversion. | |||
| // | |||
| // Example: | |||
| // | |||
| // // If the context allows implicit conversion: | |||
| // From from; | |||
| // To to = from; | |||
| // | |||
| // // Such code can be replaced by: | |||
| // implicit_cast<To>(from); | |||
| // | |||
| // An `implicit_cast()` may also be used to annotate numeric type conversions | |||
| // that, although safe, may produce compiler warnings (such as `long` to `int`). | |||
| // Additionally, an `implicit_cast()` is also useful within return statements to | |||
| // indicate a specific implicit conversion is being undertaken. | |||
| // | |||
| // Example: | |||
| // | |||
| // return implicit_cast<double>(size_in_bytes) / capacity_; | |||
| // | |||
| // Annotating code with `implicit_cast()` allows you to explicitly select | |||
| // particular overloads and template instantiations, while providing a safer | |||
| // cast than `reinterpret_cast()` or `static_cast()`. | |||
| // | |||
| // Additionally, an `implicit_cast()` can be used to allow upcasting within a | |||
| // type hierarchy where incorrect use of `static_cast()` could accidentally | |||
| // allow downcasting. | |||
| // | |||
| // Finally, an `implicit_cast()` can be used to perform implicit conversions | |||
| // from unrelated types that otherwise couldn't be implicitly cast directly; | |||
| // C++ will normally only implicitly cast "one step" in such conversions. | |||
| // | |||
| // That is, if C is a type which can be implicitly converted to B, with B being | |||
| // a type that can be implicitly converted to A, an `implicit_cast()` can be | |||
| // used to convert C to B (which the compiler can then implicitly convert to A | |||
| // using language rules). | |||
| // | |||
| // Example: | |||
| // | |||
| // // Assume an object C is convertible to B, which is implicitly convertible | |||
| // // to A | |||
| // A a = implicit_cast<B>(C); | |||
| // | |||
| // Such implicit cast chaining may be useful within template logic. | |||
| template<typename To> | |||
| constexpr To implicit_cast(typename absl::internal::identity_t<To> to) | |||
| { | |||
| return to; | |||
| } | |||
| // bit_cast() | |||
| // | |||
| // Creates a value of the new type `Dest` whose representation is the same as | |||
| // that of the argument, which is of (deduced) type `Source` (a "bitwise cast"; | |||
| // every bit in the value representation of the result is equal to the | |||
| // corresponding bit in the object representation of the source). Source and | |||
| // destination types must be of the same size, and both types must be trivially | |||
| // copyable. | |||
| // | |||
| // As with most casts, use with caution. A `bit_cast()` might be needed when you | |||
| // need to treat a value as the value of some other type, for example, to access | |||
| // the individual bits of an object which are not normally accessible through | |||
| // the object's type, such as for working with the binary representation of a | |||
| // floating point value: | |||
| // | |||
| // float f = 3.14159265358979; | |||
| // int i = bit_cast<int>(f); | |||
| // // i = 0x40490fdb | |||
| // | |||
| // Reinterpreting and accessing a value directly as a different type (as shown | |||
| // below) usually results in undefined behavior. | |||
| // | |||
| // Example: | |||
| // | |||
| // // WRONG | |||
| // float f = 3.14159265358979; | |||
| // int i = reinterpret_cast<int&>(f); // Wrong | |||
| // int j = *reinterpret_cast<int*>(&f); // Equally wrong | |||
| // int k = *bit_cast<int*>(&f); // Equally wrong | |||
| // | |||
| // Reinterpret-casting results in undefined behavior according to the ISO C++ | |||
| // specification, section [basic.lval]. Roughly, this section says: if an object | |||
| // in memory has one type, and a program accesses it with a different type, the | |||
| // result is undefined behavior for most "different type". | |||
| // | |||
| // Using bit_cast on a pointer and then dereferencing it is no better than using | |||
| // reinterpret_cast. You should only use bit_cast on the value itself. | |||
| // | |||
| // Such casting results in type punning: holding an object in memory of one type | |||
| // and reading its bits back using a different type. A `bit_cast()` avoids this | |||
| // issue by copying the object representation to a new value, which avoids | |||
| // introducing this undefined behavior (since the original value is never | |||
| // accessed in the wrong way). | |||
| // | |||
| // The requirements of `absl::bit_cast` are more strict than that of | |||
| // `std::bit_cast` unless compiler support is available. Specifically, without | |||
| // compiler support, this implementation also requires `Dest` to be | |||
| // default-constructible. In C++20, `absl::bit_cast` is replaced by | |||
| // `std::bit_cast`. | |||
| #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L | |||
| using std::bit_cast; | |||
| #else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L | |||
| template<typename Dest, typename Source, typename std::enable_if<sizeof(Dest) == sizeof(Source) && type_traits_internal::is_trivially_copyable<Source>::value && type_traits_internal::is_trivially_copyable<Dest>::value | |||
| #if !ABSL_HAVE_BUILTIN(__builtin_bit_cast) | |||
| && std::is_default_constructible<Dest>::value | |||
| #endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast) | |||
| , | |||
| int>::type = 0> | |||
| #if ABSL_HAVE_BUILTIN(__builtin_bit_cast) | |||
| inline constexpr Dest bit_cast(const Source& source) | |||
| { | |||
| return __builtin_bit_cast(Dest, source); | |||
| } | |||
| #else // ABSL_HAVE_BUILTIN(__builtin_bit_cast) | |||
| inline Dest bit_cast(const Source& source) | |||
| { | |||
| Dest dest; | |||
| memcpy(static_cast<void*>(std::addressof(dest)), static_cast<const void*>(std::addressof(source)), sizeof(dest)); | |||
| return dest; | |||
| } | |||
| #endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast) | |||
| #endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_CASTS_H_ | |||
| @@ -0,0 +1,908 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: config.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines a set of macros for checking the presence of | |||
| // important compiler and platform features. Such macros can be used to | |||
| // produce portable code by parameterizing compilation based on the presence or | |||
| // lack of a given feature. | |||
| // | |||
| // We define a "feature" as some interface we wish to program to: for example, | |||
| // a library function or system call. A value of `1` indicates support for | |||
| // that feature; any other value indicates the feature support is undefined. | |||
| // | |||
| // Example: | |||
| // | |||
| // Suppose a programmer wants to write a program that uses the 'mmap()' system | |||
| // call. The Abseil macro for that feature (`ABSL_HAVE_MMAP`) allows you to | |||
| // selectively include the `mmap.h` header and bracket code using that feature | |||
| // in the macro: | |||
| // | |||
| // #include "absl/base/config.h" | |||
| // | |||
| // #ifdef ABSL_HAVE_MMAP | |||
| // #include "sys/mman.h" | |||
| // #endif //ABSL_HAVE_MMAP | |||
| // | |||
| // ... | |||
| // #ifdef ABSL_HAVE_MMAP | |||
| // void *ptr = mmap(...); | |||
| // ... | |||
| // #endif // ABSL_HAVE_MMAP | |||
| #ifndef ABSL_BASE_CONFIG_H_ | |||
| #define ABSL_BASE_CONFIG_H_ | |||
| // Included for the __GLIBC__ macro (or similar macros on other systems). | |||
| #include <limits.h> | |||
| #ifdef __cplusplus | |||
| // Included for __GLIBCXX__, _LIBCPP_VERSION | |||
| #include <cstddef> | |||
| #endif // __cplusplus | |||
| // ABSL_INTERNAL_CPLUSPLUS_LANG | |||
| // | |||
| // MSVC does not set the value of __cplusplus correctly, but instead uses | |||
| // _MSVC_LANG as a stand-in. | |||
| // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros | |||
| // | |||
| // However, there are reports that MSVC even sets _MSVC_LANG incorrectly at | |||
| // times, for example: | |||
| // https://github.com/microsoft/vscode-cpptools/issues/1770 | |||
| // https://reviews.llvm.org/D70996 | |||
| // | |||
| // For this reason, this symbol is considered INTERNAL and code outside of | |||
| // Abseil must not use it. | |||
| #if defined(_MSVC_LANG) | |||
| #define ABSL_INTERNAL_CPLUSPLUS_LANG _MSVC_LANG | |||
| #elif defined(__cplusplus) | |||
| #define ABSL_INTERNAL_CPLUSPLUS_LANG __cplusplus | |||
| #endif | |||
| #if defined(__APPLE__) | |||
| // Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED, | |||
| // __IPHONE_8_0. | |||
| #include <Availability.h> | |||
| #include <TargetConditionals.h> | |||
| #endif | |||
| #include "absl/base/options.h" | |||
| #include "absl/base/policy_checks.h" | |||
| // Abseil long-term support (LTS) releases will define | |||
| // `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the | |||
| // LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the | |||
| // integer representing the patch-level for that release. | |||
| // | |||
| // For example, for LTS release version "20300401.2", this would give us | |||
| // ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2 | |||
| // | |||
| // These symbols will not be defined in non-LTS code. | |||
| // | |||
| // Abseil recommends that clients live-at-head. Therefore, if you are using | |||
| // these symbols to assert a minimum version requirement, we recommend you do it | |||
| // as | |||
| // | |||
| // #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401 | |||
| // #error Project foo requires Abseil LTS version >= 20300401 | |||
| // #endif | |||
| // | |||
| // The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes | |||
| // live-at-head clients from the minimum version assertion. | |||
| // | |||
| // See https://abseil.io/about/releases for more information on Abseil release | |||
| // management. | |||
| // | |||
| // LTS releases can be obtained from | |||
| // https://github.com/abseil/abseil-cpp/releases. | |||
| #define ABSL_LTS_RELEASE_VERSION 20220623 | |||
| #define ABSL_LTS_RELEASE_PATCH_LEVEL 1 | |||
| // Helper macro to convert a CPP variable to a string literal. | |||
| #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x | |||
| #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) | |||
| // ----------------------------------------------------------------------------- | |||
| // Abseil namespace annotations | |||
| // ----------------------------------------------------------------------------- | |||
| // ABSL_NAMESPACE_BEGIN/ABSL_NAMESPACE_END | |||
| // | |||
| // An annotation placed at the beginning/end of each `namespace absl` scope. | |||
| // This is used to inject an inline namespace. | |||
| // | |||
| // The proper way to write Abseil code in the `absl` namespace is: | |||
| // | |||
| // namespace absl { | |||
| // ABSL_NAMESPACE_BEGIN | |||
| // | |||
| // void Foo(); // absl::Foo(). | |||
| // | |||
| // ABSL_NAMESPACE_END | |||
| // } // namespace absl | |||
| // | |||
| // Users of Abseil should not use these macros, because users of Abseil should | |||
| // not write `namespace absl {` in their own code for any reason. (Abseil does | |||
| // not support forward declarations of its own types, nor does it support | |||
| // user-provided specialization of Abseil templates. Code that violates these | |||
| // rules may be broken without warning.) | |||
| #if !defined(ABSL_OPTION_USE_INLINE_NAMESPACE) || \ | |||
| !defined(ABSL_OPTION_INLINE_NAMESPACE_NAME) | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // Check that ABSL_OPTION_INLINE_NAMESPACE_NAME is neither "head" nor "" | |||
| #if defined(__cplusplus) && ABSL_OPTION_USE_INLINE_NAMESPACE == 1 | |||
| #define ABSL_INTERNAL_INLINE_NAMESPACE_STR \ | |||
| ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) | |||
| static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != '\0', "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " | |||
| "not be empty."); | |||
| static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' || ABSL_INTERNAL_INLINE_NAMESPACE_STR[1] != 'e' || ABSL_INTERNAL_INLINE_NAMESPACE_STR[2] != 'a' || ABSL_INTERNAL_INLINE_NAMESPACE_STR[3] != 'd' || ABSL_INTERNAL_INLINE_NAMESPACE_STR[4] != '\0', "options.h misconfigured: ABSL_OPTION_INLINE_NAMESPACE_NAME must " | |||
| "be changed to a new, unique identifier name."); | |||
| #endif | |||
| #if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 | |||
| #define ABSL_NAMESPACE_BEGIN | |||
| #define ABSL_NAMESPACE_END | |||
| #define ABSL_INTERNAL_C_SYMBOL(x) x | |||
| #elif ABSL_OPTION_USE_INLINE_NAMESPACE == 1 | |||
| #define ABSL_NAMESPACE_BEGIN \ | |||
| inline namespace ABSL_OPTION_INLINE_NAMESPACE_NAME \ | |||
| { | |||
| #define ABSL_NAMESPACE_END } | |||
| #define ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) x##_##v | |||
| #define ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, v) \ | |||
| ABSL_INTERNAL_C_SYMBOL_HELPER_2(x, v) | |||
| #define ABSL_INTERNAL_C_SYMBOL(x) \ | |||
| ABSL_INTERNAL_C_SYMBOL_HELPER_1(x, ABSL_OPTION_INLINE_NAMESPACE_NAME) | |||
| #else | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Compiler Feature Checks | |||
| // ----------------------------------------------------------------------------- | |||
| // ABSL_HAVE_BUILTIN() | |||
| // | |||
| // Checks whether the compiler supports a Clang Feature Checking Macro, and if | |||
| // so, checks whether it supports the provided builtin function "x" where x | |||
| // is one of the functions noted in | |||
| // https://clang.llvm.org/docs/LanguageExtensions.html | |||
| // | |||
| // Note: Use this macro to avoid an extra level of #ifdef __has_builtin check. | |||
| // http://releases.llvm.org/3.3/tools/clang/docs/LanguageExtensions.html | |||
| #ifdef __has_builtin | |||
| #define ABSL_HAVE_BUILTIN(x) __has_builtin(x) | |||
| #else | |||
| #define ABSL_HAVE_BUILTIN(x) 0 | |||
| #endif | |||
| #ifdef __has_feature | |||
| #define ABSL_HAVE_FEATURE(f) __has_feature(f) | |||
| #else | |||
| #define ABSL_HAVE_FEATURE(f) 0 | |||
| #endif | |||
| // Portable check for GCC minimum version: | |||
| // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html | |||
| #if defined(__GNUC__) && defined(__GNUC_MINOR__) | |||
| #define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) \ | |||
| (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y)) | |||
| #else | |||
| #define ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(x, y) 0 | |||
| #endif | |||
| #if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) | |||
| #define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) \ | |||
| (__clang_major__ > (x) || __clang_major__ == (x) && __clang_minor__ >= (y)) | |||
| #else | |||
| #define ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(x, y) 0 | |||
| #endif | |||
| // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. | |||
| // We assume __thread is supported on Linux or Asylo when compiled with Clang or | |||
| // compiled against libstdc++ with _GLIBCXX_HAVE_TLS defined. | |||
| #ifdef ABSL_HAVE_TLS | |||
| #error ABSL_HAVE_TLS cannot be directly set | |||
| #elif (defined(__linux__) || defined(__ASYLO__)) && \ | |||
| (defined(__clang__) || defined(_GLIBCXX_HAVE_TLS)) | |||
| #define ABSL_HAVE_TLS 1 | |||
| #endif | |||
| // ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE | |||
| // | |||
| // Checks whether `std::is_trivially_destructible<T>` is supported. | |||
| // | |||
| // Notes: All supported compilers using libc++ support this feature, as does | |||
| // gcc >= 4.8.1 using libstdc++, and Visual Studio. | |||
| #ifdef ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE | |||
| #error ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set | |||
| #elif defined(_LIBCPP_VERSION) || defined(_MSC_VER) || \ | |||
| (!defined(__clang__) && defined(__GLIBCXX__) && \ | |||
| ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(4, 8)) | |||
| #define ABSL_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1 | |||
| #endif | |||
| // ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE | |||
| // | |||
| // Checks whether `std::is_trivially_default_constructible<T>` and | |||
| // `std::is_trivially_copy_constructible<T>` are supported. | |||
| // ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE | |||
| // | |||
| // Checks whether `std::is_trivially_copy_assignable<T>` is supported. | |||
| // Notes: Clang with libc++ supports these features, as does gcc >= 7.4 with | |||
| // libstdc++, or gcc >= 8.2 with libc++, and Visual Studio (but not NVCC). | |||
| #if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) | |||
| #error ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set | |||
| #elif defined(ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE) | |||
| #error ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set | |||
| #elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \ | |||
| (!defined(__clang__) && \ | |||
| ((ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 4) && defined(__GLIBCXX__)) || \ | |||
| (ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(8, 2) && \ | |||
| defined(_LIBCPP_VERSION)))) || \ | |||
| (defined(_MSC_VER) && !defined(__NVCC__)) | |||
| #define ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1 | |||
| #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 | |||
| #endif | |||
| // ABSL_HAVE_THREAD_LOCAL | |||
| // | |||
| // Checks whether C++11's `thread_local` storage duration specifier is | |||
| // supported. | |||
| #ifdef ABSL_HAVE_THREAD_LOCAL | |||
| #error ABSL_HAVE_THREAD_LOCAL cannot be directly set | |||
| #elif defined(__APPLE__) | |||
| // Notes: | |||
| // * Xcode's clang did not support `thread_local` until version 8, and | |||
| // even then not for all iOS < 9.0. | |||
| // * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator | |||
| // targeting iOS 9.x. | |||
| // * Xcode 10 moves the deployment target check for iOS < 9.0 to link time | |||
| // making ABSL_HAVE_FEATURE unreliable there. | |||
| // | |||
| #if ABSL_HAVE_FEATURE(cxx_thread_local) && \ | |||
| !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0) | |||
| #define ABSL_HAVE_THREAD_LOCAL 1 | |||
| #endif | |||
| #else // !defined(__APPLE__) | |||
| #define ABSL_HAVE_THREAD_LOCAL 1 | |||
| #endif | |||
| // There are platforms for which TLS should not be used even though the compiler | |||
| // makes it seem like it's supported (Android NDK < r12b for example). | |||
| // This is primarily because of linker problems and toolchain misconfiguration: | |||
| // Abseil does not intend to support this indefinitely. Currently, the newest | |||
| // toolchain that we intend to support that requires this behavior is the | |||
| // r11 NDK - allowing for a 5 year support window on that means this option | |||
| // is likely to be removed around June of 2021. | |||
| // TLS isn't supported until NDK r12b per | |||
| // https://developer.android.com/ndk/downloads/revision_history.html | |||
| // Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in | |||
| // <android/ndk-version.h>. For NDK < r16, users should define these macros, | |||
| // e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. | |||
| #if defined(__ANDROID__) && defined(__clang__) | |||
| #if __has_include(<android/ndk-version.h>) | |||
| #include <android/ndk-version.h> | |||
| #endif // __has_include(<android/ndk-version.h>) | |||
| #if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \ | |||
| defined(__NDK_MINOR__) && \ | |||
| ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1))) | |||
| #undef ABSL_HAVE_TLS | |||
| #undef ABSL_HAVE_THREAD_LOCAL | |||
| #endif | |||
| #endif // defined(__ANDROID__) && defined(__clang__) | |||
| // ABSL_HAVE_INTRINSIC_INT128 | |||
| // | |||
| // Checks whether the __int128 compiler extension for a 128-bit integral type is | |||
| // supported. | |||
| // | |||
| // Note: __SIZEOF_INT128__ is defined by Clang and GCC when __int128 is | |||
| // supported, but we avoid using it in certain cases: | |||
| // * On Clang: | |||
| // * Building using Clang for Windows, where the Clang runtime library has | |||
| // 128-bit support only on LP64 architectures, but Windows is LLP64. | |||
| // * On Nvidia's nvcc: | |||
| // * nvcc also defines __GNUC__ and __SIZEOF_INT128__, but not all versions | |||
| // actually support __int128. | |||
| #ifdef ABSL_HAVE_INTRINSIC_INT128 | |||
| #error ABSL_HAVE_INTRINSIC_INT128 cannot be directly set | |||
| #elif defined(__SIZEOF_INT128__) | |||
| #if (defined(__clang__) && !defined(_WIN32)) || \ | |||
| (defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \ | |||
| (defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__)) | |||
| #define ABSL_HAVE_INTRINSIC_INT128 1 | |||
| #elif defined(__CUDACC__) | |||
| // __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a | |||
| // string explaining that it has been removed starting with CUDA 9. We use | |||
| // nested #ifs because there is no short-circuiting in the preprocessor. | |||
| // NOTE: `__CUDACC__` could be undefined while `__CUDACC_VER__` is defined. | |||
| #if __CUDACC_VER__ >= 70000 | |||
| #define ABSL_HAVE_INTRINSIC_INT128 1 | |||
| #endif // __CUDACC_VER__ >= 70000 | |||
| #endif // defined(__CUDACC__) | |||
| #endif // ABSL_HAVE_INTRINSIC_INT128 | |||
| // ABSL_HAVE_EXCEPTIONS | |||
| // | |||
| // Checks whether the compiler both supports and enables exceptions. Many | |||
| // compilers support a "no exceptions" mode that disables exceptions. | |||
| // | |||
| // Generally, when ABSL_HAVE_EXCEPTIONS is not defined: | |||
| // | |||
| // * Code using `throw` and `try` may not compile. | |||
| // * The `noexcept` specifier will still compile and behave as normal. | |||
| // * The `noexcept` operator may still return `false`. | |||
| // | |||
| // For further details, consult the compiler's documentation. | |||
| #ifdef ABSL_HAVE_EXCEPTIONS | |||
| #error ABSL_HAVE_EXCEPTIONS cannot be directly set. | |||
| #elif ABSL_INTERNAL_HAVE_MIN_CLANG_VERSION(3, 6) | |||
| // Clang >= 3.6 | |||
| #if ABSL_HAVE_FEATURE(cxx_exceptions) | |||
| #define ABSL_HAVE_EXCEPTIONS 1 | |||
| #endif // ABSL_HAVE_FEATURE(cxx_exceptions) | |||
| #elif defined(__clang__) | |||
| // Clang < 3.6 | |||
| // http://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html#the-exceptions-macro | |||
| #if defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) | |||
| #define ABSL_HAVE_EXCEPTIONS 1 | |||
| #endif // defined(__EXCEPTIONS) && ABSL_HAVE_FEATURE(cxx_exceptions) | |||
| // Handle remaining special cases and default to exceptions being supported. | |||
| #elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \ | |||
| !(ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(5, 0) && \ | |||
| !defined(__cpp_exceptions)) && \ | |||
| !(defined(_MSC_VER) && !defined(_CPPUNWIND)) | |||
| #define ABSL_HAVE_EXCEPTIONS 1 | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Platform Feature Checks | |||
| // ----------------------------------------------------------------------------- | |||
| // Currently supported operating systems and associated preprocessor | |||
| // symbols: | |||
| // | |||
| // Linux and Linux-derived __linux__ | |||
| // Android __ANDROID__ (implies __linux__) | |||
| // Linux (non-Android) __linux__ && !__ANDROID__ | |||
| // Darwin (macOS and iOS) __APPLE__ | |||
| // Akaros (http://akaros.org) __ros__ | |||
| // Windows _WIN32 | |||
| // NaCL __native_client__ | |||
| // AsmJS __asmjs__ | |||
| // WebAssembly __wasm__ | |||
| // Fuchsia __Fuchsia__ | |||
| // | |||
| // Note that since Android defines both __ANDROID__ and __linux__, one | |||
| // may probe for either Linux or Android by simply testing for __linux__. | |||
| // ABSL_HAVE_MMAP | |||
| // | |||
| // Checks whether the platform has an mmap(2) implementation as defined in | |||
| // POSIX.1-2001. | |||
| #ifdef ABSL_HAVE_MMAP | |||
| #error ABSL_HAVE_MMAP cannot be directly set | |||
| #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ | |||
| defined(_AIX) || defined(__ros__) || defined(__native_client__) || \ | |||
| defined(__asmjs__) || defined(__wasm__) || defined(__Fuchsia__) || \ | |||
| defined(__sun) || defined(__ASYLO__) || defined(__myriad2__) || \ | |||
| defined(__HAIKU__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ | |||
| defined(__QNX__) | |||
| #define ABSL_HAVE_MMAP 1 | |||
| #endif | |||
| // ABSL_HAVE_PTHREAD_GETSCHEDPARAM | |||
| // | |||
| // Checks whether the platform implements the pthread_(get|set)schedparam(3) | |||
| // functions as defined in POSIX.1-2001. | |||
| #ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM | |||
| #error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set | |||
| #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ | |||
| defined(_AIX) || defined(__ros__) || defined(__OpenBSD__) || \ | |||
| defined(__NetBSD__) | |||
| #define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1 | |||
| #endif | |||
| // ABSL_HAVE_SCHED_GETCPU | |||
| // | |||
| // Checks whether sched_getcpu is available. | |||
| #ifdef ABSL_HAVE_SCHED_GETCPU | |||
| #error ABSL_HAVE_SCHED_GETCPU cannot be directly set | |||
| #elif defined(__linux__) | |||
| #define ABSL_HAVE_SCHED_GETCPU 1 | |||
| #endif | |||
| // ABSL_HAVE_SCHED_YIELD | |||
| // | |||
| // Checks whether the platform implements sched_yield(2) as defined in | |||
| // POSIX.1-2001. | |||
| #ifdef ABSL_HAVE_SCHED_YIELD | |||
| #error ABSL_HAVE_SCHED_YIELD cannot be directly set | |||
| #elif defined(__linux__) || defined(__ros__) || defined(__native_client__) | |||
| #define ABSL_HAVE_SCHED_YIELD 1 | |||
| #endif | |||
| // ABSL_HAVE_SEMAPHORE_H | |||
| // | |||
| // Checks whether the platform supports the <semaphore.h> header and sem_init(3) | |||
| // family of functions as standardized in POSIX.1-2001. | |||
| // | |||
| // Note: While Apple provides <semaphore.h> for both iOS and macOS, it is | |||
| // explicitly deprecated and will cause build failures if enabled for those | |||
| // platforms. We side-step the issue by not defining it here for Apple | |||
| // platforms. | |||
| #ifdef ABSL_HAVE_SEMAPHORE_H | |||
| #error ABSL_HAVE_SEMAPHORE_H cannot be directly set | |||
| #elif defined(__linux__) || defined(__ros__) | |||
| #define ABSL_HAVE_SEMAPHORE_H 1 | |||
| #endif | |||
| // ABSL_HAVE_ALARM | |||
| // | |||
| // Checks whether the platform supports the <signal.h> header and alarm(2) | |||
| // function as standardized in POSIX.1-2001. | |||
| #ifdef ABSL_HAVE_ALARM | |||
| #error ABSL_HAVE_ALARM cannot be directly set | |||
| #elif defined(__GOOGLE_GRTE_VERSION__) | |||
| // feature tests for Google's GRTE | |||
| #define ABSL_HAVE_ALARM 1 | |||
| #elif defined(__GLIBC__) | |||
| // feature test for glibc | |||
| #define ABSL_HAVE_ALARM 1 | |||
| #elif defined(_MSC_VER) | |||
| // feature tests for Microsoft's library | |||
| #elif defined(__MINGW32__) | |||
| // mingw32 doesn't provide alarm(2): | |||
| // https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h | |||
| // mingw-w64 provides a no-op implementation: | |||
| // https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c | |||
| #elif defined(__EMSCRIPTEN__) | |||
| // emscripten doesn't support signals | |||
| #elif defined(__Fuchsia__) | |||
| // Signals don't exist on fuchsia. | |||
| #elif defined(__native_client__) | |||
| #else | |||
| // other standard libraries | |||
| #define ABSL_HAVE_ALARM 1 | |||
| #endif | |||
| // ABSL_IS_LITTLE_ENDIAN | |||
| // ABSL_IS_BIG_ENDIAN | |||
| // | |||
| // Checks the endianness of the platform. | |||
| // | |||
| // Notes: uses the built in endian macros provided by GCC (since 4.6) and | |||
| // Clang (since 3.2); see | |||
| // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html. | |||
| // Otherwise, if _WIN32, assume little endian. Otherwise, bail with an error. | |||
| #if defined(ABSL_IS_BIG_ENDIAN) | |||
| #error "ABSL_IS_BIG_ENDIAN cannot be directly set." | |||
| #endif | |||
| #if defined(ABSL_IS_LITTLE_ENDIAN) | |||
| #error "ABSL_IS_LITTLE_ENDIAN cannot be directly set." | |||
| #endif | |||
| #if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |||
| #define ABSL_IS_LITTLE_ENDIAN 1 | |||
| #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ | |||
| __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |||
| #define ABSL_IS_BIG_ENDIAN 1 | |||
| #elif defined(_WIN32) | |||
| #define ABSL_IS_LITTLE_ENDIAN 1 | |||
| #else | |||
| #error "absl endian detection needs to be set up for your compiler" | |||
| #endif | |||
| // macOS < 10.13 and iOS < 11 don't let you use <any>, <optional>, or <variant> | |||
| // even though the headers exist and are publicly noted to work, because the | |||
| // libc++ shared library shipped on the system doesn't have the requisite | |||
| // exported symbols. See https://github.com/abseil/abseil-cpp/issues/207 and | |||
| // https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes | |||
| // | |||
| // libc++ spells out the availability requirements in the file | |||
| // llvm-project/libcxx/include/__config via the #define | |||
| // _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS. | |||
| // | |||
| // Unfortunately, Apple initially mis-stated the requirements as macOS < 10.14 | |||
| // and iOS < 12 in the libc++ headers. This was corrected by | |||
| // https://github.com/llvm/llvm-project/commit/7fb40e1569dd66292b647f4501b85517e9247953 | |||
| // which subsequently made it into the XCode 12.5 release. We need to match the | |||
| // old (incorrect) conditions when built with old XCode, but can use the | |||
| // corrected earlier versions with new XCode. | |||
| #if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \ | |||
| ((_LIBCPP_VERSION >= 11000 && /* XCode 12.5 or later: */ \ | |||
| ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300) || \ | |||
| (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 110000) || \ | |||
| (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 40000) || \ | |||
| (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 110000))) || \ | |||
| (_LIBCPP_VERSION < 11000 && /* Pre-XCode 12.5: */ \ | |||
| ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400) || \ | |||
| (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ < 120000) || \ | |||
| (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ < 50000) || \ | |||
| (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) && \ | |||
| __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ < 120000)))) | |||
| #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 1 | |||
| #else | |||
| #define ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE 0 | |||
| #endif | |||
| // ABSL_HAVE_STD_ANY | |||
| // | |||
| // Checks whether C++17 std::any is available by checking whether <any> exists. | |||
| #ifdef ABSL_HAVE_STD_ANY | |||
| #error "ABSL_HAVE_STD_ANY cannot be directly set." | |||
| #endif | |||
| #ifdef __has_include | |||
| #if __has_include(<any>) && defined(__cplusplus) && __cplusplus >= 201703L && \ | |||
| !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE | |||
| #define ABSL_HAVE_STD_ANY 1 | |||
| #endif | |||
| #endif | |||
| // ABSL_HAVE_STD_OPTIONAL | |||
| // | |||
| // Checks whether C++17 std::optional is available. | |||
| #ifdef ABSL_HAVE_STD_OPTIONAL | |||
| #error "ABSL_HAVE_STD_OPTIONAL cannot be directly set." | |||
| #endif | |||
| #ifdef __has_include | |||
| #if __has_include(<optional>) && defined(__cplusplus) && \ | |||
| __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE | |||
| #define ABSL_HAVE_STD_OPTIONAL 1 | |||
| #endif | |||
| #endif | |||
| // ABSL_HAVE_STD_VARIANT | |||
| // | |||
| // Checks whether C++17 std::variant is available. | |||
| #ifdef ABSL_HAVE_STD_VARIANT | |||
| #error "ABSL_HAVE_STD_VARIANT cannot be directly set." | |||
| #endif | |||
| #ifdef __has_include | |||
| #if __has_include(<variant>) && defined(__cplusplus) && \ | |||
| __cplusplus >= 201703L && !ABSL_INTERNAL_APPLE_CXX17_TYPES_UNAVAILABLE | |||
| #define ABSL_HAVE_STD_VARIANT 1 | |||
| #endif | |||
| #endif | |||
| // ABSL_HAVE_STD_STRING_VIEW | |||
| // | |||
| // Checks whether C++17 std::string_view is available. | |||
| #ifdef ABSL_HAVE_STD_STRING_VIEW | |||
| #error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set." | |||
| #endif | |||
| #ifdef __has_include | |||
| #if __has_include(<string_view>) && defined(__cplusplus) && \ | |||
| __cplusplus >= 201703L | |||
| #define ABSL_HAVE_STD_STRING_VIEW 1 | |||
| #endif | |||
| #endif | |||
| // For MSVC, `__has_include` is supported in VS 2017 15.3, which is later than | |||
| // the support for <optional>, <any>, <string_view>, <variant>. So we use | |||
| // _MSC_VER to check whether we have VS 2017 RTM (when <optional>, <any>, | |||
| // <string_view>, <variant> is implemented) or higher. Also, `__cplusplus` is | |||
| // not correctly set by MSVC, so we use `_MSVC_LANG` to check the language | |||
| // version. | |||
| // TODO(zhangxy): fix tests before enabling aliasing for `std::any`. | |||
| #if defined(_MSC_VER) && _MSC_VER >= 1910 && \ | |||
| ((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || \ | |||
| (defined(__cplusplus) && __cplusplus > 201402)) | |||
| // #define ABSL_HAVE_STD_ANY 1 | |||
| #define ABSL_HAVE_STD_OPTIONAL 1 | |||
| #define ABSL_HAVE_STD_VARIANT 1 | |||
| #define ABSL_HAVE_STD_STRING_VIEW 1 | |||
| #endif | |||
| // ABSL_USES_STD_ANY | |||
| // | |||
| // Indicates whether absl::any is an alias for std::any. | |||
| #if !defined(ABSL_OPTION_USE_STD_ANY) | |||
| #error options.h is misconfigured. | |||
| #elif ABSL_OPTION_USE_STD_ANY == 0 || \ | |||
| (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) | |||
| #undef ABSL_USES_STD_ANY | |||
| #elif ABSL_OPTION_USE_STD_ANY == 1 || \ | |||
| (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) | |||
| #define ABSL_USES_STD_ANY 1 | |||
| #else | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // ABSL_USES_STD_OPTIONAL | |||
| // | |||
| // Indicates whether absl::optional is an alias for std::optional. | |||
| #if !defined(ABSL_OPTION_USE_STD_OPTIONAL) | |||
| #error options.h is misconfigured. | |||
| #elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ | |||
| (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) | |||
| #undef ABSL_USES_STD_OPTIONAL | |||
| #elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ | |||
| (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) | |||
| #define ABSL_USES_STD_OPTIONAL 1 | |||
| #else | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // ABSL_USES_STD_VARIANT | |||
| // | |||
| // Indicates whether absl::variant is an alias for std::variant. | |||
| #if !defined(ABSL_OPTION_USE_STD_VARIANT) | |||
| #error options.h is misconfigured. | |||
| #elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ | |||
| (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) | |||
| #undef ABSL_USES_STD_VARIANT | |||
| #elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ | |||
| (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) | |||
| #define ABSL_USES_STD_VARIANT 1 | |||
| #else | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // ABSL_USES_STD_STRING_VIEW | |||
| // | |||
| // Indicates whether absl::string_view is an alias for std::string_view. | |||
| #if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) | |||
| #error options.h is misconfigured. | |||
| #elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ | |||
| (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ | |||
| !defined(ABSL_HAVE_STD_STRING_VIEW)) | |||
| #undef ABSL_USES_STD_STRING_VIEW | |||
| #elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ | |||
| (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ | |||
| defined(ABSL_HAVE_STD_STRING_VIEW)) | |||
| #define ABSL_USES_STD_STRING_VIEW 1 | |||
| #else | |||
| #error options.h is misconfigured. | |||
| #endif | |||
| // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION | |||
| // SEH exception from emplace for variant<SomeStruct> when constructing the | |||
| // struct can throw. This defeats some of variant_test and | |||
| // variant_exception_safety_test. | |||
| #if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG) | |||
| #define ABSL_INTERNAL_MSVC_2017_DBG_MODE | |||
| #endif | |||
| // ABSL_INTERNAL_MANGLED_NS | |||
| // ABSL_INTERNAL_MANGLED_BACKREFERENCE | |||
| // | |||
| // Internal macros for building up mangled names in our internal fork of CCTZ. | |||
| // This implementation detail is only needed and provided for the MSVC build. | |||
| // | |||
| // These macros both expand to string literals. ABSL_INTERNAL_MANGLED_NS is | |||
| // the mangled spelling of the `absl` namespace, and | |||
| // ABSL_INTERNAL_MANGLED_BACKREFERENCE is a back-reference integer representing | |||
| // the proper count to skip past the CCTZ fork namespace names. (This number | |||
| // is one larger when there is an inline namespace name to skip.) | |||
| #if defined(_MSC_VER) | |||
| #if ABSL_OPTION_USE_INLINE_NAMESPACE == 0 | |||
| #define ABSL_INTERNAL_MANGLED_NS "absl" | |||
| #define ABSL_INTERNAL_MANGLED_BACKREFERENCE "5" | |||
| #else | |||
| #define ABSL_INTERNAL_MANGLED_NS \ | |||
| ABSL_INTERNAL_TOKEN_STR(ABSL_OPTION_INLINE_NAMESPACE_NAME) \ | |||
| "@absl" | |||
| #define ABSL_INTERNAL_MANGLED_BACKREFERENCE "6" | |||
| #endif | |||
| #endif | |||
| // ABSL_DLL | |||
| // | |||
| // When building Abseil as a DLL, this macro expands to `__declspec(dllexport)` | |||
| // so we can annotate symbols appropriately as being exported. When used in | |||
| // headers consuming a DLL, this macro expands to `__declspec(dllimport)` so | |||
| // that consumers know the symbol is defined inside the DLL. In all other cases, | |||
| // the macro expands to nothing. | |||
| #if defined(_MSC_VER) | |||
| #if defined(ABSL_BUILD_DLL) | |||
| #define ABSL_DLL __declspec(dllexport) | |||
| #elif defined(ABSL_CONSUME_DLL) | |||
| #define ABSL_DLL __declspec(dllimport) | |||
| #else | |||
| #define ABSL_DLL | |||
| #endif | |||
| #else | |||
| #define ABSL_DLL | |||
| #endif // defined(_MSC_VER) | |||
| // ABSL_HAVE_MEMORY_SANITIZER | |||
| // | |||
| // MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of | |||
| // a compiler instrumentation module and a run-time library. | |||
| #ifdef ABSL_HAVE_MEMORY_SANITIZER | |||
| #error "ABSL_HAVE_MEMORY_SANITIZER cannot be directly set." | |||
| #elif !defined(__native_client__) && ABSL_HAVE_FEATURE(memory_sanitizer) | |||
| #define ABSL_HAVE_MEMORY_SANITIZER 1 | |||
| #endif | |||
| // ABSL_HAVE_THREAD_SANITIZER | |||
| // | |||
| // ThreadSanitizer (TSan) is a fast data race detector. | |||
| #ifdef ABSL_HAVE_THREAD_SANITIZER | |||
| #error "ABSL_HAVE_THREAD_SANITIZER cannot be directly set." | |||
| #elif defined(__SANITIZE_THREAD__) | |||
| #define ABSL_HAVE_THREAD_SANITIZER 1 | |||
| #elif ABSL_HAVE_FEATURE(thread_sanitizer) | |||
| #define ABSL_HAVE_THREAD_SANITIZER 1 | |||
| #endif | |||
| // ABSL_HAVE_ADDRESS_SANITIZER | |||
| // | |||
| // AddressSanitizer (ASan) is a fast memory error detector. | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| #error "ABSL_HAVE_ADDRESS_SANITIZER cannot be directly set." | |||
| #elif defined(__SANITIZE_ADDRESS__) | |||
| #define ABSL_HAVE_ADDRESS_SANITIZER 1 | |||
| #elif ABSL_HAVE_FEATURE(address_sanitizer) | |||
| #define ABSL_HAVE_ADDRESS_SANITIZER 1 | |||
| #endif | |||
| // ABSL_HAVE_HWADDRESS_SANITIZER | |||
| // | |||
| // Hardware-Assisted AddressSanitizer (or HWASAN) is even faster than asan | |||
| // memory error detector which can use CPU features like ARM TBI, Intel LAM or | |||
| // AMD UAI. | |||
| #ifdef ABSL_HAVE_HWADDRESS_SANITIZER | |||
| #error "ABSL_HAVE_HWADDRESS_SANITIZER cannot be directly set." | |||
| #elif defined(__SANITIZE_HWADDRESS__) | |||
| #define ABSL_HAVE_HWADDRESS_SANITIZER 1 | |||
| #elif ABSL_HAVE_FEATURE(hwaddress_sanitizer) | |||
| #define ABSL_HAVE_HWADDRESS_SANITIZER 1 | |||
| #endif | |||
| // ABSL_HAVE_LEAK_SANITIZER | |||
| // | |||
| // LeakSanitizer (or lsan) is a detector of memory leaks. | |||
| // https://clang.llvm.org/docs/LeakSanitizer.html | |||
| // https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer | |||
| // | |||
| // The macro ABSL_HAVE_LEAK_SANITIZER can be used to detect at compile-time | |||
| // whether the LeakSanitizer is potentially available. However, just because the | |||
| // LeakSanitizer is available does not mean it is active. Use the | |||
| // always-available run-time interface in //absl/debugging/leak_check.h for | |||
| // interacting with LeakSanitizer. | |||
| #ifdef ABSL_HAVE_LEAK_SANITIZER | |||
| #error "ABSL_HAVE_LEAK_SANITIZER cannot be directly set." | |||
| #elif defined(LEAK_SANITIZER) | |||
| // GCC provides no method for detecting the presense of the standalone | |||
| // LeakSanitizer (-fsanitize=leak), so GCC users of -fsanitize=leak should also | |||
| // use -DLEAK_SANITIZER. | |||
| #define ABSL_HAVE_LEAK_SANITIZER 1 | |||
| // Clang standalone LeakSanitizer (-fsanitize=leak) | |||
| #elif ABSL_HAVE_FEATURE(leak_sanitizer) | |||
| #define ABSL_HAVE_LEAK_SANITIZER 1 | |||
| #elif defined(ABSL_HAVE_ADDRESS_SANITIZER) | |||
| // GCC or Clang using the LeakSanitizer integrated into AddressSanitizer. | |||
| #define ABSL_HAVE_LEAK_SANITIZER 1 | |||
| #endif | |||
| // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION | |||
| // | |||
| // Class template argument deduction is a language feature added in C++17. | |||
| #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION | |||
| #error "ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION cannot be directly set." | |||
| #elif defined(__cpp_deduction_guides) | |||
| #define ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 1 | |||
| #endif | |||
| // ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL | |||
| // | |||
| // Prior to C++17, static constexpr variables defined in classes required a | |||
| // separate definition outside of the class body, for example: | |||
| // | |||
| // class Foo { | |||
| // static constexpr int kBar = 0; | |||
| // }; | |||
| // constexpr int Foo::kBar; | |||
| // | |||
| // In C++17, these variables defined in classes are considered inline variables, | |||
| // and the extra declaration is redundant. Since some compilers warn on the | |||
| // extra declarations, ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL can be used | |||
| // conditionally ignore them: | |||
| // | |||
| // #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL | |||
| // constexpr int Foo::kBar; | |||
| // #endif | |||
| #if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ | |||
| ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L | |||
| #define ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL 1 | |||
| #endif | |||
| // `ABSL_INTERNAL_HAS_RTTI` determines whether abseil is being compiled with | |||
| // RTTI support. | |||
| #ifdef ABSL_INTERNAL_HAS_RTTI | |||
| #error ABSL_INTERNAL_HAS_RTTI cannot be directly set | |||
| #elif !defined(__GNUC__) || defined(__GXX_RTTI) | |||
| #define ABSL_INTERNAL_HAS_RTTI 1 | |||
| #endif // !defined(__GNUC__) || defined(__GXX_RTTI) | |||
| // ABSL_INTERNAL_HAVE_SSE is used for compile-time detection of SSE support. | |||
| // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of | |||
| // which architectures support the various x86 instruction sets. | |||
| #ifdef ABSL_INTERNAL_HAVE_SSE | |||
| #error ABSL_INTERNAL_HAVE_SSE cannot be directly set | |||
| #elif defined(__SSE__) | |||
| #define ABSL_INTERNAL_HAVE_SSE 1 | |||
| #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1) | |||
| // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 1 | |||
| // indicates that at least SSE was targeted with the /arch:SSE option. | |||
| // All x86-64 processors support SSE, so support can be assumed. | |||
| // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros | |||
| #define ABSL_INTERNAL_HAVE_SSE 1 | |||
| #endif | |||
| // ABSL_INTERNAL_HAVE_SSE2 is used for compile-time detection of SSE2 support. | |||
| // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of | |||
| // which architectures support the various x86 instruction sets. | |||
| #ifdef ABSL_INTERNAL_HAVE_SSE2 | |||
| #error ABSL_INTERNAL_HAVE_SSE2 cannot be directly set | |||
| #elif defined(__SSE2__) | |||
| #define ABSL_INTERNAL_HAVE_SSE2 1 | |||
| #elif defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) | |||
| // MSVC only defines _M_IX86_FP for x86 32-bit code, and _M_IX86_FP >= 2 | |||
| // indicates that at least SSE2 was targeted with the /arch:SSE2 option. | |||
| // All x86-64 processors support SSE2, so support can be assumed. | |||
| // https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros | |||
| #define ABSL_INTERNAL_HAVE_SSE2 1 | |||
| #endif | |||
| // ABSL_INTERNAL_HAVE_SSSE3 is used for compile-time detection of SSSE3 support. | |||
| // See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html for an overview of | |||
| // which architectures support the various x86 instruction sets. | |||
| // | |||
| // MSVC does not have a mode that targets SSSE3 at compile-time. To use SSSE3 | |||
| // with MSVC requires either assuming that the code will only every run on CPUs | |||
| // that support SSSE3, otherwise __cpuid() can be used to detect support at | |||
| // runtime and fallback to a non-SSSE3 implementation when SSSE3 is unsupported | |||
| // by the CPU. | |||
| #ifdef ABSL_INTERNAL_HAVE_SSSE3 | |||
| #error ABSL_INTERNAL_HAVE_SSSE3 cannot be directly set | |||
| #elif defined(__SSSE3__) | |||
| #define ABSL_INTERNAL_HAVE_SSSE3 1 | |||
| #endif | |||
| // ABSL_INTERNAL_HAVE_ARM_NEON is used for compile-time detection of NEON (ARM | |||
| // SIMD). | |||
| #ifdef ABSL_INTERNAL_HAVE_ARM_NEON | |||
| #error ABSL_INTERNAL_HAVE_ARM_NEON cannot be directly set | |||
| #elif defined(__ARM_NEON) | |||
| #define ABSL_INTERNAL_HAVE_ARM_NEON 1 | |||
| #endif | |||
| #endif // ABSL_BASE_CONFIG_H_ | |||
| @@ -0,0 +1,78 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // kConstInit | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // A constructor tag used to mark an object as safe for use as a global | |||
| // variable, avoiding the usual lifetime issues that can affect globals. | |||
| #ifndef ABSL_BASE_CONST_INIT_H_ | |||
| #define ABSL_BASE_CONST_INIT_H_ | |||
| #include "absl/base/config.h" | |||
| // In general, objects with static storage duration (such as global variables) | |||
| // can trigger tricky object lifetime situations. Attempting to access them | |||
| // from the constructors or destructors of other global objects can result in | |||
| // undefined behavior, unless their constructors and destructors are designed | |||
| // with this issue in mind. | |||
| // | |||
| // The normal way to deal with this issue in C++11 is to use constant | |||
| // initialization and trivial destructors. | |||
| // | |||
| // Constant initialization is guaranteed to occur before any other code | |||
| // executes. Constructors that are declared 'constexpr' are eligible for | |||
| // constant initialization. You can annotate a variable declaration with the | |||
| // ABSL_CONST_INIT macro to express this intent. For compilers that support | |||
| // it, this annotation will cause a compilation error for declarations that | |||
| // aren't subject to constant initialization (perhaps because a runtime value | |||
| // was passed as a constructor argument). | |||
| // | |||
| // On program shutdown, lifetime issues can be avoided on global objects by | |||
| // ensuring that they contain trivial destructors. A class has a trivial | |||
| // destructor unless it has a user-defined destructor, a virtual method or base | |||
| // class, or a data member or base class with a non-trivial destructor of its | |||
| // own. Objects with static storage duration and a trivial destructor are not | |||
| // cleaned up on program shutdown, and are thus safe to access from other code | |||
| // running during shutdown. | |||
| // | |||
| // For a few core Abseil classes, we make a best effort to allow for safe global | |||
| // instances, even though these classes have non-trivial destructors. These | |||
| // objects can be created with the absl::kConstInit tag. For example: | |||
| // ABSL_CONST_INIT absl::Mutex global_mutex(absl::kConstInit); | |||
| // | |||
| // The line above declares a global variable of type absl::Mutex which can be | |||
| // accessed at any point during startup or shutdown. global_mutex's destructor | |||
| // will still run, but will not invalidate the object. Note that C++ specifies | |||
| // that accessing an object after its destructor has run results in undefined | |||
| // behavior, but this pattern works on the toolchains we support. | |||
| // | |||
| // The absl::kConstInit tag should only be used to define objects with static | |||
| // or thread_local storage duration. | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| enum ConstInitType | |||
| { | |||
| kConstInit, | |||
| }; | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_CONST_INIT_H_ | |||
| @@ -0,0 +1,487 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // This file defines dynamic annotations for use with dynamic analysis tool | |||
| // such as valgrind, PIN, etc. | |||
| // | |||
| // Dynamic annotation is a source code annotation that affects the generated | |||
| // code (that is, the annotation is not a comment). Each such annotation is | |||
| // attached to a particular instruction and/or to a particular object (address) | |||
| // in the program. | |||
| // | |||
| // The annotations that should be used by users are macros in all upper-case | |||
| // (e.g., ABSL_ANNOTATE_THREAD_NAME). | |||
| // | |||
| // Actual implementation of these macros may differ depending on the dynamic | |||
| // analysis tool being used. | |||
| // | |||
| // This file supports the following configurations: | |||
| // - Dynamic Annotations enabled (with static thread-safety warnings disabled). | |||
| // In this case, macros expand to functions implemented by Thread Sanitizer, | |||
| // when building with TSan. When not provided an external implementation, | |||
| // dynamic_annotations.cc provides no-op implementations. | |||
| // | |||
| // - Static Clang thread-safety warnings enabled. | |||
| // When building with a Clang compiler that supports thread-safety warnings, | |||
| // a subset of annotations can be statically-checked at compile-time. We | |||
| // expand these macros to static-inline functions that can be analyzed for | |||
| // thread-safety, but afterwards elided when building the final binary. | |||
| // | |||
| // - All annotations are disabled. | |||
| // If neither Dynamic Annotations nor Clang thread-safety warnings are | |||
| // enabled, then all annotation-macros expand to empty. | |||
| #ifndef ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ | |||
| #define ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ | |||
| #include <stddef.h> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #ifdef __cplusplus | |||
| #include "absl/base/macros.h" | |||
| #endif | |||
| // TODO(rogeeff): Remove after the backward compatibility period. | |||
| #include "absl/base/internal/dynamic_annotations.h" // IWYU pragma: export | |||
| // ------------------------------------------------------------------------- | |||
| // Decide which features are enabled. | |||
| #ifdef ABSL_HAVE_THREAD_SANITIZER | |||
| #define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 | |||
| #define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 | |||
| #else | |||
| #define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 | |||
| #define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 | |||
| #define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 | |||
| // Clang provides limited support for static thread-safety analysis through a | |||
| // feature called Annotalysis. We configure macro-definitions according to | |||
| // whether Annotalysis support is available. When running in opt-mode, GCC | |||
| // will issue a warning, if these attributes are compiled. Only include them | |||
| // when compiling using Clang. | |||
| #if defined(__clang__) | |||
| #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 1 | |||
| #if !defined(SWIG) | |||
| #define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 | |||
| #endif | |||
| #else | |||
| #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 | |||
| #endif | |||
| // Read/write annotations are enabled in Annotalysis mode; disabled otherwise. | |||
| #define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ | |||
| ABSL_INTERNAL_ANNOTALYSIS_ENABLED | |||
| #endif // ABSL_HAVE_THREAD_SANITIZER | |||
| #ifdef __cplusplus | |||
| #define ABSL_INTERNAL_BEGIN_EXTERN_C \ | |||
| extern "C" \ | |||
| { | |||
| #define ABSL_INTERNAL_END_EXTERN_C } // extern "C" | |||
| #define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F | |||
| #define ABSL_INTERNAL_STATIC_INLINE inline | |||
| #else | |||
| #define ABSL_INTERNAL_BEGIN_EXTERN_C // empty | |||
| #define ABSL_INTERNAL_END_EXTERN_C // empty | |||
| #define ABSL_INTERNAL_GLOBAL_SCOPED(F) F | |||
| #define ABSL_INTERNAL_STATIC_INLINE static inline | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define race annotations. | |||
| #if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 | |||
| // Some of the symbols used in this section (e.g. AnnotateBenignRaceSized) are | |||
| // defined by the compiler-based santizer implementation, not by the Abseil | |||
| // library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. | |||
| // ------------------------------------------------------------- | |||
| // Annotations that suppress errors. It is usually better to express the | |||
| // program's synchronization using the other annotations, but these can be used | |||
| // when all else fails. | |||
| // Report that we may have a benign race at `pointer`, with size | |||
| // "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the | |||
| // point where `pointer` has been allocated, preferably close to the point | |||
| // where the race happens. See also ABSL_ANNOTATE_BENIGN_RACE_STATIC. | |||
| #define ABSL_ANNOTATE_BENIGN_RACE(pointer, description) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ | |||
| (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) | |||
| // Same as ABSL_ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to | |||
| // the memory range [`address`, `address`+`size`). | |||
| #define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ | |||
| (__FILE__, __LINE__, address, size, description) | |||
| // Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. | |||
| // This annotation could be useful if you want to skip expensive race analysis | |||
| // during some period of program execution, e.g. during initialization. | |||
| #define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ | |||
| (__FILE__, __LINE__, enable) | |||
| // ------------------------------------------------------------- | |||
| // Annotations useful for debugging. | |||
| // Report the current thread `name` to a race detector. | |||
| #define ABSL_ANNOTATE_THREAD_NAME(name) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName) \ | |||
| (__FILE__, __LINE__, name) | |||
| // ------------------------------------------------------------- | |||
| // Annotations useful when implementing locks. They are not normally needed by | |||
| // modules that merely use locks. The `lock` argument is a pointer to the lock | |||
| // object. | |||
| // Report that a lock has been created at address `lock`. | |||
| #define ABSL_ANNOTATE_RWLOCK_CREATE(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate) \ | |||
| (__FILE__, __LINE__, lock) | |||
| // Report that a linker initialized lock has been created at address `lock`. | |||
| #ifdef ABSL_HAVE_THREAD_SANITIZER | |||
| #define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ | |||
| (__FILE__, __LINE__, lock) | |||
| #else | |||
| #define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ | |||
| ABSL_ANNOTATE_RWLOCK_CREATE(lock) | |||
| #endif | |||
| // Report that the lock at address `lock` is about to be destroyed. | |||
| #define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy) \ | |||
| (__FILE__, __LINE__, lock) | |||
| // Report that the lock at address `lock` has been acquired. | |||
| // `is_w`=1 for writer lock, `is_w`=0 for reader lock. | |||
| #define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ | |||
| (__FILE__, __LINE__, lock, is_w) | |||
| // Report that the lock at address `lock` is about to be released. | |||
| // `is_w`=1 for writer lock, `is_w`=0 for reader lock. | |||
| #define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ | |||
| (__FILE__, __LINE__, lock, is_w) | |||
| // Apply ABSL_ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. | |||
| #define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ | |||
| namespace \ | |||
| { \ | |||
| class static_var##_annotator \ | |||
| { \ | |||
| public: \ | |||
| static_var##_annotator() \ | |||
| { \ | |||
| ABSL_ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), #static_var ": " description); \ | |||
| } \ | |||
| }; \ | |||
| static static_var##_annotator the##static_var##_annotator; \ | |||
| } // namespace | |||
| // Function prototypes of annotations provided by the compiler-based sanitizer | |||
| // implementation. | |||
| ABSL_INTERNAL_BEGIN_EXTERN_C | |||
| void AnnotateRWLockCreate(const char* file, int line, const volatile void* lock); | |||
| void AnnotateRWLockCreateStatic(const char* file, int line, const volatile void* lock); | |||
| void AnnotateRWLockDestroy(const char* file, int line, const volatile void* lock); | |||
| void AnnotateRWLockAcquired(const char* file, int line, const volatile void* lock, long is_w); // NOLINT | |||
| void AnnotateRWLockReleased(const char* file, int line, const volatile void* lock, long is_w); // NOLINT | |||
| void AnnotateBenignRace(const char* file, int line, const volatile void* address, const char* description); | |||
| void AnnotateBenignRaceSized(const char* file, int line, const volatile void* address, size_t size, const char* description); | |||
| void AnnotateThreadName(const char* file, int line, const char* name); | |||
| void AnnotateEnableRaceDetection(const char* file, int line, int enable); | |||
| ABSL_INTERNAL_END_EXTERN_C | |||
| #else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 | |||
| #define ABSL_ANNOTATE_RWLOCK_CREATE(lock) // empty | |||
| #define ABSL_ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty | |||
| #define ABSL_ANNOTATE_RWLOCK_DESTROY(lock) // empty | |||
| #define ABSL_ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty | |||
| #define ABSL_ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty | |||
| #define ABSL_ANNOTATE_BENIGN_RACE(address, description) // empty | |||
| #define ABSL_ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty | |||
| #define ABSL_ANNOTATE_THREAD_NAME(name) // empty | |||
| #define ABSL_ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty | |||
| #define ABSL_ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty | |||
| #endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED | |||
| // ------------------------------------------------------------------------- | |||
| // Define memory annotations. | |||
| #ifdef ABSL_HAVE_MEMORY_SANITIZER | |||
| #include <sanitizer/msan_interface.h> | |||
| #define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ | |||
| __msan_unpoison(address, size) | |||
| #define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ | |||
| __msan_allocated_memory(address, size) | |||
| #else // !defined(ABSL_HAVE_MEMORY_SANITIZER) | |||
| // TODO(rogeeff): remove this branch | |||
| #ifdef ABSL_HAVE_THREAD_SANITIZER | |||
| #define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ | |||
| do \ | |||
| { \ | |||
| (void)(address); \ | |||
| (void)(size); \ | |||
| } while (0) | |||
| #define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ | |||
| do \ | |||
| { \ | |||
| (void)(address); \ | |||
| (void)(size); \ | |||
| } while (0) | |||
| #else | |||
| #define ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty | |||
| #define ABSL_ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty | |||
| #endif | |||
| #endif // ABSL_HAVE_MEMORY_SANITIZER | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_READS_BEGIN/_END attributes. | |||
| #if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| #define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ | |||
| __attribute((exclusive_lock_function("*"))) | |||
| #define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ | |||
| __attribute((unlock_function("*"))) | |||
| #else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| #define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty | |||
| #define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty | |||
| #endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_READS_BEGIN/_END annotations. | |||
| #if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 | |||
| // Some of the symbols used in this section (e.g. AnnotateIgnoreReadsBegin) are | |||
| // defined by the compiler-based implementation, not by the Abseil | |||
| // library. Therefore they do not use ABSL_INTERNAL_C_SYMBOL. | |||
| // Request the analysis tool to ignore all reads in the current thread until | |||
| // ABSL_ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey | |||
| // reads, while still checking other reads and all writes. | |||
| // See also ABSL_ANNOTATE_UNPROTECTED_READ. | |||
| #define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \ | |||
| (__FILE__, __LINE__) | |||
| // Stop ignoring reads. | |||
| #define ABSL_ANNOTATE_IGNORE_READS_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \ | |||
| (__FILE__, __LINE__) | |||
| // Function prototypes of annotations provided by the compiler-based sanitizer | |||
| // implementation. | |||
| ABSL_INTERNAL_BEGIN_EXTERN_C | |||
| void AnnotateIgnoreReadsBegin(const char* file, int line) | |||
| ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE; | |||
| void AnnotateIgnoreReadsEnd(const char* file, int line) ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE; | |||
| ABSL_INTERNAL_END_EXTERN_C | |||
| #elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) | |||
| // When Annotalysis is enabled without Dynamic Annotations, the use of | |||
| // static-inline functions allows the annotations to be read at compile-time, | |||
| // while still letting the compiler elide the functions from the final build. | |||
| // | |||
| // TODO(delesley) -- The exclusive lock here ignores writes as well, but | |||
| // allows IGNORE_READS_AND_WRITES to work properly. | |||
| #define ABSL_ANNOTATE_IGNORE_READS_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED( \ | |||
| ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsBegin) \ | |||
| ) \ | |||
| () | |||
| #define ABSL_ANNOTATE_IGNORE_READS_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED( \ | |||
| ABSL_INTERNAL_C_SYMBOL(AbslInternalAnnotateIgnoreReadsEnd) \ | |||
| ) \ | |||
| () | |||
| ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( | |||
| AbslInternalAnnotateIgnoreReadsBegin | |||
| )() | |||
| ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE | |||
| { | |||
| } | |||
| ABSL_INTERNAL_STATIC_INLINE void ABSL_INTERNAL_C_SYMBOL( | |||
| AbslInternalAnnotateIgnoreReadsEnd | |||
| )() | |||
| ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE | |||
| { | |||
| } | |||
| #else | |||
| #define ABSL_ANNOTATE_IGNORE_READS_BEGIN() // empty | |||
| #define ABSL_ANNOTATE_IGNORE_READS_END() // empty | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_WRITES_BEGIN/_END annotations. | |||
| #if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 | |||
| // Similar to ABSL_ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. | |||
| #define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin) \ | |||
| (__FILE__, __LINE__) | |||
| // Stop ignoring writes. | |||
| #define ABSL_ANNOTATE_IGNORE_WRITES_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd) \ | |||
| (__FILE__, __LINE__) | |||
| // Function prototypes of annotations provided by the compiler-based sanitizer | |||
| // implementation. | |||
| ABSL_INTERNAL_BEGIN_EXTERN_C | |||
| void AnnotateIgnoreWritesBegin(const char* file, int line); | |||
| void AnnotateIgnoreWritesEnd(const char* file, int line); | |||
| ABSL_INTERNAL_END_EXTERN_C | |||
| #else | |||
| #define ABSL_ANNOTATE_IGNORE_WRITES_BEGIN() // empty | |||
| #define ABSL_ANNOTATE_IGNORE_WRITES_END() // empty | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define the ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more | |||
| // primitive annotations defined above. | |||
| // | |||
| // Instead of doing | |||
| // ABSL_ANNOTATE_IGNORE_READS_BEGIN(); | |||
| // ... = x; | |||
| // ABSL_ANNOTATE_IGNORE_READS_END(); | |||
| // one can use | |||
| // ... = ABSL_ANNOTATE_UNPROTECTED_READ(x); | |||
| #if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) | |||
| // Start ignoring all memory accesses (both reads and writes). | |||
| #define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ | |||
| do \ | |||
| { \ | |||
| ABSL_ANNOTATE_IGNORE_READS_BEGIN(); \ | |||
| ABSL_ANNOTATE_IGNORE_WRITES_BEGIN(); \ | |||
| } while (0) | |||
| // Stop ignoring both reads and writes. | |||
| #define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() \ | |||
| do \ | |||
| { \ | |||
| ABSL_ANNOTATE_IGNORE_WRITES_END(); \ | |||
| ABSL_ANNOTATE_IGNORE_READS_END(); \ | |||
| } while (0) | |||
| #ifdef __cplusplus | |||
| // ABSL_ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. | |||
| #define ABSL_ANNOTATE_UNPROTECTED_READ(x) \ | |||
| absl::base_internal::AnnotateUnprotectedRead(x) | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| template<typename T> | |||
| inline T AnnotateUnprotectedRead(const volatile T& x) | |||
| { // NOLINT | |||
| ABSL_ANNOTATE_IGNORE_READS_BEGIN(); | |||
| T res = x; | |||
| ABSL_ANNOTATE_IGNORE_READS_END(); | |||
| return res; | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif | |||
| #else | |||
| #define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty | |||
| #define ABSL_ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty | |||
| #define ABSL_ANNOTATE_UNPROTECTED_READ(x) (x) | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Address sanitizer annotations | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| // Describe the current state of a contiguous container such as e.g. | |||
| // std::vector or std::string. For more details see | |||
| // sanitizer/common_interface_defs.h, which is provided by the compiler. | |||
| #include <sanitizer/common_interface_defs.h> | |||
| #define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ | |||
| __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) | |||
| #define ABSL_ADDRESS_SANITIZER_REDZONE(name) \ | |||
| struct \ | |||
| { \ | |||
| alignas(8) char x[8]; \ | |||
| } name | |||
| #else | |||
| #define ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) // empty | |||
| #define ABSL_ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") | |||
| #endif // ABSL_HAVE_ADDRESS_SANITIZER | |||
| // ------------------------------------------------------------------------- | |||
| // Undefine the macros intended only for this file. | |||
| #undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED | |||
| #undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_BEGIN_EXTERN_C | |||
| #undef ABSL_INTERNAL_END_EXTERN_C | |||
| #undef ABSL_INTERNAL_STATIC_INLINE | |||
| #endif // ABSL_BASE_DYNAMIC_ANNOTATIONS_H_ | |||
| @@ -0,0 +1,225 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ | |||
| #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ | |||
| #include <atomic> | |||
| #include <cassert> | |||
| #include <cstdint> | |||
| #include <utility> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #if defined(_MSC_VER) && !defined(__clang__) | |||
| #define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 | |||
| #else | |||
| #define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 | |||
| #endif | |||
| #if defined(_MSC_VER) | |||
| #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 | |||
| #else | |||
| #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| template<typename T> | |||
| class AtomicHook; | |||
| // To workaround AtomicHook not being constant-initializable on some platforms, | |||
| // prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES` | |||
| // instead of `ABSL_CONST_INIT`. | |||
| #if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT | |||
| #define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT | |||
| #else | |||
| #define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES | |||
| #endif | |||
| // `AtomicHook` is a helper class, templatized on a raw function pointer type, | |||
| // for implementing Abseil customization hooks. It is a callable object that | |||
| // dispatches to the registered hook. Objects of type `AtomicHook` must have | |||
| // static or thread storage duration. | |||
| // | |||
| // A default constructed object performs a no-op (and returns a default | |||
| // constructed object) if no hook has been registered. | |||
| // | |||
| // Hooks can be pre-registered via constant initialization, for example: | |||
| // | |||
| // ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook<void(*)()> | |||
| // my_hook(DefaultAction); | |||
| // | |||
| // and then changed at runtime via a call to `Store()`. | |||
| // | |||
| // Reads and writes guarantee memory_order_acquire/memory_order_release | |||
| // semantics. | |||
| template<typename ReturnType, typename... Args> | |||
| class AtomicHook<ReturnType (*)(Args...)> | |||
| { | |||
| public: | |||
| using FnPtr = ReturnType (*)(Args...); | |||
| // Constructs an object that by default performs a no-op (and | |||
| // returns a default constructed object) when no hook as been registered. | |||
| constexpr AtomicHook() : | |||
| AtomicHook(DummyFunction) | |||
| { | |||
| } | |||
| // Constructs an object that by default dispatches to/returns the | |||
| // pre-registered default_fn when no hook has been registered at runtime. | |||
| #if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT | |||
| explicit constexpr AtomicHook(FnPtr default_fn) : | |||
| hook_(default_fn), | |||
| default_fn_(default_fn) | |||
| { | |||
| } | |||
| #elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT | |||
| explicit constexpr AtomicHook(FnPtr default_fn) : | |||
| hook_(kUninitialized), | |||
| default_fn_(default_fn) | |||
| { | |||
| } | |||
| #else | |||
| // As of January 2020, on all known versions of MSVC this constructor runs in | |||
| // the global constructor sequence. If `Store()` is called by a dynamic | |||
| // initializer, we want to preserve the value, even if this constructor runs | |||
| // after the call to `Store()`. If not, `hook_` will be | |||
| // zero-initialized by the linker and we have no need to set it. | |||
| // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html | |||
| explicit constexpr AtomicHook(FnPtr default_fn) : | |||
| /* hook_(deliberately omitted), */ default_fn_(default_fn) | |||
| { | |||
| static_assert(kUninitialized == 0, "here we rely on zero-initialization"); | |||
| } | |||
| #endif | |||
| // Stores the provided function pointer as the value for this hook. | |||
| // | |||
| // This is intended to be called once. Multiple calls are legal only if the | |||
| // same function pointer is provided for each call. The store is implemented | |||
| // as a memory_order_release operation, and read accesses are implemented as | |||
| // memory_order_acquire. | |||
| void Store(FnPtr fn) | |||
| { | |||
| bool success = DoStore(fn); | |||
| static_cast<void>(success); | |||
| assert(success); | |||
| } | |||
| // Invokes the registered callback. If no callback has yet been registered, a | |||
| // default-constructed object of the appropriate type is returned instead. | |||
| template<typename... CallArgs> | |||
| ReturnType operator()(CallArgs&&... args) const | |||
| { | |||
| return DoLoad()(std::forward<CallArgs>(args)...); | |||
| } | |||
| // Returns the registered callback, or nullptr if none has been registered. | |||
| // Useful if client code needs to conditionalize behavior based on whether a | |||
| // callback was registered. | |||
| // | |||
| // Note that atomic_hook.Load()() and atomic_hook() have different semantics: | |||
| // operator()() will perform a no-op if no callback was registered, while | |||
| // Load()() will dereference a null function pointer. Prefer operator()() to | |||
| // Load()() unless you must conditionalize behavior on whether a hook was | |||
| // registered. | |||
| FnPtr Load() const | |||
| { | |||
| FnPtr ptr = DoLoad(); | |||
| return (ptr == DummyFunction) ? nullptr : ptr; | |||
| } | |||
| private: | |||
| static ReturnType DummyFunction(Args...) | |||
| { | |||
| return ReturnType(); | |||
| } | |||
| // Current versions of MSVC (as of September 2017) have a broken | |||
| // implementation of std::atomic<T*>: Its constructor attempts to do the | |||
| // equivalent of a reinterpret_cast in a constexpr context, which is not | |||
| // allowed. | |||
| // | |||
| // This causes an issue when building with LLVM under Windows. To avoid this, | |||
| // we use a less-efficient, intptr_t-based implementation on Windows. | |||
| #if ABSL_HAVE_WORKING_ATOMIC_POINTER | |||
| // Return the stored value, or DummyFunction if no value has been stored. | |||
| FnPtr DoLoad() const | |||
| { | |||
| return hook_.load(std::memory_order_acquire); | |||
| } | |||
| // Store the given value. Returns false if a different value was already | |||
| // stored to this object. | |||
| bool DoStore(FnPtr fn) | |||
| { | |||
| assert(fn); | |||
| FnPtr expected = default_fn_; | |||
| const bool store_succeeded = hook_.compare_exchange_strong( | |||
| expected, fn, std::memory_order_acq_rel, std::memory_order_acquire | |||
| ); | |||
| const bool same_value_already_stored = (expected == fn); | |||
| return store_succeeded || same_value_already_stored; | |||
| } | |||
| std::atomic<FnPtr> hook_; | |||
| #else // !ABSL_HAVE_WORKING_ATOMIC_POINTER | |||
| // Use a sentinel value unlikely to be the address of an actual function. | |||
| static constexpr intptr_t kUninitialized = 0; | |||
| static_assert(sizeof(intptr_t) >= sizeof(FnPtr), "intptr_t can't contain a function pointer"); | |||
| FnPtr DoLoad() const | |||
| { | |||
| const intptr_t value = hook_.load(std::memory_order_acquire); | |||
| if (value == kUninitialized) | |||
| { | |||
| return default_fn_; | |||
| } | |||
| return reinterpret_cast<FnPtr>(value); | |||
| } | |||
| bool DoStore(FnPtr fn) | |||
| { | |||
| assert(fn); | |||
| const auto value = reinterpret_cast<intptr_t>(fn); | |||
| intptr_t expected = kUninitialized; | |||
| const bool store_succeeded = hook_.compare_exchange_strong( | |||
| expected, value, std::memory_order_acq_rel, std::memory_order_acquire | |||
| ); | |||
| const bool same_value_already_stored = (expected == value); | |||
| return store_succeeded || same_value_already_stored; | |||
| } | |||
| std::atomic<intptr_t> hook_; | |||
| #endif | |||
| const FnPtr default_fn_; | |||
| }; | |||
| #undef ABSL_HAVE_WORKING_ATOMIC_POINTER | |||
| #undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ | |||
| @@ -0,0 +1,36 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ | |||
| #define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ | |||
| #include "absl/base/internal/atomic_hook.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace atomic_hook_internal | |||
| { | |||
| using VoidF = void (*)(); | |||
| extern absl::base_internal::AtomicHook<VoidF> func; | |||
| extern int default_func_calls; | |||
| void DefaultFunc(); | |||
| void RegisterFunc(VoidF func); | |||
| } // namespace atomic_hook_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ | |||
| @@ -0,0 +1,168 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: cycleclock.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines a `CycleClock`, which yields the value and frequency | |||
| // of a cycle counter that increments at a rate that is approximately constant. | |||
| // | |||
| // NOTE: | |||
| // | |||
| // The cycle counter frequency is not necessarily related to the core clock | |||
| // frequency and should not be treated as such. That is, `CycleClock` cycles are | |||
| // not necessarily "CPU cycles" and code should not rely on that behavior, even | |||
| // if experimentally observed. | |||
| // | |||
| // An arbitrary offset may have been added to the counter at power on. | |||
| // | |||
| // On some platforms, the rate and offset of the counter may differ | |||
| // slightly when read from different CPUs of a multiprocessor. Usually, | |||
| // we try to ensure that the operating system adjusts values periodically | |||
| // so that values agree approximately. If you need stronger guarantees, | |||
| // consider using alternate interfaces. | |||
| // | |||
| // The CPU is not required to maintain the ordering of a cycle counter read | |||
| // with respect to surrounding instructions. | |||
| #ifndef ABSL_BASE_INTERNAL_CYCLECLOCK_H_ | |||
| #define ABSL_BASE_INTERNAL_CYCLECLOCK_H_ | |||
| #include <atomic> | |||
| #include <cstdint> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/internal/unscaledcycleclock.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| using CycleClockSourceFunc = int64_t (*)(); | |||
| // ----------------------------------------------------------------------------- | |||
| // CycleClock | |||
| // ----------------------------------------------------------------------------- | |||
| class CycleClock | |||
| { | |||
| public: | |||
| // CycleClock::Now() | |||
| // | |||
| // Returns the value of a cycle counter that counts at a rate that is | |||
| // approximately constant. | |||
| static int64_t Now(); | |||
| // CycleClock::Frequency() | |||
| // | |||
| // Returns the amount by which `CycleClock::Now()` increases per second. Note | |||
| // that this value may not necessarily match the core CPU clock frequency. | |||
| static double Frequency(); | |||
| private: | |||
| #if ABSL_USE_UNSCALED_CYCLECLOCK | |||
| static CycleClockSourceFunc LoadCycleClockSource(); | |||
| #ifdef NDEBUG | |||
| #ifdef ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY | |||
| // Not debug mode and the UnscaledCycleClock frequency is the CPU | |||
| // frequency. Scale the CycleClock to prevent overflow if someone | |||
| // tries to represent the time as cycles since the Unix epoch. | |||
| static constexpr int32_t kShift = 1; | |||
| #else | |||
| // Not debug mode and the UnscaledCycleClock isn't operating at the | |||
| // raw CPU frequency. There is no need to do any scaling, so don't | |||
| // needlessly sacrifice precision. | |||
| static constexpr int32_t kShift = 0; | |||
| #endif | |||
| #else // NDEBUG | |||
| // In debug mode use a different shift to discourage depending on a | |||
| // particular shift value. | |||
| static constexpr int32_t kShift = 2; | |||
| #endif // NDEBUG | |||
| static constexpr double kFrequencyScale = 1.0 / (1 << kShift); | |||
| ABSL_CONST_INIT static std::atomic<CycleClockSourceFunc> cycle_clock_source_; | |||
| #endif // ABSL_USE_UNSCALED_CYCLECLOC | |||
| CycleClock() = delete; // no instances | |||
| CycleClock(const CycleClock&) = delete; | |||
| CycleClock& operator=(const CycleClock&) = delete; | |||
| friend class CycleClockSource; | |||
| }; | |||
| class CycleClockSource | |||
| { | |||
| private: | |||
| // CycleClockSource::Register() | |||
| // | |||
| // Register a function that provides an alternate source for the unscaled CPU | |||
| // cycle count value. The source function must be async signal safe, must not | |||
| // call CycleClock::Now(), and must have a frequency that matches that of the | |||
| // unscaled clock used by CycleClock. A nullptr value resets CycleClock to use | |||
| // the default source. | |||
| static void Register(CycleClockSourceFunc source); | |||
| }; | |||
| #if ABSL_USE_UNSCALED_CYCLECLOCK | |||
| inline CycleClockSourceFunc CycleClock::LoadCycleClockSource() | |||
| { | |||
| #if !defined(__x86_64__) | |||
| // Optimize for the common case (no callback) by first doing a relaxed load; | |||
| // this is significantly faster on non-x86 platforms. | |||
| if (cycle_clock_source_.load(std::memory_order_relaxed) == nullptr) | |||
| { | |||
| return nullptr; | |||
| } | |||
| #endif // !defined(__x86_64__) | |||
| // This corresponds to the store(std::memory_order_release) in | |||
| // CycleClockSource::Register, and makes sure that any updates made prior to | |||
| // registering the callback are visible to this thread before the callback | |||
| // is invoked. | |||
| return cycle_clock_source_.load(std::memory_order_acquire); | |||
| } | |||
| // Accessing globals in inlined code in Window DLLs is problematic. | |||
| #ifndef _WIN32 | |||
| inline int64_t CycleClock::Now() | |||
| { | |||
| auto fn = LoadCycleClockSource(); | |||
| if (fn == nullptr) | |||
| { | |||
| return base_internal::UnscaledCycleClock::Now() >> kShift; | |||
| } | |||
| return fn() >> kShift; | |||
| } | |||
| #endif | |||
| inline double CycleClock::Frequency() | |||
| { | |||
| return kFrequencyScale * base_internal::UnscaledCycleClock::Frequency(); | |||
| } | |||
| #endif // ABSL_USE_UNSCALED_CYCLECLOCK | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_CYCLECLOCK_H_ | |||
| @@ -0,0 +1,177 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Functions for directly invoking mmap() via syscall, avoiding the case where | |||
| // mmap() has been locally overridden. | |||
| #ifndef ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ | |||
| #define ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ | |||
| #include "absl/base/config.h" | |||
| #ifdef ABSL_HAVE_MMAP | |||
| #include <sys/mman.h> | |||
| #ifdef __linux__ | |||
| #include <sys/types.h> | |||
| #ifdef __BIONIC__ | |||
| #include <sys/syscall.h> | |||
| #else | |||
| #include <syscall.h> | |||
| #endif | |||
| #include <linux/unistd.h> | |||
| #include <unistd.h> | |||
| #include <cerrno> | |||
| #include <cstdarg> | |||
| #include <cstdint> | |||
| #ifdef __mips__ | |||
| // Include definitions of the ABI currently in use. | |||
| #if defined(__BIONIC__) || !defined(__GLIBC__) | |||
| // Android doesn't have sgidefs.h, but does have asm/sgidefs.h, which has the | |||
| // definitions we need. | |||
| #include <asm/sgidefs.h> | |||
| #else | |||
| #include <sgidefs.h> | |||
| #endif // __BIONIC__ || !__GLIBC__ | |||
| #endif // __mips__ | |||
| // SYS_mmap and SYS_munmap are not defined in Android. | |||
| #ifdef __BIONIC__ | |||
| extern "C" void* __mmap2(void*, size_t, int, int, int, size_t); | |||
| #if defined(__NR_mmap) && !defined(SYS_mmap) | |||
| #define SYS_mmap __NR_mmap | |||
| #endif | |||
| #ifndef SYS_munmap | |||
| #define SYS_munmap __NR_munmap | |||
| #endif | |||
| #endif // __BIONIC__ | |||
| #if defined(__NR_mmap2) && !defined(SYS_mmap2) | |||
| #define SYS_mmap2 __NR_mmap2 | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Platform specific logic extracted from | |||
| // https://chromium.googlesource.com/linux-syscall-support/+/master/linux_syscall_support.h | |||
| inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, off64_t offset) noexcept | |||
| { | |||
| #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ | |||
| defined(__m68k__) || defined(__sh__) || \ | |||
| (defined(__hppa__) && !defined(__LP64__)) || \ | |||
| (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ | |||
| (defined(__PPC__) && !defined(__PPC64__)) || \ | |||
| (defined(__riscv) && __riscv_xlen == 32) || \ | |||
| (defined(__s390__) && !defined(__s390x__)) || \ | |||
| (defined(__sparc__) && !defined(__arch64__)) | |||
| // On these architectures, implement mmap with mmap2. | |||
| static int pagesize = 0; | |||
| if (pagesize == 0) | |||
| { | |||
| #if defined(__wasm__) || defined(__asmjs__) | |||
| pagesize = getpagesize(); | |||
| #else | |||
| pagesize = sysconf(_SC_PAGESIZE); | |||
| #endif | |||
| } | |||
| if (offset < 0 || offset % pagesize != 0) | |||
| { | |||
| errno = EINVAL; | |||
| return MAP_FAILED; | |||
| } | |||
| #ifdef __BIONIC__ | |||
| // SYS_mmap2 has problems on Android API level <= 16. | |||
| // Workaround by invoking __mmap2() instead. | |||
| return __mmap2(start, length, prot, flags, fd, offset / pagesize); | |||
| #else | |||
| return reinterpret_cast<void*>( | |||
| syscall(SYS_mmap2, start, length, prot, flags, fd, static_cast<off_t>(offset / pagesize)) | |||
| ); | |||
| #endif | |||
| #elif defined(__s390x__) | |||
| // On s390x, mmap() arguments are passed in memory. | |||
| unsigned long buf[6] = {reinterpret_cast<unsigned long>(start), // NOLINT | |||
| static_cast<unsigned long>(length), // NOLINT | |||
| static_cast<unsigned long>(prot), // NOLINT | |||
| static_cast<unsigned long>(flags), // NOLINT | |||
| static_cast<unsigned long>(fd), // NOLINT | |||
| static_cast<unsigned long>(offset)}; // NOLINT | |||
| return reinterpret_cast<void*>(syscall(SYS_mmap, buf)); | |||
| #elif defined(__x86_64__) | |||
| // The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. | |||
| // We need to explicitly cast to an unsigned 64 bit type to avoid implicit | |||
| // sign extension. We can't cast pointers directly because those are | |||
| // 32 bits, and gcc will dump ugly warnings about casting from a pointer | |||
| // to an integer of a different size. We also need to make sure __off64_t | |||
| // isn't truncated to 32-bits under x32. | |||
| #define MMAP_SYSCALL_ARG(x) ((uint64_t)(uintptr_t)(x)) | |||
| return reinterpret_cast<void*>( | |||
| syscall(SYS_mmap, MMAP_SYSCALL_ARG(start), MMAP_SYSCALL_ARG(length), MMAP_SYSCALL_ARG(prot), MMAP_SYSCALL_ARG(flags), MMAP_SYSCALL_ARG(fd), static_cast<uint64_t>(offset)) | |||
| ); | |||
| #undef MMAP_SYSCALL_ARG | |||
| #else // Remaining 64-bit aritectures. | |||
| static_assert(sizeof(unsigned long) == 8, "Platform is not 64-bit"); | |||
| return reinterpret_cast<void*>( | |||
| syscall(SYS_mmap, start, length, prot, flags, fd, offset) | |||
| ); | |||
| #endif | |||
| } | |||
| inline int DirectMunmap(void* start, size_t length) | |||
| { | |||
| return static_cast<int>(syscall(SYS_munmap, start, length)); | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #else // !__linux__ | |||
| // For non-linux platforms where we have mmap, just dispatch directly to the | |||
| // actual mmap()/munmap() methods. | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| inline void* DirectMmap(void* start, size_t length, int prot, int flags, int fd, off_t offset) | |||
| { | |||
| return mmap(start, length, prot, flags, fd, offset); | |||
| } | |||
| inline int DirectMunmap(void* start, size_t length) | |||
| { | |||
| return munmap(start, length); | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // __linux__ | |||
| #endif // ABSL_HAVE_MMAP | |||
| #endif // ABSL_BASE_INTERNAL_DIRECT_MMAP_H_ | |||
| @@ -0,0 +1,416 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // This file defines dynamic annotations for use with dynamic analysis tool | |||
| // such as valgrind, PIN, etc. | |||
| // | |||
| // Dynamic annotation is a source code annotation that affects the generated | |||
| // code (that is, the annotation is not a comment). Each such annotation is | |||
| // attached to a particular instruction and/or to a particular object (address) | |||
| // in the program. | |||
| // | |||
| // The annotations that should be used by users are macros in all upper-case | |||
| // (e.g., ANNOTATE_THREAD_NAME). | |||
| // | |||
| // Actual implementation of these macros may differ depending on the dynamic | |||
| // analysis tool being used. | |||
| // | |||
| // This file supports the following configurations: | |||
| // - Dynamic Annotations enabled (with static thread-safety warnings disabled). | |||
| // In this case, macros expand to functions implemented by Thread Sanitizer, | |||
| // when building with TSan. When not provided an external implementation, | |||
| // dynamic_annotations.cc provides no-op implementations. | |||
| // | |||
| // - Static Clang thread-safety warnings enabled. | |||
| // When building with a Clang compiler that supports thread-safety warnings, | |||
| // a subset of annotations can be statically-checked at compile-time. We | |||
| // expand these macros to static-inline functions that can be analyzed for | |||
| // thread-safety, but afterwards elided when building the final binary. | |||
| // | |||
| // - All annotations are disabled. | |||
| // If neither Dynamic Annotations nor Clang thread-safety warnings are | |||
| // enabled, then all annotation-macros expand to empty. | |||
| #ifndef ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ | |||
| #define ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ | |||
| #include <stddef.h> | |||
| #include "absl/base/config.h" | |||
| // ------------------------------------------------------------------------- | |||
| // Decide which features are enabled | |||
| #ifndef DYNAMIC_ANNOTATIONS_ENABLED | |||
| #define DYNAMIC_ANNOTATIONS_ENABLED 0 | |||
| #endif | |||
| #if defined(__clang__) && !defined(SWIG) | |||
| #define ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED 1 | |||
| #endif | |||
| #if DYNAMIC_ANNOTATIONS_ENABLED != 0 | |||
| #define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 1 | |||
| #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED 0 | |||
| #define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED 1 | |||
| #else | |||
| #define ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED 0 | |||
| #define ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED 0 | |||
| #define ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED 0 | |||
| // Clang provides limited support for static thread-safety analysis through a | |||
| // feature called Annotalysis. We configure macro-definitions according to | |||
| // whether Annotalysis support is available. When running in opt-mode, GCC | |||
| // will issue a warning, if these attributes are compiled. Only include them | |||
| // when compiling using Clang. | |||
| // ANNOTALYSIS_ENABLED == 1 when IGNORE_READ_ATTRIBUTE_ENABLED == 1 | |||
| #define ABSL_INTERNAL_ANNOTALYSIS_ENABLED \ | |||
| defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| // Read/write annotations are enabled in Annotalysis mode; disabled otherwise. | |||
| #define ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED \ | |||
| ABSL_INTERNAL_ANNOTALYSIS_ENABLED | |||
| #endif | |||
| // Memory annotations are also made available to LLVM's Memory Sanitizer | |||
| #if defined(ABSL_HAVE_MEMORY_SANITIZER) && !defined(__native_client__) | |||
| #define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 1 | |||
| #endif | |||
| #ifndef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED | |||
| #define ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED 0 | |||
| #endif | |||
| #ifdef __cplusplus | |||
| #define ABSL_INTERNAL_BEGIN_EXTERN_C \ | |||
| extern "C" \ | |||
| { | |||
| #define ABSL_INTERNAL_END_EXTERN_C } // extern "C" | |||
| #define ABSL_INTERNAL_GLOBAL_SCOPED(F) ::F | |||
| #define ABSL_INTERNAL_STATIC_INLINE inline | |||
| #else | |||
| #define ABSL_INTERNAL_BEGIN_EXTERN_C // empty | |||
| #define ABSL_INTERNAL_END_EXTERN_C // empty | |||
| #define ABSL_INTERNAL_GLOBAL_SCOPED(F) F | |||
| #define ABSL_INTERNAL_STATIC_INLINE static inline | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define race annotations. | |||
| #if ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 1 | |||
| // ------------------------------------------------------------- | |||
| // Annotations that suppress errors. It is usually better to express the | |||
| // program's synchronization using the other annotations, but these can be used | |||
| // when all else fails. | |||
| // Report that we may have a benign race at `pointer`, with size | |||
| // "sizeof(*(pointer))". `pointer` must be a non-void* pointer. Insert at the | |||
| // point where `pointer` has been allocated, preferably close to the point | |||
| // where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. | |||
| #define ANNOTATE_BENIGN_RACE(pointer, description) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ | |||
| (__FILE__, __LINE__, pointer, sizeof(*(pointer)), description) | |||
| // Same as ANNOTATE_BENIGN_RACE(`address`, `description`), but applies to | |||
| // the memory range [`address`, `address`+`size`). | |||
| #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateBenignRaceSized) \ | |||
| (__FILE__, __LINE__, address, size, description) | |||
| // Enable (`enable`!=0) or disable (`enable`==0) race detection for all threads. | |||
| // This annotation could be useful if you want to skip expensive race analysis | |||
| // during some period of program execution, e.g. during initialization. | |||
| #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateEnableRaceDetection) \ | |||
| (__FILE__, __LINE__, enable) | |||
| // ------------------------------------------------------------- | |||
| // Annotations useful for debugging. | |||
| // Report the current thread `name` to a race detector. | |||
| #define ANNOTATE_THREAD_NAME(name) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateThreadName) \ | |||
| (__FILE__, __LINE__, name) | |||
| // ------------------------------------------------------------- | |||
| // Annotations useful when implementing locks. They are not normally needed by | |||
| // modules that merely use locks. The `lock` argument is a pointer to the lock | |||
| // object. | |||
| // Report that a lock has been created at address `lock`. | |||
| #define ANNOTATE_RWLOCK_CREATE(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreate) \ | |||
| (__FILE__, __LINE__, lock) | |||
| // Report that a linker initialized lock has been created at address `lock`. | |||
| #ifdef ABSL_HAVE_THREAD_SANITIZER | |||
| #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockCreateStatic) \ | |||
| (__FILE__, __LINE__, lock) | |||
| #else | |||
| #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock) | |||
| #endif | |||
| // Report that the lock at address `lock` is about to be destroyed. | |||
| #define ANNOTATE_RWLOCK_DESTROY(lock) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockDestroy) \ | |||
| (__FILE__, __LINE__, lock) | |||
| // Report that the lock at address `lock` has been acquired. | |||
| // `is_w`=1 for writer lock, `is_w`=0 for reader lock. | |||
| #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockAcquired) \ | |||
| (__FILE__, __LINE__, lock, is_w) | |||
| // Report that the lock at address `lock` is about to be released. | |||
| // `is_w`=1 for writer lock, `is_w`=0 for reader lock. | |||
| #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateRWLockReleased) \ | |||
| (__FILE__, __LINE__, lock, is_w) | |||
| // Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable `static_var`. | |||
| #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ | |||
| namespace \ | |||
| { \ | |||
| class static_var##_annotator \ | |||
| { \ | |||
| public: \ | |||
| static_var##_annotator() \ | |||
| { \ | |||
| ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), #static_var ": " description); \ | |||
| } \ | |||
| }; \ | |||
| static static_var##_annotator the##static_var##_annotator; \ | |||
| } // namespace | |||
| #else // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED == 0 | |||
| #define ANNOTATE_RWLOCK_CREATE(lock) // empty | |||
| #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) // empty | |||
| #define ANNOTATE_RWLOCK_DESTROY(lock) // empty | |||
| #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) // empty | |||
| #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) // empty | |||
| #define ANNOTATE_BENIGN_RACE(address, description) // empty | |||
| #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) // empty | |||
| #define ANNOTATE_THREAD_NAME(name) // empty | |||
| #define ANNOTATE_ENABLE_RACE_DETECTION(enable) // empty | |||
| #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) // empty | |||
| #endif // ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED | |||
| // ------------------------------------------------------------------------- | |||
| // Define memory annotations. | |||
| #if ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 1 | |||
| #include <sanitizer/msan_interface.h> | |||
| #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ | |||
| __msan_unpoison(address, size) | |||
| #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ | |||
| __msan_allocated_memory(address, size) | |||
| #else // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED == 0 | |||
| #if DYNAMIC_ANNOTATIONS_ENABLED == 1 | |||
| #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ | |||
| do \ | |||
| { \ | |||
| (void)(address); \ | |||
| (void)(size); \ | |||
| } while (0) | |||
| #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \ | |||
| do \ | |||
| { \ | |||
| (void)(address); \ | |||
| (void)(size); \ | |||
| } while (0) | |||
| #else | |||
| #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) // empty | |||
| #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) // empty | |||
| #endif | |||
| #endif // ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_READS_BEGIN/_END attributes. | |||
| #if defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| #define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE \ | |||
| __attribute((exclusive_lock_function("*"))) | |||
| #define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE \ | |||
| __attribute((unlock_function("*"))) | |||
| #else // !defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| #define ABSL_INTERNAL_IGNORE_READS_BEGIN_ATTRIBUTE // empty | |||
| #define ABSL_INTERNAL_IGNORE_READS_END_ATTRIBUTE // empty | |||
| #endif // defined(ABSL_INTERNAL_IGNORE_READS_ATTRIBUTE_ENABLED) | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_READS_BEGIN/_END annotations. | |||
| #if ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED == 1 | |||
| // Request the analysis tool to ignore all reads in the current thread until | |||
| // ANNOTATE_IGNORE_READS_END is called. Useful to ignore intentional racey | |||
| // reads, while still checking other reads and all writes. | |||
| // See also ANNOTATE_UNPROTECTED_READ. | |||
| #define ANNOTATE_IGNORE_READS_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsBegin) \ | |||
| (__FILE__, __LINE__) | |||
| // Stop ignoring reads. | |||
| #define ANNOTATE_IGNORE_READS_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreReadsEnd) \ | |||
| (__FILE__, __LINE__) | |||
| #elif defined(ABSL_INTERNAL_ANNOTALYSIS_ENABLED) | |||
| // When Annotalysis is enabled without Dynamic Annotations, the use of | |||
| // static-inline functions allows the annotations to be read at compile-time, | |||
| // while still letting the compiler elide the functions from the final build. | |||
| // | |||
| // TODO(delesley) -- The exclusive lock here ignores writes as well, but | |||
| // allows IGNORE_READS_AND_WRITES to work properly. | |||
| #define ANNOTATE_IGNORE_READS_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsBegin) \ | |||
| () | |||
| #define ANNOTATE_IGNORE_READS_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AbslInternalAnnotateIgnoreReadsEnd) \ | |||
| () | |||
| #else | |||
| #define ANNOTATE_IGNORE_READS_BEGIN() // empty | |||
| #define ANNOTATE_IGNORE_READS_END() // empty | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define IGNORE_WRITES_BEGIN/_END annotations. | |||
| #if ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED == 1 | |||
| // Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. | |||
| #define ANNOTATE_IGNORE_WRITES_BEGIN() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesBegin) \ | |||
| (__FILE__, __LINE__) | |||
| // Stop ignoring writes. | |||
| #define ANNOTATE_IGNORE_WRITES_END() \ | |||
| ABSL_INTERNAL_GLOBAL_SCOPED(AnnotateIgnoreWritesEnd) \ | |||
| (__FILE__, __LINE__) | |||
| #else | |||
| #define ANNOTATE_IGNORE_WRITES_BEGIN() // empty | |||
| #define ANNOTATE_IGNORE_WRITES_END() // empty | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Define the ANNOTATE_IGNORE_READS_AND_WRITES_* annotations using the more | |||
| // primitive annotations defined above. | |||
| // | |||
| // Instead of doing | |||
| // ANNOTATE_IGNORE_READS_BEGIN(); | |||
| // ... = x; | |||
| // ANNOTATE_IGNORE_READS_END(); | |||
| // one can use | |||
| // ... = ANNOTATE_UNPROTECTED_READ(x); | |||
| #if defined(ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED) | |||
| // Start ignoring all memory accesses (both reads and writes). | |||
| #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ | |||
| do \ | |||
| { \ | |||
| ANNOTATE_IGNORE_READS_BEGIN(); \ | |||
| ANNOTATE_IGNORE_WRITES_BEGIN(); \ | |||
| } while (0) | |||
| // Stop ignoring both reads and writes. | |||
| #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ | |||
| do \ | |||
| { \ | |||
| ANNOTATE_IGNORE_WRITES_END(); \ | |||
| ANNOTATE_IGNORE_READS_END(); \ | |||
| } while (0) | |||
| #ifdef __cplusplus | |||
| // ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. | |||
| #define ANNOTATE_UNPROTECTED_READ(x) \ | |||
| absl::base_internal::AnnotateUnprotectedRead(x) | |||
| #endif | |||
| #else | |||
| #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() // empty | |||
| #define ANNOTATE_IGNORE_READS_AND_WRITES_END() // empty | |||
| #define ANNOTATE_UNPROTECTED_READ(x) (x) | |||
| #endif | |||
| // ------------------------------------------------------------------------- | |||
| // Address sanitizer annotations | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| // Describe the current state of a contiguous container such as e.g. | |||
| // std::vector or std::string. For more details see | |||
| // sanitizer/common_interface_defs.h, which is provided by the compiler. | |||
| #include <sanitizer/common_interface_defs.h> | |||
| #define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) \ | |||
| __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid) | |||
| #define ADDRESS_SANITIZER_REDZONE(name) \ | |||
| struct \ | |||
| { \ | |||
| char x[8] __attribute__((aligned(8))); \ | |||
| } name | |||
| #else | |||
| #define ANNOTATE_CONTIGUOUS_CONTAINER(beg, end, old_mid, new_mid) | |||
| #define ADDRESS_SANITIZER_REDZONE(name) static_assert(true, "") | |||
| #endif // ABSL_HAVE_ADDRESS_SANITIZER | |||
| // ------------------------------------------------------------------------- | |||
| // Undefine the macros intended only for this file. | |||
| #undef ABSL_INTERNAL_RACE_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_MEMORY_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_READS_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_WRITES_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_ANNOTALYSIS_ENABLED | |||
| #undef ABSL_INTERNAL_READS_WRITES_ANNOTATIONS_ENABLED | |||
| #undef ABSL_INTERNAL_BEGIN_EXTERN_C | |||
| #undef ABSL_INTERNAL_END_EXTERN_C | |||
| #undef ABSL_INTERNAL_STATIC_INLINE | |||
| #endif // ABSL_BASE_INTERNAL_DYNAMIC_ANNOTATIONS_H_ | |||
| @@ -0,0 +1,472 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_ENDIAN_H_ | |||
| #define ABSL_BASE_INTERNAL_ENDIAN_H_ | |||
| #include <cstdint> | |||
| #include <cstdlib> | |||
| #include "absl/base/casts.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/internal/unaligned_access.h" | |||
| #include "absl/base/port.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| inline uint64_t gbswap_64(uint64_t host_int) | |||
| { | |||
| #if ABSL_HAVE_BUILTIN(__builtin_bswap64) || defined(__GNUC__) | |||
| return __builtin_bswap64(host_int); | |||
| #elif defined(_MSC_VER) | |||
| return _byteswap_uint64(host_int); | |||
| #else | |||
| return (((host_int & uint64_t{0xFF}) << 56) | ((host_int & uint64_t{0xFF00}) << 40) | ((host_int & uint64_t{0xFF0000}) << 24) | ((host_int & uint64_t{0xFF000000}) << 8) | ((host_int & uint64_t{0xFF00000000}) >> 8) | ((host_int & uint64_t{0xFF0000000000}) >> 24) | ((host_int & uint64_t{0xFF000000000000}) >> 40) | ((host_int & uint64_t{0xFF00000000000000}) >> 56)); | |||
| #endif | |||
| } | |||
| inline uint32_t gbswap_32(uint32_t host_int) | |||
| { | |||
| #if ABSL_HAVE_BUILTIN(__builtin_bswap32) || defined(__GNUC__) | |||
| return __builtin_bswap32(host_int); | |||
| #elif defined(_MSC_VER) | |||
| return _byteswap_ulong(host_int); | |||
| #else | |||
| return (((host_int & uint32_t{0xFF}) << 24) | ((host_int & uint32_t{0xFF00}) << 8) | ((host_int & uint32_t{0xFF0000}) >> 8) | ((host_int & uint32_t{0xFF000000}) >> 24)); | |||
| #endif | |||
| } | |||
| inline uint16_t gbswap_16(uint16_t host_int) | |||
| { | |||
| #if ABSL_HAVE_BUILTIN(__builtin_bswap16) || defined(__GNUC__) | |||
| return __builtin_bswap16(host_int); | |||
| #elif defined(_MSC_VER) | |||
| return _byteswap_ushort(host_int); | |||
| #else | |||
| return (((host_int & uint16_t{0xFF}) << 8) | ((host_int & uint16_t{0xFF00}) >> 8)); | |||
| #endif | |||
| } | |||
| #ifdef ABSL_IS_LITTLE_ENDIAN | |||
| // Portable definitions for htonl (host-to-network) and friends on little-endian | |||
| // architectures. | |||
| inline uint16_t ghtons(uint16_t x) | |||
| { | |||
| return gbswap_16(x); | |||
| } | |||
| inline uint32_t ghtonl(uint32_t x) | |||
| { | |||
| return gbswap_32(x); | |||
| } | |||
| inline uint64_t ghtonll(uint64_t x) | |||
| { | |||
| return gbswap_64(x); | |||
| } | |||
| #elif defined ABSL_IS_BIG_ENDIAN | |||
| // Portable definitions for htonl (host-to-network) etc on big-endian | |||
| // architectures. These definitions are simpler since the host byte order is the | |||
| // same as network byte order. | |||
| inline uint16_t ghtons(uint16_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint32_t ghtonl(uint32_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint64_t ghtonll(uint64_t x) | |||
| { | |||
| return x; | |||
| } | |||
| #else | |||
| #error \ | |||
| "Unsupported byte order: Either ABSL_IS_BIG_ENDIAN or " \ | |||
| "ABSL_IS_LITTLE_ENDIAN must be defined" | |||
| #endif // byte order | |||
| inline uint16_t gntohs(uint16_t x) | |||
| { | |||
| return ghtons(x); | |||
| } | |||
| inline uint32_t gntohl(uint32_t x) | |||
| { | |||
| return ghtonl(x); | |||
| } | |||
| inline uint64_t gntohll(uint64_t x) | |||
| { | |||
| return ghtonll(x); | |||
| } | |||
| // Utilities to convert numbers between the current hosts's native byte | |||
| // order and little-endian byte order | |||
| // | |||
| // Load/Store methods are alignment safe | |||
| namespace little_endian | |||
| { | |||
| // Conversion functions. | |||
| #ifdef ABSL_IS_LITTLE_ENDIAN | |||
| inline uint16_t FromHost16(uint16_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t ToHost16(uint16_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint32_t FromHost32(uint32_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint32_t ToHost32(uint32_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint64_t FromHost64(uint64_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint64_t ToHost64(uint64_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline constexpr bool IsLittleEndian() | |||
| { | |||
| return true; | |||
| } | |||
| #elif defined ABSL_IS_BIG_ENDIAN | |||
| inline uint16_t FromHost16(uint16_t x) | |||
| { | |||
| return gbswap_16(x); | |||
| } | |||
| inline uint16_t ToHost16(uint16_t x) | |||
| { | |||
| return gbswap_16(x); | |||
| } | |||
| inline uint32_t FromHost32(uint32_t x) | |||
| { | |||
| return gbswap_32(x); | |||
| } | |||
| inline uint32_t ToHost32(uint32_t x) | |||
| { | |||
| return gbswap_32(x); | |||
| } | |||
| inline uint64_t FromHost64(uint64_t x) | |||
| { | |||
| return gbswap_64(x); | |||
| } | |||
| inline uint64_t ToHost64(uint64_t x) | |||
| { | |||
| return gbswap_64(x); | |||
| } | |||
| inline constexpr bool IsLittleEndian() | |||
| { | |||
| return false; | |||
| } | |||
| #endif /* ENDIAN */ | |||
| inline uint8_t FromHost(uint8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t FromHost(uint16_t x) | |||
| { | |||
| return FromHost16(x); | |||
| } | |||
| inline uint32_t FromHost(uint32_t x) | |||
| { | |||
| return FromHost32(x); | |||
| } | |||
| inline uint64_t FromHost(uint64_t x) | |||
| { | |||
| return FromHost64(x); | |||
| } | |||
| inline uint8_t ToHost(uint8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t ToHost(uint16_t x) | |||
| { | |||
| return ToHost16(x); | |||
| } | |||
| inline uint32_t ToHost(uint32_t x) | |||
| { | |||
| return ToHost32(x); | |||
| } | |||
| inline uint64_t ToHost(uint64_t x) | |||
| { | |||
| return ToHost64(x); | |||
| } | |||
| inline int8_t FromHost(int8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline int16_t FromHost(int16_t x) | |||
| { | |||
| return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); | |||
| } | |||
| inline int32_t FromHost(int32_t x) | |||
| { | |||
| return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); | |||
| } | |||
| inline int64_t FromHost(int64_t x) | |||
| { | |||
| return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); | |||
| } | |||
| inline int8_t ToHost(int8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline int16_t ToHost(int16_t x) | |||
| { | |||
| return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); | |||
| } | |||
| inline int32_t ToHost(int32_t x) | |||
| { | |||
| return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); | |||
| } | |||
| inline int64_t ToHost(int64_t x) | |||
| { | |||
| return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); | |||
| } | |||
| // Functions to do unaligned loads and stores in little-endian order. | |||
| inline uint16_t Load16(const void* p) | |||
| { | |||
| return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); | |||
| } | |||
| inline void Store16(void* p, uint16_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); | |||
| } | |||
| inline uint32_t Load32(const void* p) | |||
| { | |||
| return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); | |||
| } | |||
| inline void Store32(void* p, uint32_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); | |||
| } | |||
| inline uint64_t Load64(const void* p) | |||
| { | |||
| return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); | |||
| } | |||
| inline void Store64(void* p, uint64_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); | |||
| } | |||
| } // namespace little_endian | |||
| // Utilities to convert numbers between the current hosts's native byte | |||
| // order and big-endian byte order (same as network byte order) | |||
| // | |||
| // Load/Store methods are alignment safe | |||
| namespace big_endian | |||
| { | |||
| #ifdef ABSL_IS_LITTLE_ENDIAN | |||
| inline uint16_t FromHost16(uint16_t x) | |||
| { | |||
| return gbswap_16(x); | |||
| } | |||
| inline uint16_t ToHost16(uint16_t x) | |||
| { | |||
| return gbswap_16(x); | |||
| } | |||
| inline uint32_t FromHost32(uint32_t x) | |||
| { | |||
| return gbswap_32(x); | |||
| } | |||
| inline uint32_t ToHost32(uint32_t x) | |||
| { | |||
| return gbswap_32(x); | |||
| } | |||
| inline uint64_t FromHost64(uint64_t x) | |||
| { | |||
| return gbswap_64(x); | |||
| } | |||
| inline uint64_t ToHost64(uint64_t x) | |||
| { | |||
| return gbswap_64(x); | |||
| } | |||
| inline constexpr bool IsLittleEndian() | |||
| { | |||
| return true; | |||
| } | |||
| #elif defined ABSL_IS_BIG_ENDIAN | |||
| inline uint16_t FromHost16(uint16_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t ToHost16(uint16_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint32_t FromHost32(uint32_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint32_t ToHost32(uint32_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint64_t FromHost64(uint64_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint64_t ToHost64(uint64_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline constexpr bool IsLittleEndian() | |||
| { | |||
| return false; | |||
| } | |||
| #endif /* ENDIAN */ | |||
| inline uint8_t FromHost(uint8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t FromHost(uint16_t x) | |||
| { | |||
| return FromHost16(x); | |||
| } | |||
| inline uint32_t FromHost(uint32_t x) | |||
| { | |||
| return FromHost32(x); | |||
| } | |||
| inline uint64_t FromHost(uint64_t x) | |||
| { | |||
| return FromHost64(x); | |||
| } | |||
| inline uint8_t ToHost(uint8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline uint16_t ToHost(uint16_t x) | |||
| { | |||
| return ToHost16(x); | |||
| } | |||
| inline uint32_t ToHost(uint32_t x) | |||
| { | |||
| return ToHost32(x); | |||
| } | |||
| inline uint64_t ToHost(uint64_t x) | |||
| { | |||
| return ToHost64(x); | |||
| } | |||
| inline int8_t FromHost(int8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline int16_t FromHost(int16_t x) | |||
| { | |||
| return bit_cast<int16_t>(FromHost16(bit_cast<uint16_t>(x))); | |||
| } | |||
| inline int32_t FromHost(int32_t x) | |||
| { | |||
| return bit_cast<int32_t>(FromHost32(bit_cast<uint32_t>(x))); | |||
| } | |||
| inline int64_t FromHost(int64_t x) | |||
| { | |||
| return bit_cast<int64_t>(FromHost64(bit_cast<uint64_t>(x))); | |||
| } | |||
| inline int8_t ToHost(int8_t x) | |||
| { | |||
| return x; | |||
| } | |||
| inline int16_t ToHost(int16_t x) | |||
| { | |||
| return bit_cast<int16_t>(ToHost16(bit_cast<uint16_t>(x))); | |||
| } | |||
| inline int32_t ToHost(int32_t x) | |||
| { | |||
| return bit_cast<int32_t>(ToHost32(bit_cast<uint32_t>(x))); | |||
| } | |||
| inline int64_t ToHost(int64_t x) | |||
| { | |||
| return bit_cast<int64_t>(ToHost64(bit_cast<uint64_t>(x))); | |||
| } | |||
| // Functions to do unaligned loads and stores in big-endian order. | |||
| inline uint16_t Load16(const void* p) | |||
| { | |||
| return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); | |||
| } | |||
| inline void Store16(void* p, uint16_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); | |||
| } | |||
| inline uint32_t Load32(const void* p) | |||
| { | |||
| return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); | |||
| } | |||
| inline void Store32(void* p, uint32_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); | |||
| } | |||
| inline uint64_t Load64(const void* p) | |||
| { | |||
| return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); | |||
| } | |||
| inline void Store64(void* p, uint64_t v) | |||
| { | |||
| ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); | |||
| } | |||
| } // namespace big_endian | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_ENDIAN_H_ | |||
| @@ -0,0 +1,55 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ | |||
| #define ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ | |||
| #include <cerrno> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // `ErrnoSaver` captures the value of `errno` upon construction and restores it | |||
| // upon deletion. It is used in low-level code and must be super fast. Do not | |||
| // add instrumentation, even in debug modes. | |||
| class ErrnoSaver | |||
| { | |||
| public: | |||
| ErrnoSaver() : | |||
| saved_errno_(errno) | |||
| { | |||
| } | |||
| ~ErrnoSaver() | |||
| { | |||
| errno = saved_errno_; | |||
| } | |||
| int operator()() const | |||
| { | |||
| return saved_errno_; | |||
| } | |||
| private: | |||
| const int saved_errno_; | |||
| }; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_ERRNO_SAVER_H_ | |||
| @@ -0,0 +1,42 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // Testing utilities for ABSL types which throw exceptions. | |||
| #ifndef ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ | |||
| #define ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ | |||
| #include "gtest/gtest.h" | |||
| #include "absl/base/config.h" | |||
| // ABSL_BASE_INTERNAL_EXPECT_FAIL tests either for a specified thrown exception | |||
| // if exceptions are enabled, or for death with a specified text in the error | |||
| // message | |||
| #ifdef ABSL_HAVE_EXCEPTIONS | |||
| #define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ | |||
| EXPECT_THROW(expr, exception_t) | |||
| #elif defined(__ANDROID__) | |||
| // Android asserts do not log anywhere that gtest can currently inspect. | |||
| // So we expect exit, but cannot match the message. | |||
| #define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ | |||
| EXPECT_DEATH(expr, ".*") | |||
| #else | |||
| #define ABSL_BASE_INTERNAL_EXPECT_FAIL(expr, exception_t, text) \ | |||
| EXPECT_DEATH_IF_SUPPORTED(expr, text) | |||
| #endif | |||
| #endif // ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_ | |||
| @@ -0,0 +1,54 @@ | |||
| // | |||
| // Copyright 2020 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ | |||
| #define ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| template<typename Type> | |||
| struct FastTypeTag | |||
| { | |||
| constexpr static char dummy_var = 0; | |||
| }; | |||
| #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL | |||
| template<typename Type> | |||
| constexpr char FastTypeTag<Type>::dummy_var; | |||
| #endif | |||
| // FastTypeId<Type>() evaluates at compile/link-time to a unique pointer for the | |||
| // passed-in type. These are meant to be good match for keys into maps or | |||
| // straight up comparisons. | |||
| using FastTypeIdType = const void*; | |||
| template<typename Type> | |||
| constexpr inline FastTypeIdType FastTypeId() | |||
| { | |||
| return &FastTypeTag<Type>::dummy_var; | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_FAST_TYPE_ID_H_ | |||
| @@ -0,0 +1,56 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_HIDE_PTR_H_ | |||
| #define ABSL_BASE_INTERNAL_HIDE_PTR_H_ | |||
| #include <cstdint> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Arbitrary value with high bits set. Xor'ing with it is unlikely | |||
| // to map one valid pointer to another valid pointer. | |||
| constexpr uintptr_t HideMask() | |||
| { | |||
| return (uintptr_t{0xF03A5F7BU} << (sizeof(uintptr_t) - 4) * 8) | 0xF03A5F7BU; | |||
| } | |||
| // Hide a pointer from the leak checker. For internal use only. | |||
| // Differs from absl::IgnoreLeak(ptr) in that absl::IgnoreLeak(ptr) causes ptr | |||
| // and all objects reachable from ptr to be ignored by the leak checker. | |||
| template<class T> | |||
| inline uintptr_t HidePtr(T* ptr) | |||
| { | |||
| return reinterpret_cast<uintptr_t>(ptr) ^ HideMask(); | |||
| } | |||
| // Return a pointer that has been hidden from the leak checker. | |||
| // For internal use only. | |||
| template<class T> | |||
| inline T* UnhidePtr(uintptr_t hidden) | |||
| { | |||
| return reinterpret_cast<T*>(hidden ^ HideMask()); | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_HIDE_PTR_H_ | |||
| @@ -0,0 +1,40 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_IDENTITY_H_ | |||
| #define ABSL_BASE_INTERNAL_IDENTITY_H_ | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace internal | |||
| { | |||
| template<typename T> | |||
| struct identity | |||
| { | |||
| typedef T type; | |||
| }; | |||
| template<typename T> | |||
| using identity_t = typename identity<T>::type; | |||
| } // namespace internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_IDENTITY_H_ | |||
| @@ -0,0 +1,107 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ | |||
| #define ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ | |||
| #include <type_traits> | |||
| #include "absl/base/internal/identity.h" | |||
| // File: | |||
| // This file define a macro that allows the creation of or emulation of C++17 | |||
| // inline variables based on whether or not the feature is supported. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Macro: ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) | |||
| // | |||
| // Description: | |||
| // Expands to the equivalent of an inline constexpr instance of the specified | |||
| // `type` and `name`, initialized to the value `init`. If the compiler being | |||
| // used is detected as supporting actual inline variables as a language | |||
| // feature, then the macro expands to an actual inline variable definition. | |||
| // | |||
| // Requires: | |||
| // `type` is a type that is usable in an extern variable declaration. | |||
| // | |||
| // Requires: `name` is a valid identifier | |||
| // | |||
| // Requires: | |||
| // `init` is an expression that can be used in the following definition: | |||
| // constexpr type name = init; | |||
| // | |||
| // Usage: | |||
| // | |||
| // // Equivalent to: `inline constexpr size_t variant_npos = -1;` | |||
| // ABSL_INTERNAL_INLINE_CONSTEXPR(size_t, variant_npos, -1); | |||
| // | |||
| // Differences in implementation: | |||
| // For a direct, language-level inline variable, decltype(name) will be the | |||
| // type that was specified along with const qualification, whereas for | |||
| // emulated inline variables, decltype(name) may be different (in practice | |||
| // it will likely be a reference type). | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| #ifdef __cpp_inline_variables | |||
| // Clang's -Wmissing-variable-declarations option erroneously warned that | |||
| // inline constexpr objects need to be pre-declared. This has now been fixed, | |||
| // but we will need to support this workaround for people building with older | |||
| // versions of clang. | |||
| // | |||
| // Bug: https://bugs.llvm.org/show_bug.cgi?id=35862 | |||
| // | |||
| // Note: | |||
| // identity_t is used here so that the const and name are in the | |||
| // appropriate place for pointer types, reference types, function pointer | |||
| // types, etc.. | |||
| #if defined(__clang__) | |||
| #define ABSL_INTERNAL_EXTERN_DECL(type, name) \ | |||
| extern const ::absl::internal::identity_t<type> name; | |||
| #else // Otherwise, just define the macro to do nothing. | |||
| #define ABSL_INTERNAL_EXTERN_DECL(type, name) | |||
| #endif // defined(__clang__) | |||
| // See above comment at top of file for details. | |||
| #define ABSL_INTERNAL_INLINE_CONSTEXPR(type, name, init) \ | |||
| ABSL_INTERNAL_EXTERN_DECL(type, name) \ | |||
| inline constexpr ::absl::internal::identity_t<type> name = init | |||
| #else | |||
| // See above comment at top of file for details. | |||
| // | |||
| // Note: | |||
| // identity_t is used here so that the const and name are in the | |||
| // appropriate place for pointer types, reference types, function pointer | |||
| // types, etc.. | |||
| #define ABSL_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \ | |||
| template<class /*AbslInternalDummy*/ = void> \ | |||
| struct AbslInternalInlineVariableHolder##name \ | |||
| { \ | |||
| static constexpr ::absl::internal::identity_t<var_type> kInstance = init; \ | |||
| }; \ | |||
| \ | |||
| template<class AbslInternalDummy> \ | |||
| constexpr ::absl::internal::identity_t<var_type> \ | |||
| AbslInternalInlineVariableHolder##name<AbslInternalDummy>::kInstance; \ | |||
| \ | |||
| static constexpr const ::absl::internal::identity_t<var_type>& \ | |||
| name = /* NOLINT */ \ | |||
| AbslInternalInlineVariableHolder##name<>::kInstance; \ | |||
| static_assert(sizeof(void (*)(decltype(name))) != 0, "Silence unused variable warnings.") | |||
| #endif // __cpp_inline_variables | |||
| #endif // ABSL_BASE_INTERNAL_INLINE_VARIABLE_EMULATION_H_ | |||
| @@ -0,0 +1,49 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INLINE_VARIABLE_TESTING_H_ | |||
| #define ABSL_BASE_INLINE_VARIABLE_TESTING_H_ | |||
| #include "absl/base/internal/inline_variable.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace inline_variable_testing_internal | |||
| { | |||
| struct Foo | |||
| { | |||
| int value = 5; | |||
| }; | |||
| ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, inline_variable_foo, {}); | |||
| ABSL_INTERNAL_INLINE_CONSTEXPR(Foo, other_inline_variable_foo, {}); | |||
| ABSL_INTERNAL_INLINE_CONSTEXPR(int, inline_variable_int, 5); | |||
| ABSL_INTERNAL_INLINE_CONSTEXPR(int, other_inline_variable_int, 5); | |||
| ABSL_INTERNAL_INLINE_CONSTEXPR(void (*)(), inline_variable_fun_ptr, nullptr); | |||
| const Foo& get_foo_a(); | |||
| const Foo& get_foo_b(); | |||
| const int& get_int_a(); | |||
| const int& get_int_b(); | |||
| } // namespace inline_variable_testing_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INLINE_VARIABLE_TESTING_H_ | |||
| @@ -0,0 +1,267 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // absl::base_internal::invoke(f, args...) is an implementation of | |||
| // INVOKE(f, args...) from section [func.require] of the C++ standard. | |||
| // When compiled as C++17 and later versions, it is implemented as an alias of | |||
| // std::invoke. | |||
| // | |||
| // [func.require] | |||
| // Define INVOKE (f, t1, t2, ..., tN) as follows: | |||
| // 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T | |||
| // and t1 is an object of type T or a reference to an object of type T or a | |||
| // reference to an object of a type derived from T; | |||
| // 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a | |||
| // class T and t1 is not one of the types described in the previous item; | |||
| // 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is | |||
| // an object of type T or a reference to an object of type T or a reference | |||
| // to an object of a type derived from T; | |||
| // 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 | |||
| // is not one of the types described in the previous item; | |||
| // 5. f(t1, t2, ..., tN) in all other cases. | |||
| // | |||
| // The implementation is SFINAE-friendly: substitution failure within invoke() | |||
| // isn't an error. | |||
| #ifndef ABSL_BASE_INTERNAL_INVOKE_H_ | |||
| #define ABSL_BASE_INTERNAL_INVOKE_H_ | |||
| #include "absl/base/config.h" | |||
| #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L | |||
| #include <functional> | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| using std::invoke; | |||
| using std::invoke_result_t; | |||
| using std::is_invocable_r; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #else // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L | |||
| #include <algorithm> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/meta/type_traits.h" | |||
| // The following code is internal implementation detail. See the comment at the | |||
| // top of this file for the API documentation. | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // The five classes below each implement one of the clauses from the definition | |||
| // of INVOKE. The inner class template Accept<F, Args...> checks whether the | |||
| // clause is applicable; static function template Invoke(f, args...) does the | |||
| // invocation. | |||
| // | |||
| // By separating the clause selection logic from invocation we make sure that | |||
| // Invoke() does exactly what the standard says. | |||
| template<typename Derived> | |||
| struct StrippedAccept | |||
| { | |||
| template<typename... Args> | |||
| struct Accept : Derived::template AcceptImpl<typename std::remove_cv<typename std::remove_reference<Args>::type>::type...> | |||
| { | |||
| }; | |||
| }; | |||
| // (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T | |||
| // and t1 is an object of type T or a reference to an object of type T or a | |||
| // reference to an object of a type derived from T. | |||
| struct MemFunAndRef : StrippedAccept<MemFunAndRef> | |||
| { | |||
| template<typename... Args> | |||
| struct AcceptImpl : std::false_type | |||
| { | |||
| }; | |||
| template<typename MemFunType, typename C, typename Obj, typename... Args> | |||
| struct AcceptImpl<MemFunType C::*, Obj, Args...> : std::integral_constant<bool, std::is_base_of<C, Obj>::value && absl::is_function<MemFunType>::value> | |||
| { | |||
| }; | |||
| template<typename MemFun, typename Obj, typename... Args> | |||
| static decltype((std::declval<Obj>().*std::declval<MemFun>())(std::declval<Args>()...)) | |||
| Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) | |||
| { | |||
| // Ignore bogus GCC warnings on this line. | |||
| // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101436 for similar example. | |||
| #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Warray-bounds" | |||
| #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |||
| #endif | |||
| return (std::forward<Obj>(obj).*std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...); | |||
| #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(11, 0) | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| } | |||
| }; | |||
| // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a | |||
| // class T and t1 is not one of the types described in the previous item. | |||
| struct MemFunAndPtr : StrippedAccept<MemFunAndPtr> | |||
| { | |||
| template<typename... Args> | |||
| struct AcceptImpl : std::false_type | |||
| { | |||
| }; | |||
| template<typename MemFunType, typename C, typename Ptr, typename... Args> | |||
| struct AcceptImpl<MemFunType C::*, Ptr, Args...> : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value && absl::is_function<MemFunType>::value> | |||
| { | |||
| }; | |||
| template<typename MemFun, typename Ptr, typename... Args> | |||
| static decltype(((*std::declval<Ptr>()).*std::declval<MemFun>())(std::declval<Args>()...)) | |||
| Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) | |||
| { | |||
| return ((*std::forward<Ptr>(ptr)).*std::forward<MemFun>(mem_fun))(std::forward<Args>(args)...); | |||
| } | |||
| }; | |||
| // t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is | |||
| // an object of type T or a reference to an object of type T or a reference | |||
| // to an object of a type derived from T. | |||
| struct DataMemAndRef : StrippedAccept<DataMemAndRef> | |||
| { | |||
| template<typename... Args> | |||
| struct AcceptImpl : std::false_type | |||
| { | |||
| }; | |||
| template<typename R, typename C, typename Obj> | |||
| struct AcceptImpl<R C::*, Obj> : std::integral_constant<bool, std::is_base_of<C, Obj>::value && !absl::is_function<R>::value> | |||
| { | |||
| }; | |||
| template<typename DataMem, typename Ref> | |||
| static decltype(std::declval<Ref>().*std::declval<DataMem>()) Invoke( | |||
| DataMem&& data_mem, Ref&& ref | |||
| ) | |||
| { | |||
| return std::forward<Ref>(ref).*std::forward<DataMem>(data_mem); | |||
| } | |||
| }; | |||
| // (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 | |||
| // is not one of the types described in the previous item. | |||
| struct DataMemAndPtr : StrippedAccept<DataMemAndPtr> | |||
| { | |||
| template<typename... Args> | |||
| struct AcceptImpl : std::false_type | |||
| { | |||
| }; | |||
| template<typename R, typename C, typename Ptr> | |||
| struct AcceptImpl<R C::*, Ptr> : std::integral_constant<bool, !std::is_base_of<C, Ptr>::value && !absl::is_function<R>::value> | |||
| { | |||
| }; | |||
| template<typename DataMem, typename Ptr> | |||
| static decltype((*std::declval<Ptr>()).*std::declval<DataMem>()) Invoke( | |||
| DataMem&& data_mem, Ptr&& ptr | |||
| ) | |||
| { | |||
| return (*std::forward<Ptr>(ptr)).*std::forward<DataMem>(data_mem); | |||
| } | |||
| }; | |||
| // f(t1, t2, ..., tN) in all other cases. | |||
| struct Callable | |||
| { | |||
| // Callable doesn't have Accept because it's the last clause that gets picked | |||
| // when none of the previous clauses are applicable. | |||
| template<typename F, typename... Args> | |||
| static decltype(std::declval<F>()(std::declval<Args>()...)) Invoke( | |||
| F&& f, Args&&... args | |||
| ) | |||
| { | |||
| return std::forward<F>(f)(std::forward<Args>(args)...); | |||
| } | |||
| }; | |||
| // Resolves to the first matching clause. | |||
| template<typename... Args> | |||
| struct Invoker | |||
| { | |||
| typedef typename std::conditional< | |||
| MemFunAndRef::Accept<Args...>::value, | |||
| MemFunAndRef, | |||
| typename std::conditional< | |||
| MemFunAndPtr::Accept<Args...>::value, | |||
| MemFunAndPtr, | |||
| typename std::conditional< | |||
| DataMemAndRef::Accept<Args...>::value, | |||
| DataMemAndRef, | |||
| typename std::conditional<DataMemAndPtr::Accept<Args...>::value, DataMemAndPtr, Callable>::type>::type>:: | |||
| type>::type type; | |||
| }; | |||
| // The result type of Invoke<F, Args...>. | |||
| template<typename F, typename... Args> | |||
| using invoke_result_t = decltype(Invoker<F, Args...>::type::Invoke( | |||
| std::declval<F>(), std::declval<Args>()... | |||
| )); | |||
| // Invoke(f, args...) is an implementation of INVOKE(f, args...) from section | |||
| // [func.require] of the C++ standard. | |||
| template<typename F, typename... Args> | |||
| invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) | |||
| { | |||
| return Invoker<F, Args...>::type::Invoke(std::forward<F>(f), std::forward<Args>(args)...); | |||
| } | |||
| template<typename AlwaysVoid, typename, typename, typename...> | |||
| struct IsInvocableRImpl : std::false_type | |||
| { | |||
| }; | |||
| template<typename R, typename F, typename... Args> | |||
| struct IsInvocableRImpl< | |||
| absl::void_t<absl::base_internal::invoke_result_t<F, Args...>>, | |||
| R, | |||
| F, | |||
| Args...> : std::integral_constant<bool, std::is_convertible<absl::base_internal::invoke_result_t<F, Args...>, R>::value || std::is_void<R>::value> | |||
| { | |||
| }; | |||
| // Type trait whose member `value` is true if invoking `F` with `Args` is valid, | |||
| // and either the return type is convertible to `R`, or `R` is void. | |||
| // C++11-compatible version of `std::is_invocable_r`. | |||
| template<typename R, typename F, typename... Args> | |||
| using is_invocable_r = IsInvocableRImpl<void, R, F, Args...>; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L | |||
| #endif // ABSL_BASE_INTERNAL_INVOKE_H_ | |||
| @@ -0,0 +1,130 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ | |||
| #define ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ | |||
| // A simple thread-safe memory allocator that does not depend on | |||
| // mutexes or thread-specific data. It is intended to be used | |||
| // sparingly, and only when malloc() would introduce an unwanted | |||
| // dependency, such as inside the heap-checker, or the Mutex | |||
| // implementation. | |||
| // IWYU pragma: private, include "base/low_level_alloc.h" | |||
| #include <sys/types.h> | |||
| #include <cstdint> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| // LowLevelAlloc requires that the platform support low-level | |||
| // allocation of virtual memory. Platforms lacking this cannot use | |||
| // LowLevelAlloc. | |||
| #ifdef ABSL_LOW_LEVEL_ALLOC_MISSING | |||
| #error ABSL_LOW_LEVEL_ALLOC_MISSING cannot be directly set | |||
| #elif !defined(ABSL_HAVE_MMAP) && !defined(_WIN32) | |||
| #define ABSL_LOW_LEVEL_ALLOC_MISSING 1 | |||
| #endif | |||
| // Using LowLevelAlloc with kAsyncSignalSafe isn't supported on Windows or | |||
| // asm.js / WebAssembly. | |||
| // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html | |||
| // for more information. | |||
| #ifdef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING | |||
| #error ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING cannot be directly set | |||
| #elif defined(_WIN32) || defined(__asmjs__) || defined(__wasm__) | |||
| #define ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING 1 | |||
| #endif | |||
| #include <cstddef> | |||
| #include "absl/base/port.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| class LowLevelAlloc | |||
| { | |||
| public: | |||
| struct Arena; // an arena from which memory may be allocated | |||
| // Returns a pointer to a block of at least "request" bytes | |||
| // that have been newly allocated from the specific arena. | |||
| // for Alloc() call the DefaultArena() is used. | |||
| // Returns 0 if passed request==0. | |||
| // Does not return 0 under other circumstances; it crashes if memory | |||
| // is not available. | |||
| static void* Alloc(size_t request) ABSL_ATTRIBUTE_SECTION(malloc_hook); | |||
| static void* AllocWithArena(size_t request, Arena* arena) | |||
| ABSL_ATTRIBUTE_SECTION(malloc_hook); | |||
| // Deallocates a region of memory that was previously allocated with | |||
| // Alloc(). Does nothing if passed 0. "s" must be either 0, | |||
| // or must have been returned from a call to Alloc() and not yet passed to | |||
| // Free() since that call to Alloc(). The space is returned to the arena | |||
| // from which it was allocated. | |||
| static void Free(void* s) ABSL_ATTRIBUTE_SECTION(malloc_hook); | |||
| // ABSL_ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free | |||
| // are to put all callers of MallocHook::Invoke* in this module | |||
| // into special section, | |||
| // so that MallocHook::GetCallerStackTrace can function accurately. | |||
| // Create a new arena. | |||
| // The root metadata for the new arena is allocated in the | |||
| // meta_data_arena; the DefaultArena() can be passed for meta_data_arena. | |||
| // These values may be ored into flags: | |||
| enum | |||
| { | |||
| // Report calls to Alloc() and Free() via the MallocHook interface. | |||
| // Set in the DefaultArena. | |||
| kCallMallocHook = 0x0001, | |||
| #ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING | |||
| // Make calls to Alloc(), Free() be async-signal-safe. Not set in | |||
| // DefaultArena(). Not supported on all platforms. | |||
| kAsyncSignalSafe = 0x0002, | |||
| #endif | |||
| }; | |||
| // Construct a new arena. The allocation of the underlying metadata honors | |||
| // the provided flags. For example, the call NewArena(kAsyncSignalSafe) | |||
| // is itself async-signal-safe, as well as generatating an arena that provides | |||
| // async-signal-safe Alloc/Free. | |||
| static Arena* NewArena(int32_t flags); | |||
| // Destroys an arena allocated by NewArena and returns true, | |||
| // provided no allocated blocks remain in the arena. | |||
| // If allocated blocks remain in the arena, does nothing and | |||
| // returns false. | |||
| // It is illegal to attempt to destroy the DefaultArena(). | |||
| static bool DeleteArena(Arena* arena); | |||
| // The default arena that always exists. | |||
| static Arena* DefaultArena(); | |||
| private: | |||
| LowLevelAlloc(); // no instances | |||
| }; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_ALLOC_H_ | |||
| @@ -0,0 +1,152 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Core interfaces and definitions used by by low-level interfaces such as | |||
| // SpinLock. | |||
| #ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ | |||
| #define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ | |||
| #include "absl/base/internal/raw_logging.h" | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| #include "absl/base/macros.h" | |||
| // The following two declarations exist so SchedulingGuard may friend them with | |||
| // the appropriate language linkage. These callbacks allow libc internals, such | |||
| // as function level statics, to schedule cooperatively when locking. | |||
| extern "C" bool __google_disable_rescheduling(void); | |||
| extern "C" void __google_enable_rescheduling(bool disable_result); | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| class CondVar; | |||
| class Mutex; | |||
| namespace synchronization_internal | |||
| { | |||
| int MutexDelay(int32_t c, int mode); | |||
| } // namespace synchronization_internal | |||
| namespace base_internal | |||
| { | |||
| class SchedulingHelper; // To allow use of SchedulingGuard. | |||
| class SpinLock; // To allow use of SchedulingGuard. | |||
| // SchedulingGuard | |||
| // Provides guard semantics that may be used to disable cooperative rescheduling | |||
| // of the calling thread within specific program blocks. This is used to | |||
| // protect resources (e.g. low-level SpinLocks or Domain code) that cooperative | |||
| // scheduling depends on. | |||
| // | |||
| // Domain implementations capable of rescheduling in reaction to involuntary | |||
| // kernel thread actions (e.g blocking due to a pagefault or syscall) must | |||
| // guarantee that an annotated thread is not allowed to (cooperatively) | |||
| // reschedule until the annotated region is complete. | |||
| // | |||
| // It is an error to attempt to use a cooperatively scheduled resource (e.g. | |||
| // Mutex) within a rescheduling-disabled region. | |||
| // | |||
| // All methods are async-signal safe. | |||
| class SchedulingGuard | |||
| { | |||
| public: | |||
| // Returns true iff the calling thread may be cooperatively rescheduled. | |||
| static bool ReschedulingIsAllowed(); | |||
| SchedulingGuard(const SchedulingGuard&) = delete; | |||
| SchedulingGuard& operator=(const SchedulingGuard&) = delete; | |||
| private: | |||
| // Disable cooperative rescheduling of the calling thread. It may still | |||
| // initiate scheduling operations (e.g. wake-ups), however, it may not itself | |||
| // reschedule. Nestable. The returned result is opaque, clients should not | |||
| // attempt to interpret it. | |||
| // REQUIRES: Result must be passed to a pairing EnableScheduling(). | |||
| static bool DisableRescheduling(); | |||
| // Marks the end of a rescheduling disabled region, previously started by | |||
| // DisableRescheduling(). | |||
| // REQUIRES: Pairs with innermost call (and result) of DisableRescheduling(). | |||
| static void EnableRescheduling(bool disable_result); | |||
| // A scoped helper for {Disable, Enable}Rescheduling(). | |||
| // REQUIRES: destructor must run in same thread as constructor. | |||
| struct ScopedDisable | |||
| { | |||
| ScopedDisable() | |||
| { | |||
| disabled = SchedulingGuard::DisableRescheduling(); | |||
| } | |||
| ~ScopedDisable() | |||
| { | |||
| SchedulingGuard::EnableRescheduling(disabled); | |||
| } | |||
| bool disabled; | |||
| }; | |||
| // A scoped helper to enable rescheduling temporarily. | |||
| // REQUIRES: destructor must run in same thread as constructor. | |||
| class ScopedEnable | |||
| { | |||
| public: | |||
| ScopedEnable(); | |||
| ~ScopedEnable(); | |||
| private: | |||
| int scheduling_disabled_depth_; | |||
| }; | |||
| // Access to SchedulingGuard is explicitly permitted. | |||
| friend class absl::CondVar; | |||
| friend class absl::Mutex; | |||
| friend class SchedulingHelper; | |||
| friend class SpinLock; | |||
| friend int absl::synchronization_internal::MutexDelay(int32_t c, int mode); | |||
| }; | |||
| //------------------------------------------------------------------------------ | |||
| // End of public interfaces. | |||
| //------------------------------------------------------------------------------ | |||
| inline bool SchedulingGuard::ReschedulingIsAllowed() | |||
| { | |||
| return false; | |||
| } | |||
| inline bool SchedulingGuard::DisableRescheduling() | |||
| { | |||
| return false; | |||
| } | |||
| inline void SchedulingGuard::EnableRescheduling(bool /* disable_result */) | |||
| { | |||
| return; | |||
| } | |||
| inline SchedulingGuard::ScopedEnable::ScopedEnable() : | |||
| scheduling_disabled_depth_(0) | |||
| { | |||
| } | |||
| inline SchedulingGuard::ScopedEnable::~ScopedEnable() | |||
| { | |||
| ABSL_RAW_CHECK(scheduling_disabled_depth_ == 0, "disable unused warning"); | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_ | |||
| @@ -0,0 +1,52 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ | |||
| #define ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ | |||
| // This header defines two macros: | |||
| // | |||
| // If the platform supports thread-local storage: | |||
| // | |||
| // * ABSL_PER_THREAD_TLS_KEYWORD is the C keyword needed to declare a | |||
| // thread-local variable | |||
| // * ABSL_PER_THREAD_TLS is 1 | |||
| // | |||
| // Otherwise: | |||
| // | |||
| // * ABSL_PER_THREAD_TLS_KEYWORD is empty | |||
| // * ABSL_PER_THREAD_TLS is 0 | |||
| // | |||
| // Microsoft C supports thread-local storage. | |||
| // GCC supports it if the appropriate version of glibc is available, | |||
| // which the programmer can indicate by defining ABSL_HAVE_TLS | |||
| #include "absl/base/port.h" // For ABSL_HAVE_TLS | |||
| #if defined(ABSL_PER_THREAD_TLS) | |||
| #error ABSL_PER_THREAD_TLS cannot be directly set | |||
| #elif defined(ABSL_PER_THREAD_TLS_KEYWORD) | |||
| #error ABSL_PER_THREAD_TLS_KEYWORD cannot be directly set | |||
| #elif defined(ABSL_HAVE_TLS) | |||
| #define ABSL_PER_THREAD_TLS_KEYWORD __thread | |||
| #define ABSL_PER_THREAD_TLS 1 | |||
| #elif defined(_MSC_VER) | |||
| #define ABSL_PER_THREAD_TLS_KEYWORD __declspec(thread) | |||
| #define ABSL_PER_THREAD_TLS 1 | |||
| #else | |||
| #define ABSL_PER_THREAD_TLS_KEYWORD | |||
| #define ABSL_PER_THREAD_TLS 0 | |||
| #endif | |||
| #endif // ABSL_BASE_INTERNAL_PER_THREAD_TLS_H_ | |||
| @@ -0,0 +1,156 @@ | |||
| // Copyright 2022 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_PREFETCH_H_ | |||
| #define ABSL_BASE_INTERNAL_PREFETCH_H_ | |||
| #include "absl/base/config.h" | |||
| #ifdef __SSE__ | |||
| #include <xmmintrin.h> | |||
| #endif | |||
| #if defined(_MSC_VER) && defined(ABSL_INTERNAL_HAVE_SSE) | |||
| #include <intrin.h> | |||
| #pragma intrinsic(_mm_prefetch) | |||
| #endif | |||
| // Compatibility wrappers around __builtin_prefetch, to prefetch data | |||
| // for read if supported by the toolchain. | |||
| // Move data into the cache before it is read, or "prefetch" it. | |||
| // | |||
| // The value of `addr` is the address of the memory to prefetch. If | |||
| // the target and compiler support it, data prefetch instructions are | |||
| // generated. If the prefetch is done some time before the memory is | |||
| // read, it may be in the cache by the time the read occurs. | |||
| // | |||
| // The function names specify the temporal locality heuristic applied, | |||
| // using the names of Intel prefetch instructions: | |||
| // | |||
| // T0 - high degree of temporal locality; data should be left in as | |||
| // many levels of the cache possible | |||
| // T1 - moderate degree of temporal locality | |||
| // T2 - low degree of temporal locality | |||
| // Nta - no temporal locality, data need not be left in the cache | |||
| // after the read | |||
| // | |||
| // Incorrect or gratuitous use of these functions can degrade | |||
| // performance, so use them only when representative benchmarks show | |||
| // an improvement. | |||
| // | |||
| // Example usage: | |||
| // | |||
| // absl::base_internal::PrefetchT0(addr); | |||
| // | |||
| // Currently, the different prefetch calls behave on some Intel | |||
| // architectures as follows: | |||
| // | |||
| // SNB..SKL SKX | |||
| // PrefetchT0() L1/L2/L3 L1/L2 | |||
| // PrefetchT1() L2/L3 L2 | |||
| // PrefetchT2() L2/L3 L2 | |||
| // PrefetchNta() L1/--/L3 L1* | |||
| // | |||
| // * On SKX PrefetchNta() will bring the line into L1 but will evict | |||
| // from L3 cache. This might result in surprising behavior. | |||
| // | |||
| // SNB = Sandy Bridge, SKL = Skylake, SKX = Skylake Xeon. | |||
| // | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| void PrefetchT0(const void* addr); | |||
| void PrefetchT1(const void* addr); | |||
| void PrefetchT2(const void* addr); | |||
| void PrefetchNta(const void* addr); | |||
| // Implementation details follow. | |||
| #if ABSL_HAVE_BUILTIN(__builtin_prefetch) || defined(__GNUC__) | |||
| #define ABSL_INTERNAL_HAVE_PREFETCH 1 | |||
| // See __builtin_prefetch: | |||
| // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html. | |||
| // | |||
| // These functions speculatively load for read only. This is | |||
| // safe for all currently supported platforms. However, prefetch for | |||
| // store may have problems depending on the target platform. | |||
| // | |||
| inline void PrefetchT0(const void* addr) | |||
| { | |||
| // Note: this uses prefetcht0 on Intel. | |||
| __builtin_prefetch(addr, 0, 3); | |||
| } | |||
| inline void PrefetchT1(const void* addr) | |||
| { | |||
| // Note: this uses prefetcht1 on Intel. | |||
| __builtin_prefetch(addr, 0, 2); | |||
| } | |||
| inline void PrefetchT2(const void* addr) | |||
| { | |||
| // Note: this uses prefetcht2 on Intel. | |||
| __builtin_prefetch(addr, 0, 1); | |||
| } | |||
| inline void PrefetchNta(const void* addr) | |||
| { | |||
| // Note: this uses prefetchtnta on Intel. | |||
| __builtin_prefetch(addr, 0, 0); | |||
| } | |||
| #elif defined(ABSL_INTERNAL_HAVE_SSE) | |||
| #define ABSL_INTERNAL_HAVE_PREFETCH 1 | |||
| inline void PrefetchT0(const void* addr) | |||
| { | |||
| _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0); | |||
| } | |||
| inline void PrefetchT1(const void* addr) | |||
| { | |||
| _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T1); | |||
| } | |||
| inline void PrefetchT2(const void* addr) | |||
| { | |||
| _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T2); | |||
| } | |||
| inline void PrefetchNta(const void* addr) | |||
| { | |||
| _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_NTA); | |||
| } | |||
| #else | |||
| inline void PrefetchT0(const void*) | |||
| { | |||
| } | |||
| inline void PrefetchT1(const void*) | |||
| { | |||
| } | |||
| inline void PrefetchT2(const void*) | |||
| { | |||
| } | |||
| inline void PrefetchNta(const void*) | |||
| { | |||
| } | |||
| #endif | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_PREFETCH_H_ | |||
| @@ -0,0 +1,33 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ | |||
| #define ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ | |||
| // ABSL_PRETTY_FUNCTION | |||
| // | |||
| // In C++11, __func__ gives the undecorated name of the current function. That | |||
| // is, "main", not "int main()". Various compilers give extra macros to get the | |||
| // decorated function name, including return type and arguments, to | |||
| // differentiate between overload sets. ABSL_PRETTY_FUNCTION is a portable | |||
| // version of these macros which forwards to the correct macro on each compiler. | |||
| #if defined(_MSC_VER) | |||
| #define ABSL_PRETTY_FUNCTION __FUNCSIG__ | |||
| #elif defined(__GNUC__) | |||
| #define ABSL_PRETTY_FUNCTION __PRETTY_FUNCTION__ | |||
| #else | |||
| #error "Unsupported compiler" | |||
| #endif | |||
| #endif // ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_ | |||
| @@ -0,0 +1,197 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Thread-safe logging routines that do not allocate any memory or | |||
| // acquire any locks, and can therefore be used by low-level memory | |||
| // allocation, synchronization, and signal-handling code. | |||
| #ifndef ABSL_BASE_INTERNAL_RAW_LOGGING_H_ | |||
| #define ABSL_BASE_INTERNAL_RAW_LOGGING_H_ | |||
| #include <string> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/internal/atomic_hook.h" | |||
| #include "absl/base/log_severity.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/base/port.h" | |||
| // This is similar to LOG(severity) << format..., but | |||
| // * it is to be used ONLY by low-level modules that can't use normal LOG() | |||
| // * it is designed to be a low-level logger that does not allocate any | |||
| // memory and does not need any locks, hence: | |||
| // * it logs straight and ONLY to STDERR w/o buffering | |||
| // * it uses an explicit printf-format and arguments list | |||
| // * it will silently chop off really long message strings | |||
| // Usage example: | |||
| // ABSL_RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); | |||
| // This will print an almost standard log line like this to stderr only: | |||
| // E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file | |||
| #define ABSL_RAW_LOG(severity, ...) \ | |||
| do \ | |||
| { \ | |||
| constexpr const char* absl_raw_logging_internal_basename = \ | |||
| ::absl::raw_logging_internal::Basename(__FILE__, sizeof(__FILE__) - 1); \ | |||
| ::absl::raw_logging_internal::RawLog(ABSL_RAW_LOGGING_INTERNAL_##severity, absl_raw_logging_internal_basename, __LINE__, __VA_ARGS__); \ | |||
| } while (0) | |||
| // Similar to CHECK(condition) << message, but for low-level modules: | |||
| // we use only ABSL_RAW_LOG that does not allocate memory. | |||
| // We do not want to provide args list here to encourage this usage: | |||
| // if (!cond) ABSL_RAW_LOG(FATAL, "foo ...", hard_to_compute_args); | |||
| // so that the args are not computed when not needed. | |||
| #define ABSL_RAW_CHECK(condition, message) \ | |||
| do \ | |||
| { \ | |||
| if (ABSL_PREDICT_FALSE(!(condition))) \ | |||
| { \ | |||
| ABSL_RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ | |||
| } \ | |||
| } while (0) | |||
| // ABSL_INTERNAL_LOG and ABSL_INTERNAL_CHECK work like the RAW variants above, | |||
| // except that if the richer log library is linked into the binary, we dispatch | |||
| // to that instead. This is potentially useful for internal logging and | |||
| // assertions, where we are using RAW_LOG neither for its async-signal-safety | |||
| // nor for its non-allocating nature, but rather because raw logging has very | |||
| // few other dependencies. | |||
| // | |||
| // The API is a subset of the above: each macro only takes two arguments. Use | |||
| // StrCat if you need to build a richer message. | |||
| #define ABSL_INTERNAL_LOG(severity, message) \ | |||
| do \ | |||
| { \ | |||
| constexpr const char* absl_raw_logging_internal_filename = __FILE__; \ | |||
| ::absl::raw_logging_internal::internal_log_function( \ | |||
| ABSL_RAW_LOGGING_INTERNAL_##severity, \ | |||
| absl_raw_logging_internal_filename, \ | |||
| __LINE__, \ | |||
| message \ | |||
| ); \ | |||
| if (ABSL_RAW_LOGGING_INTERNAL_##severity == ::absl::LogSeverity::kFatal) \ | |||
| ABSL_INTERNAL_UNREACHABLE; \ | |||
| } while (0) | |||
| #define ABSL_INTERNAL_CHECK(condition, message) \ | |||
| do \ | |||
| { \ | |||
| if (ABSL_PREDICT_FALSE(!(condition))) \ | |||
| { \ | |||
| std::string death_message = "Check " #condition " failed: "; \ | |||
| death_message += std::string(message); \ | |||
| ABSL_INTERNAL_LOG(FATAL, death_message); \ | |||
| } \ | |||
| } while (0) | |||
| #define ABSL_RAW_LOGGING_INTERNAL_INFO ::absl::LogSeverity::kInfo | |||
| #define ABSL_RAW_LOGGING_INTERNAL_WARNING ::absl::LogSeverity::kWarning | |||
| #define ABSL_RAW_LOGGING_INTERNAL_ERROR ::absl::LogSeverity::kError | |||
| #define ABSL_RAW_LOGGING_INTERNAL_FATAL ::absl::LogSeverity::kFatal | |||
| #define ABSL_RAW_LOGGING_INTERNAL_LEVEL(severity) \ | |||
| ::absl::NormalizeLogSeverity(severity) | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace raw_logging_internal | |||
| { | |||
| // Helper function to implement ABSL_RAW_LOG | |||
| // Logs format... at "severity" level, reporting it | |||
| // as called from file:line. | |||
| // This does not allocate memory or acquire locks. | |||
| void RawLog(absl::LogSeverity severity, const char* file, int line, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); | |||
| // Writes the provided buffer directly to stderr, in a signal-safe, low-level | |||
| // manner. | |||
| void AsyncSignalSafeWriteToStderr(const char* s, size_t len); | |||
| // compile-time function to get the "base" filename, that is, the part of | |||
| // a filename after the last "/" or "\" path separator. The search starts at | |||
| // the end of the string; the second parameter is the length of the string. | |||
| constexpr const char* Basename(const char* fname, int offset) | |||
| { | |||
| return offset == 0 || fname[offset - 1] == '/' || fname[offset - 1] == '\\' ? fname + offset : Basename(fname, offset - 1); | |||
| } | |||
| // For testing only. | |||
| // Returns true if raw logging is fully supported. When it is not | |||
| // fully supported, no messages will be emitted, but a log at FATAL | |||
| // severity will cause an abort. | |||
| // | |||
| // TODO(gfalcon): Come up with a better name for this method. | |||
| bool RawLoggingFullySupported(); | |||
| // Function type for a raw_logging customization hook for suppressing messages | |||
| // by severity, and for writing custom prefixes on non-suppressed messages. | |||
| // | |||
| // The installed hook is called for every raw log invocation. The message will | |||
| // be logged to stderr only if the hook returns true. FATAL errors will cause | |||
| // the process to abort, even if writing to stderr is suppressed. The hook is | |||
| // also provided with an output buffer, where it can write a custom log message | |||
| // prefix. | |||
| // | |||
| // The raw_logging system does not allocate memory or grab locks. User-provided | |||
| // hooks must avoid these operations, and must not throw exceptions. | |||
| // | |||
| // 'severity' is the severity level of the message being written. | |||
| // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro | |||
| // was located. | |||
| // 'buf' and 'buf_size' are pointers to the buffer and buffer size. If the | |||
| // hook writes a prefix, it must increment *buf and decrement *buf_size | |||
| // accordingly. | |||
| using LogFilterAndPrefixHook = bool (*)(absl::LogSeverity severity, const char* file, int line, char** buf, int* buf_size); | |||
| // Function type for a raw_logging customization hook called to abort a process | |||
| // when a FATAL message is logged. If the provided AbortHook() returns, the | |||
| // logging system will call abort(). | |||
| // | |||
| // 'file' and 'line' are the file and line number where the ABSL_RAW_LOG macro | |||
| // was located. | |||
| // The NUL-terminated logged message lives in the buffer between 'buf_start' | |||
| // and 'buf_end'. 'prefix_end' points to the first non-prefix character of the | |||
| // buffer (as written by the LogFilterAndPrefixHook.) | |||
| // | |||
| // The lifetime of the filename and message buffers will not end while the | |||
| // process remains alive. | |||
| using AbortHook = void (*)(const char* file, int line, const char* buf_start, const char* prefix_end, const char* buf_end); | |||
| // Internal logging function for ABSL_INTERNAL_LOG to dispatch to. | |||
| // | |||
| // TODO(gfalcon): When string_view no longer depends on base, change this | |||
| // interface to take its message as a string_view instead. | |||
| using InternalLogFunction = void (*)(absl::LogSeverity severity, const char* file, int line, const std::string& message); | |||
| ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL extern base_internal::AtomicHook< | |||
| InternalLogFunction> | |||
| internal_log_function; | |||
| // Registers hooks of the above types. Only a single hook of each type may be | |||
| // registered. It is an error to call these functions multiple times with | |||
| // different input arguments. | |||
| // | |||
| // These functions are safe to call at any point during initialization; they do | |||
| // not block or malloc, and are async-signal safe. | |||
| void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func); | |||
| void RegisterAbortHook(AbortHook func); | |||
| void RegisterInternalLogFunction(InternalLogFunction func); | |||
| } // namespace raw_logging_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_RAW_LOGGING_H_ | |||
| @@ -0,0 +1,61 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Core interfaces and definitions used by by low-level interfaces such as | |||
| // SpinLock. | |||
| #ifndef ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ | |||
| #define ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Used to describe how a thread may be scheduled. Typically associated with | |||
| // the declaration of a resource supporting synchronized access. | |||
| // | |||
| // SCHEDULE_COOPERATIVE_AND_KERNEL: | |||
| // Specifies that when waiting, a cooperative thread (e.g. a Fiber) may | |||
| // reschedule (using base::scheduling semantics); allowing other cooperative | |||
| // threads to proceed. | |||
| // | |||
| // SCHEDULE_KERNEL_ONLY: (Also described as "non-cooperative") | |||
| // Specifies that no cooperative scheduling semantics may be used, even if the | |||
| // current thread is itself cooperatively scheduled. This means that | |||
| // cooperative threads will NOT allow other cooperative threads to execute in | |||
| // their place while waiting for a resource of this type. Host operating system | |||
| // semantics (e.g. a futex) may still be used. | |||
| // | |||
| // When optional, clients should strongly prefer SCHEDULE_COOPERATIVE_AND_KERNEL | |||
| // by default. SCHEDULE_KERNEL_ONLY should only be used for resources on which | |||
| // base::scheduling (e.g. the implementation of a Scheduler) may depend. | |||
| // | |||
| // NOTE: Cooperative resources may not be nested below non-cooperative ones. | |||
| // This means that it is invalid to to acquire a SCHEDULE_COOPERATIVE_AND_KERNEL | |||
| // resource if a SCHEDULE_KERNEL_ONLY resource is already held. | |||
| enum SchedulingMode | |||
| { | |||
| SCHEDULE_KERNEL_ONLY = 0, // Allow scheduling only the host OS. | |||
| SCHEDULE_COOPERATIVE_AND_KERNEL, // Also allow cooperative scheduling. | |||
| }; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_SCHEDULING_MODE_H_ | |||
| @@ -0,0 +1,48 @@ | |||
| // | |||
| // Copyright 2019 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ | |||
| #define ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ | |||
| #include <string> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| class ScopedSetEnv | |||
| { | |||
| public: | |||
| ScopedSetEnv(const char* var_name, const char* new_value); | |||
| ~ScopedSetEnv(); | |||
| private: | |||
| std::string var_name_; | |||
| std::string old_value_; | |||
| // True if the environment variable was initially not set. | |||
| bool was_unset_; | |||
| }; | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_SCOPED_SET_ENV_H_ | |||
| @@ -0,0 +1,286 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Most users requiring mutual exclusion should use Mutex. | |||
| // SpinLock is provided for use in two situations: | |||
| // - for use by Abseil internal code that Mutex itself depends on | |||
| // - for async signal safety (see below) | |||
| // SpinLock is async signal safe. If a spinlock is used within a signal | |||
| // handler, all code that acquires the lock must ensure that the signal cannot | |||
| // arrive while they are holding the lock. Typically, this is done by blocking | |||
| // the signal. | |||
| // | |||
| // Threads waiting on a SpinLock may be woken in an arbitrary order. | |||
| #ifndef ABSL_BASE_INTERNAL_SPINLOCK_H_ | |||
| #define ABSL_BASE_INTERNAL_SPINLOCK_H_ | |||
| #include <stdint.h> | |||
| #include <sys/types.h> | |||
| #include <atomic> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/const_init.h" | |||
| #include "absl/base/dynamic_annotations.h" | |||
| #include "absl/base/internal/low_level_scheduling.h" | |||
| #include "absl/base/internal/raw_logging.h" | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| #include "absl/base/internal/tsan_mutex_interface.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/port.h" | |||
| #include "absl/base/thread_annotations.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| class ABSL_LOCKABLE SpinLock | |||
| { | |||
| public: | |||
| SpinLock() : | |||
| lockword_(kSpinLockCooperative) | |||
| { | |||
| ABSL_TSAN_MUTEX_CREATE(this, __tsan_mutex_not_static); | |||
| } | |||
| // Constructors that allow non-cooperative spinlocks to be created for use | |||
| // inside thread schedulers. Normal clients should not use these. | |||
| explicit SpinLock(base_internal::SchedulingMode mode); | |||
| // Constructor for global SpinLock instances. See absl/base/const_init.h. | |||
| constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode) : | |||
| lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) | |||
| { | |||
| } | |||
| // For global SpinLock instances prefer trivial destructor when possible. | |||
| // Default but non-trivial destructor in some build configurations causes an | |||
| // extra static initializer. | |||
| #ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE | |||
| ~SpinLock() | |||
| { | |||
| ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); | |||
| } | |||
| #else | |||
| ~SpinLock() = default; | |||
| #endif | |||
| // Acquire this SpinLock. | |||
| inline void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() | |||
| { | |||
| ABSL_TSAN_MUTEX_PRE_LOCK(this, 0); | |||
| if (!TryLockImpl()) | |||
| { | |||
| SlowLock(); | |||
| } | |||
| ABSL_TSAN_MUTEX_POST_LOCK(this, 0, 0); | |||
| } | |||
| // Try to acquire this SpinLock without blocking and return true if the | |||
| // acquisition was successful. If the lock was not acquired, false is | |||
| // returned. If this SpinLock is free at the time of the call, TryLock | |||
| // will return true with high probability. | |||
| inline bool TryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) | |||
| { | |||
| ABSL_TSAN_MUTEX_PRE_LOCK(this, __tsan_mutex_try_lock); | |||
| bool res = TryLockImpl(); | |||
| ABSL_TSAN_MUTEX_POST_LOCK( | |||
| this, __tsan_mutex_try_lock | (res ? 0 : __tsan_mutex_try_lock_failed), 0 | |||
| ); | |||
| return res; | |||
| } | |||
| // Release this SpinLock, which must be held by the calling thread. | |||
| inline void Unlock() ABSL_UNLOCK_FUNCTION() | |||
| { | |||
| ABSL_TSAN_MUTEX_PRE_UNLOCK(this, 0); | |||
| uint32_t lock_value = lockword_.load(std::memory_order_relaxed); | |||
| lock_value = lockword_.exchange(lock_value & kSpinLockCooperative, std::memory_order_release); | |||
| if ((lock_value & kSpinLockDisabledScheduling) != 0) | |||
| { | |||
| base_internal::SchedulingGuard::EnableRescheduling(true); | |||
| } | |||
| if ((lock_value & kWaitTimeMask) != 0) | |||
| { | |||
| // Collect contentionz profile info, and speed the wakeup of any waiter. | |||
| // The wait_cycles value indicates how long this thread spent waiting | |||
| // for the lock. | |||
| SlowUnlock(lock_value); | |||
| } | |||
| ABSL_TSAN_MUTEX_POST_UNLOCK(this, 0); | |||
| } | |||
| // Determine if the lock is held. When the lock is held by the invoking | |||
| // thread, true will always be returned. Intended to be used as | |||
| // CHECK(lock.IsHeld()). | |||
| inline bool IsHeld() const | |||
| { | |||
| return (lockword_.load(std::memory_order_relaxed) & kSpinLockHeld) != 0; | |||
| } | |||
| // Return immediately if this thread holds the SpinLock exclusively. | |||
| // Otherwise, report an error by crashing with a diagnostic. | |||
| inline void AssertHeld() const ABSL_ASSERT_EXCLUSIVE_LOCK() | |||
| { | |||
| if (!IsHeld()) | |||
| { | |||
| ABSL_RAW_LOG(FATAL, "thread should hold the lock on SpinLock"); | |||
| } | |||
| } | |||
| protected: | |||
| // These should not be exported except for testing. | |||
| // Store number of cycles between wait_start_time and wait_end_time in a | |||
| // lock value. | |||
| static uint32_t EncodeWaitCycles(int64_t wait_start_time, int64_t wait_end_time); | |||
| // Extract number of wait cycles in a lock value. | |||
| static uint64_t DecodeWaitCycles(uint32_t lock_value); | |||
| // Provide access to protected method above. Use for testing only. | |||
| friend struct SpinLockTest; | |||
| private: | |||
| // lockword_ is used to store the following: | |||
| // | |||
| // bit[0] encodes whether a lock is being held. | |||
| // bit[1] encodes whether a lock uses cooperative scheduling. | |||
| // bit[2] encodes whether the current lock holder disabled scheduling when | |||
| // acquiring the lock. Only set when kSpinLockHeld is also set. | |||
| // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. | |||
| // This is set by the lock holder to indicate how long it waited on | |||
| // the lock before eventually acquiring it. The number of cycles is | |||
| // encoded as a 29-bit unsigned int, or in the case that the current | |||
| // holder did not wait but another waiter is queued, the LSB | |||
| // (kSpinLockSleeper) is set. The implementation does not explicitly | |||
| // track the number of queued waiters beyond this. It must always be | |||
| // assumed that waiters may exist if the current holder was required to | |||
| // queue. | |||
| // | |||
| // Invariant: if the lock is not held, the value is either 0 or | |||
| // kSpinLockCooperative. | |||
| static constexpr uint32_t kSpinLockHeld = 1; | |||
| static constexpr uint32_t kSpinLockCooperative = 2; | |||
| static constexpr uint32_t kSpinLockDisabledScheduling = 4; | |||
| static constexpr uint32_t kSpinLockSleeper = 8; | |||
| // Includes kSpinLockSleeper. | |||
| static constexpr uint32_t kWaitTimeMask = | |||
| ~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling); | |||
| // Returns true if the provided scheduling mode is cooperative. | |||
| static constexpr bool IsCooperative( | |||
| base_internal::SchedulingMode scheduling_mode | |||
| ) | |||
| { | |||
| return scheduling_mode == base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL; | |||
| } | |||
| uint32_t TryLockInternal(uint32_t lock_value, uint32_t wait_cycles); | |||
| void SlowLock() ABSL_ATTRIBUTE_COLD; | |||
| void SlowUnlock(uint32_t lock_value) ABSL_ATTRIBUTE_COLD; | |||
| uint32_t SpinLoop(); | |||
| inline bool TryLockImpl() | |||
| { | |||
| uint32_t lock_value = lockword_.load(std::memory_order_relaxed); | |||
| return (TryLockInternal(lock_value, 0) & kSpinLockHeld) == 0; | |||
| } | |||
| std::atomic<uint32_t> lockword_; | |||
| SpinLock(const SpinLock&) = delete; | |||
| SpinLock& operator=(const SpinLock&) = delete; | |||
| }; | |||
| // Corresponding locker object that arranges to acquire a spinlock for | |||
| // the duration of a C++ scope. | |||
| class ABSL_SCOPED_LOCKABLE SpinLockHolder | |||
| { | |||
| public: | |||
| inline explicit SpinLockHolder(SpinLock* l) ABSL_EXCLUSIVE_LOCK_FUNCTION(l) : | |||
| lock_(l) | |||
| { | |||
| l->Lock(); | |||
| } | |||
| inline ~SpinLockHolder() ABSL_UNLOCK_FUNCTION() | |||
| { | |||
| lock_->Unlock(); | |||
| } | |||
| SpinLockHolder(const SpinLockHolder&) = delete; | |||
| SpinLockHolder& operator=(const SpinLockHolder&) = delete; | |||
| private: | |||
| SpinLock* lock_; | |||
| }; | |||
| // Register a hook for profiling support. | |||
| // | |||
| // The function pointer registered here will be called whenever a spinlock is | |||
| // contended. The callback is given an opaque handle to the contended spinlock | |||
| // and the number of wait cycles. This is thread-safe, but only a single | |||
| // profiler can be registered. It is an error to call this function multiple | |||
| // times with different arguments. | |||
| void RegisterSpinLockProfiler(void (*fn)(const void* lock, int64_t wait_cycles)); | |||
| //------------------------------------------------------------------------------ | |||
| // Public interface ends here. | |||
| //------------------------------------------------------------------------------ | |||
| // If (result & kSpinLockHeld) == 0, then *this was successfully locked. | |||
| // Otherwise, returns last observed value for lockword_. | |||
| inline uint32_t SpinLock::TryLockInternal(uint32_t lock_value, uint32_t wait_cycles) | |||
| { | |||
| if ((lock_value & kSpinLockHeld) != 0) | |||
| { | |||
| return lock_value; | |||
| } | |||
| uint32_t sched_disabled_bit = 0; | |||
| if ((lock_value & kSpinLockCooperative) == 0) | |||
| { | |||
| // For non-cooperative locks we must make sure we mark ourselves as | |||
| // non-reschedulable before we attempt to CompareAndSwap. | |||
| if (base_internal::SchedulingGuard::DisableRescheduling()) | |||
| { | |||
| sched_disabled_bit = kSpinLockDisabledScheduling; | |||
| } | |||
| } | |||
| if (!lockword_.compare_exchange_strong( | |||
| lock_value, | |||
| kSpinLockHeld | lock_value | wait_cycles | sched_disabled_bit, | |||
| std::memory_order_acquire, | |||
| std::memory_order_relaxed | |||
| )) | |||
| { | |||
| base_internal::SchedulingGuard::EnableRescheduling(sched_disabled_bit != 0); | |||
| } | |||
| return lock_value; | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_SPINLOCK_H_ | |||
| @@ -0,0 +1,35 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file is an Akaros-specific part of spinlock_wait.cc | |||
| #include <atomic> | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| extern "C" { | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( | |||
| std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, | |||
| int /* loop */, absl::base_internal::SchedulingMode /* mode */) { | |||
| // In Akaros, one must take care not to call anything that could cause a | |||
| // malloc(), a blocking system call, or a uthread_yield() while holding a | |||
| // spinlock. Our callers assume will not call into libraries or other | |||
| // arbitrary code. | |||
| } | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( | |||
| std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} | |||
| } // extern "C" | |||
| @@ -0,0 +1,71 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file is a Linux-specific part of spinlock_wait.cc | |||
| #include <linux/futex.h> | |||
| #include <sys/syscall.h> | |||
| #include <unistd.h> | |||
| #include <atomic> | |||
| #include <climits> | |||
| #include <cstdint> | |||
| #include <ctime> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/internal/errno_saver.h" | |||
| // The SpinLock lockword is `std::atomic<uint32_t>`. Here we assert that | |||
| // `std::atomic<uint32_t>` is bitwise equivalent of the `int` expected | |||
| // by SYS_futex. We also assume that reads/writes done to the lockword | |||
| // by SYS_futex have rational semantics with regard to the | |||
| // std::atomic<> API. C++ provides no guarantees of these assumptions, | |||
| // but they are believed to hold in practice. | |||
| static_assert(sizeof(std::atomic<uint32_t>) == sizeof(int), | |||
| "SpinLock lockword has the wrong size for a futex"); | |||
| // Some Android headers are missing these definitions even though they | |||
| // support these futex operations. | |||
| #ifdef __BIONIC__ | |||
| #ifndef SYS_futex | |||
| #define SYS_futex __NR_futex | |||
| #endif | |||
| #ifndef FUTEX_PRIVATE_FLAG | |||
| #define FUTEX_PRIVATE_FLAG 128 | |||
| #endif | |||
| #endif | |||
| #if defined(__NR_futex_time64) && !defined(SYS_futex_time64) | |||
| #define SYS_futex_time64 __NR_futex_time64 | |||
| #endif | |||
| #if defined(SYS_futex_time64) && !defined(SYS_futex) | |||
| #define SYS_futex SYS_futex_time64 | |||
| #endif | |||
| extern "C" { | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( | |||
| std::atomic<uint32_t> *w, uint32_t value, int, | |||
| absl::base_internal::SchedulingMode) { | |||
| absl::base_internal::ErrnoSaver errno_saver; | |||
| syscall(SYS_futex, w, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, nullptr); | |||
| } | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( | |||
| std::atomic<uint32_t> *w, bool all) { | |||
| syscall(SYS_futex, w, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, all ? INT_MAX : 1, 0); | |||
| } | |||
| } // extern "C" | |||
| @@ -0,0 +1,46 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file is a Posix-specific part of spinlock_wait.cc | |||
| #include <sched.h> | |||
| #include <atomic> | |||
| #include <ctime> | |||
| #include "absl/base/internal/errno_saver.h" | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| #include "absl/base/port.h" | |||
| extern "C" { | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( | |||
| std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop, | |||
| absl::base_internal::SchedulingMode /* mode */) { | |||
| absl::base_internal::ErrnoSaver errno_saver; | |||
| if (loop == 0) { | |||
| } else if (loop == 1) { | |||
| sched_yield(); | |||
| } else { | |||
| struct timespec tm; | |||
| tm.tv_sec = 0; | |||
| tm.tv_nsec = absl::base_internal::SpinLockSuggestedDelayNS(loop); | |||
| nanosleep(&tm, nullptr); | |||
| } | |||
| } | |||
| ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( | |||
| std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} | |||
| } // extern "C" | |||
| @@ -0,0 +1,97 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ | |||
| #define ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ | |||
| // Operations to make atomic transitions on a word, and to allow | |||
| // waiting for those transitions to become possible. | |||
| #include <stdint.h> | |||
| #include <atomic> | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // SpinLockWait() waits until it can perform one of several transitions from | |||
| // "from" to "to". It returns when it performs a transition where done==true. | |||
| struct SpinLockWaitTransition | |||
| { | |||
| uint32_t from; | |||
| uint32_t to; | |||
| bool done; | |||
| }; | |||
| // Wait until *w can transition from trans[i].from to trans[i].to for some i | |||
| // satisfying 0<=i<n && trans[i].done, atomically make the transition, | |||
| // then return the old value of *w. Make any other atomic transitions | |||
| // where !trans[i].done, but continue waiting. | |||
| // | |||
| // Wakeups for threads blocked on SpinLockWait do not respect priorities. | |||
| uint32_t SpinLockWait(std::atomic<uint32_t>* w, int n, const SpinLockWaitTransition trans[], SchedulingMode scheduling_mode); | |||
| // If possible, wake some thread that has called SpinLockDelay(w, ...). If `all` | |||
| // is true, wake all such threads. On some systems, this may be a no-op; on | |||
| // those systems, threads calling SpinLockDelay() will always wake eventually | |||
| // even if SpinLockWake() is never called. | |||
| void SpinLockWake(std::atomic<uint32_t>* w, bool all); | |||
| // Wait for an appropriate spin delay on iteration "loop" of a | |||
| // spin loop on location *w, whose previously observed value was "value". | |||
| // SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick, | |||
| // or may wait for a call to SpinLockWake(w). | |||
| void SpinLockDelay(std::atomic<uint32_t>* w, uint32_t value, int loop, base_internal::SchedulingMode scheduling_mode); | |||
| // Helper used by AbslInternalSpinLockDelay. | |||
| // Returns a suggested delay in nanoseconds for iteration number "loop". | |||
| int SpinLockSuggestedDelayNS(int loop); | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| // In some build configurations we pass --detect-odr-violations to the | |||
| // gold linker. This causes it to flag weak symbol overrides as ODR | |||
| // violations. Because ODR only applies to C++ and not C, | |||
| // --detect-odr-violations ignores symbols not mangled with C++ names. | |||
| // By changing our extension points to be extern "C", we dodge this | |||
| // check. | |||
| extern "C" | |||
| { | |||
| void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)(std::atomic<uint32_t>* w, bool all); | |||
| void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( | |||
| std::atomic<uint32_t>* w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode | |||
| ); | |||
| } | |||
| inline void absl::base_internal::SpinLockWake(std::atomic<uint32_t>* w, bool all) | |||
| { | |||
| ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake) | |||
| (w, all); | |||
| } | |||
| inline void absl::base_internal::SpinLockDelay( | |||
| std::atomic<uint32_t>* w, uint32_t value, int loop, absl::base_internal::SchedulingMode scheduling_mode | |||
| ) | |||
| { | |||
| ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay) | |||
| (w, value, loop, scheduling_mode); | |||
| } | |||
| #endif // ABSL_BASE_INTERNAL_SPINLOCK_WAIT_H_ | |||
| @@ -0,0 +1,37 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file is a Win32-specific part of spinlock_wait.cc | |||
| #include <windows.h> | |||
| #include <atomic> | |||
| #include "absl/base/internal/scheduling_mode.h" | |||
| extern "C" { | |||
| void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockDelay)( | |||
| std::atomic<uint32_t>* /* lock_word */, uint32_t /* value */, int loop, | |||
| absl::base_internal::SchedulingMode /* mode */) { | |||
| if (loop == 0) { | |||
| } else if (loop == 1) { | |||
| Sleep(0); | |||
| } else { | |||
| Sleep(absl::base_internal::SpinLockSuggestedDelayNS(loop) / 1000000); | |||
| } | |||
| } | |||
| void ABSL_INTERNAL_C_SYMBOL(AbslInternalSpinLockWake)( | |||
| std::atomic<uint32_t>* /* lock_word */, bool /* all */) {} | |||
| } // extern "C" | |||
| @@ -0,0 +1,41 @@ | |||
| // Copyright 2020 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_INTERNAL_STRERROR_H_ | |||
| #define ABSL_BASE_INTERNAL_STRERROR_H_ | |||
| #include <string> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // A portable and thread-safe alternative to C89's `strerror`. | |||
| // | |||
| // The C89 specification of `strerror` is not suitable for use in a | |||
| // multi-threaded application as the returned string may be changed by calls to | |||
| // `strerror` from another thread. The many non-stdlib alternatives differ | |||
| // enough in their names, availability, and semantics to justify this wrapper | |||
| // around them. `errno` will not be modified by a call to `absl::StrError`. | |||
| std::string StrError(int errnum); | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_STRERROR_H_ | |||
| @@ -0,0 +1,76 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file includes routines to find out characteristics | |||
| // of the machine a program is running on. It is undoubtedly | |||
| // system-dependent. | |||
| // Functions listed here that accept a pid_t as an argument act on the | |||
| // current process if the pid_t argument is 0 | |||
| // All functions here are thread-hostile due to file caching unless | |||
| // commented otherwise. | |||
| #ifndef ABSL_BASE_INTERNAL_SYSINFO_H_ | |||
| #define ABSL_BASE_INTERNAL_SYSINFO_H_ | |||
| #ifndef _WIN32 | |||
| #include <sys/types.h> | |||
| #endif | |||
| #include <cstdint> | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/port.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Nominal core processor cycles per second of each processor. This is _not_ | |||
| // necessarily the frequency of the CycleClock counter (see cycleclock.h) | |||
| // Thread-safe. | |||
| double NominalCPUFrequency(); | |||
| // Number of logical processors (hyperthreads) in system. Thread-safe. | |||
| int NumCPUs(); | |||
| // Return the thread id of the current thread, as told by the system. | |||
| // No two currently-live threads implemented by the OS shall have the same ID. | |||
| // Thread ids of exited threads may be reused. Multiple user-level threads | |||
| // may have the same thread ID if multiplexed on the same OS thread. | |||
| // | |||
| // On Linux, you may send a signal to the resulting ID with kill(). However, | |||
| // it is recommended for portability that you use pthread_kill() instead. | |||
| #ifdef _WIN32 | |||
| // On Windows, process id and thread id are of the same type according to the | |||
| // return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned | |||
| // 32-bit type. | |||
| using pid_t = uint32_t; | |||
| #endif | |||
| pid_t GetTID(); | |||
| // Like GetTID(), but caches the result in thread-local storage in order | |||
| // to avoid unnecessary system calls. Note that there are some cases where | |||
| // one must call through to GetTID directly, which is why this exists as a | |||
| // separate function. For example, GetCachedTID() is not safe to call in | |||
| // an asynchronous signal-handling context nor right after a call to fork(). | |||
| pid_t GetCachedTID(); | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_SYSINFO_H_ | |||
| @@ -0,0 +1,273 @@ | |||
| // Copyright 2019 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: thread_annotations.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // WARNING: This is a backwards compatible header and it will be removed after | |||
| // the migration to prefixed thread annotations is finished; please include | |||
| // "absl/base/thread_annotations.h". | |||
| // | |||
| // This header file contains macro definitions for thread safety annotations | |||
| // that allow developers to document the locking policies of multi-threaded | |||
| // code. The annotations can also help program analysis tools to identify | |||
| // potential thread safety issues. | |||
| // | |||
| // These annotations are implemented using compiler attributes. Using the macros | |||
| // defined here instead of raw attributes allow for portability and future | |||
| // compatibility. | |||
| // | |||
| // When referring to mutexes in the arguments of the attributes, you should | |||
| // use variable names or more complex expressions (e.g. my_object->mutex_) | |||
| // that evaluate to a concrete mutex object whenever possible. If the mutex | |||
| // you want to refer to is not in scope, you may use a member pointer | |||
| // (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. | |||
| #ifndef ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ | |||
| #define ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ | |||
| #if defined(__clang__) | |||
| #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) | |||
| #else | |||
| #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op | |||
| #endif | |||
| // GUARDED_BY() | |||
| // | |||
| // Documents if a shared field or global variable needs to be protected by a | |||
| // mutex. GUARDED_BY() allows the user to specify a particular mutex that | |||
| // should be held when accessing the annotated variable. | |||
| // | |||
| // Although this annotation (and PT_GUARDED_BY, below) cannot be applied to | |||
| // local variables, a local variable and its associated mutex can often be | |||
| // combined into a small class or struct, thereby allowing the annotation. | |||
| // | |||
| // Example: | |||
| // | |||
| // class Foo { | |||
| // Mutex mu_; | |||
| // int p1_ GUARDED_BY(mu_); | |||
| // ... | |||
| // }; | |||
| #define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) | |||
| // PT_GUARDED_BY() | |||
| // | |||
| // Documents if the memory location pointed to by a pointer should be guarded | |||
| // by a mutex when dereferencing the pointer. | |||
| // | |||
| // Example: | |||
| // class Foo { | |||
| // Mutex mu_; | |||
| // int *p1_ PT_GUARDED_BY(mu_); | |||
| // ... | |||
| // }; | |||
| // | |||
| // Note that a pointer variable to a shared memory location could itself be a | |||
| // shared variable. | |||
| // | |||
| // Example: | |||
| // | |||
| // // `q_`, guarded by `mu1_`, points to a shared memory location that is | |||
| // // guarded by `mu2_`: | |||
| // int *q_ GUARDED_BY(mu1_) PT_GUARDED_BY(mu2_); | |||
| #define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) | |||
| // ACQUIRED_AFTER() / ACQUIRED_BEFORE() | |||
| // | |||
| // Documents the acquisition order between locks that can be held | |||
| // simultaneously by a thread. For any two locks that need to be annotated | |||
| // to establish an acquisition order, only one of them needs the annotation. | |||
| // (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER | |||
| // and ACQUIRED_BEFORE.) | |||
| // | |||
| // As with GUARDED_BY, this is only applicable to mutexes that are shared | |||
| // fields or global variables. | |||
| // | |||
| // Example: | |||
| // | |||
| // Mutex m1_; | |||
| // Mutex m2_ ACQUIRED_AFTER(m1_); | |||
| #define ACQUIRED_AFTER(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) | |||
| #define ACQUIRED_BEFORE(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) | |||
| // EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED() | |||
| // | |||
| // Documents a function that expects a mutex to be held prior to entry. | |||
| // The mutex is expected to be held both on entry to, and exit from, the | |||
| // function. | |||
| // | |||
| // An exclusive lock allows read-write access to the guarded data member(s), and | |||
| // only one thread can acquire a lock exclusively at any one time. A shared lock | |||
| // allows read-only access, and any number of threads can acquire a shared lock | |||
| // concurrently. | |||
| // | |||
| // Generally, non-const methods should be annotated with | |||
| // EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with | |||
| // SHARED_LOCKS_REQUIRED. | |||
| // | |||
| // Example: | |||
| // | |||
| // Mutex mu1, mu2; | |||
| // int a GUARDED_BY(mu1); | |||
| // int b GUARDED_BY(mu2); | |||
| // | |||
| // void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } | |||
| // void bar() const SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } | |||
| #define EXCLUSIVE_LOCKS_REQUIRED(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) | |||
| #define SHARED_LOCKS_REQUIRED(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) | |||
| // LOCKS_EXCLUDED() | |||
| // | |||
| // Documents the locks acquired in the body of the function. These locks | |||
| // cannot be held when calling this function (as Abseil's `Mutex` locks are | |||
| // non-reentrant). | |||
| #define LOCKS_EXCLUDED(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) | |||
| // LOCK_RETURNED() | |||
| // | |||
| // Documents a function that returns a mutex without acquiring it. For example, | |||
| // a public getter method that returns a pointer to a private mutex should | |||
| // be annotated with LOCK_RETURNED. | |||
| #define LOCK_RETURNED(x) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) | |||
| // LOCKABLE | |||
| // | |||
| // Documents if a class/type is a lockable type (such as the `Mutex` class). | |||
| #define LOCKABLE \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(lockable) | |||
| // SCOPED_LOCKABLE | |||
| // | |||
| // Documents if a class does RAII locking (such as the `MutexLock` class). | |||
| // The constructor should use `LOCK_FUNCTION()` to specify the mutex that is | |||
| // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no | |||
| // arguments; the analysis will assume that the destructor unlocks whatever the | |||
| // constructor locked. | |||
| #define SCOPED_LOCKABLE \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) | |||
| // EXCLUSIVE_LOCK_FUNCTION() | |||
| // | |||
| // Documents functions that acquire a lock in the body of a function, and do | |||
| // not release it. | |||
| #define EXCLUSIVE_LOCK_FUNCTION(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) | |||
| // SHARED_LOCK_FUNCTION() | |||
| // | |||
| // Documents functions that acquire a shared (reader) lock in the body of a | |||
| // function, and do not release it. | |||
| #define SHARED_LOCK_FUNCTION(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) | |||
| // UNLOCK_FUNCTION() | |||
| // | |||
| // Documents functions that expect a lock to be held on entry to the function, | |||
| // and release it in the body of the function. | |||
| #define UNLOCK_FUNCTION(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) | |||
| // EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION() | |||
| // | |||
| // Documents functions that try to acquire a lock, and return success or failure | |||
| // (or a non-boolean value that can be interpreted as a boolean). | |||
| // The first argument should be `true` for functions that return `true` on | |||
| // success, or `false` for functions that return `false` on success. The second | |||
| // argument specifies the mutex that is locked on success. If unspecified, this | |||
| // mutex is assumed to be `this`. | |||
| #define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) | |||
| #define SHARED_TRYLOCK_FUNCTION(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) | |||
| // ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK() | |||
| // | |||
| // Documents functions that dynamically check to see if a lock is held, and fail | |||
| // if it is not held. | |||
| #define ASSERT_EXCLUSIVE_LOCK(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) | |||
| #define ASSERT_SHARED_LOCK(...) \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) | |||
| // NO_THREAD_SAFETY_ANALYSIS | |||
| // | |||
| // Turns off thread safety checking within the body of a particular function. | |||
| // This annotation is used to mark functions that are known to be correct, but | |||
| // the locking behavior is more complicated than the analyzer can handle. | |||
| #define NO_THREAD_SAFETY_ANALYSIS \ | |||
| THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) | |||
| //------------------------------------------------------------------------------ | |||
| // Tool-Supplied Annotations | |||
| //------------------------------------------------------------------------------ | |||
| // TS_UNCHECKED should be placed around lock expressions that are not valid | |||
| // C++ syntax, but which are present for documentation purposes. These | |||
| // annotations will be ignored by the analysis. | |||
| #define TS_UNCHECKED(x) "" | |||
| // TS_FIXME is used to mark lock expressions that are not valid C++ syntax. | |||
| // It is used by automated tools to mark and disable invalid expressions. | |||
| // The annotation should either be fixed, or changed to TS_UNCHECKED. | |||
| #define TS_FIXME(x) "" | |||
| // Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of | |||
| // a particular function. However, this attribute is used to mark functions | |||
| // that are incorrect and need to be fixed. It is used by automated tools to | |||
| // avoid breaking the build when the analysis is updated. | |||
| // Code owners are expected to eventually fix the routine. | |||
| #define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS | |||
| // Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY | |||
| // annotation that needs to be fixed, because it is producing thread safety | |||
| // warning. It disables the GUARDED_BY. | |||
| #define GUARDED_BY_FIXME(x) | |||
| // Disables warnings for a single read operation. This can be used to avoid | |||
| // warnings when it is known that the read is not actually involved in a race, | |||
| // but the compiler cannot confirm that. | |||
| #define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x) | |||
| namespace thread_safety_analysis | |||
| { | |||
| // Takes a reference to a guarded data member, and returns an unguarded | |||
| // reference. | |||
| template<typename T> | |||
| inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS | |||
| { | |||
| return v; | |||
| } | |||
| template<typename T> | |||
| inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS | |||
| { | |||
| return v; | |||
| } | |||
| } // namespace thread_safety_analysis | |||
| #endif // ABSL_BASE_INTERNAL_THREAD_ANNOTATIONS_H_ | |||
| @@ -0,0 +1,272 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Each active thread has an ThreadIdentity that may represent the thread in | |||
| // various level interfaces. ThreadIdentity objects are never deallocated. | |||
| // When a thread terminates, its ThreadIdentity object may be reused for a | |||
| // thread created later. | |||
| #ifndef ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ | |||
| #define ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ | |||
| #ifndef _WIN32 | |||
| #include <pthread.h> | |||
| // Defines __GOOGLE_GRTE_VERSION__ (via glibc-specific features.h) when | |||
| // supported. | |||
| #include <unistd.h> | |||
| #endif | |||
| #include <atomic> | |||
| #include <cstdint> | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/internal/per_thread_tls.h" | |||
| #include "absl/base/optimization.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| struct SynchLocksHeld; | |||
| struct SynchWaitParams; | |||
| namespace base_internal | |||
| { | |||
| class SpinLock; | |||
| struct ThreadIdentity; | |||
| // Used by the implementation of absl::Mutex and absl::CondVar. | |||
| struct PerThreadSynch | |||
| { | |||
| // The internal representation of absl::Mutex and absl::CondVar rely | |||
| // on the alignment of PerThreadSynch. Both store the address of the | |||
| // PerThreadSynch in the high-order bits of their internal state, | |||
| // which means the low kLowZeroBits of the address of PerThreadSynch | |||
| // must be zero. | |||
| static constexpr int kLowZeroBits = 8; | |||
| static constexpr int kAlignment = 1 << kLowZeroBits; | |||
| // Returns the associated ThreadIdentity. | |||
| // This can be implemented as a cast because we guarantee | |||
| // PerThreadSynch is the first element of ThreadIdentity. | |||
| ThreadIdentity* thread_identity() | |||
| { | |||
| return reinterpret_cast<ThreadIdentity*>(this); | |||
| } | |||
| PerThreadSynch* next; // Circular waiter queue; initialized to 0. | |||
| PerThreadSynch* skip; // If non-zero, all entries in Mutex queue | |||
| // up to and including "skip" have same | |||
| // condition as this, and will be woken later | |||
| bool may_skip; // if false while on mutex queue, a mutex unlocker | |||
| // is using this PerThreadSynch as a terminator. Its | |||
| // skip field must not be filled in because the loop | |||
| // might then skip over the terminator. | |||
| bool wake; // This thread is to be woken from a Mutex. | |||
| // If "x" is on a waiter list for a mutex, "x->cond_waiter" is true iff the | |||
| // waiter is waiting on the mutex as part of a CV Wait or Mutex Await. | |||
| // | |||
| // The value of "x->cond_waiter" is meaningless if "x" is not on a | |||
| // Mutex waiter list. | |||
| bool cond_waiter; | |||
| bool maybe_unlocking; // Valid at head of Mutex waiter queue; | |||
| // true if UnlockSlow could be searching | |||
| // for a waiter to wake. Used for an optimization | |||
| // in Enqueue(). true is always a valid value. | |||
| // Can be reset to false when the unlocker or any | |||
| // writer releases the lock, or a reader fully | |||
| // releases the lock. It may not be set to false | |||
| // by a reader that decrements the count to | |||
| // non-zero. protected by mutex spinlock | |||
| bool suppress_fatal_errors; // If true, try to proceed even in the face | |||
| // of broken invariants. This is used within | |||
| // fatal signal handlers to improve the | |||
| // chances of debug logging information being | |||
| // output successfully. | |||
| int priority; // Priority of thread (updated every so often). | |||
| // State values: | |||
| // kAvailable: This PerThreadSynch is available. | |||
| // kQueued: This PerThreadSynch is unavailable, it's currently queued on a | |||
| // Mutex or CondVar waistlist. | |||
| // | |||
| // Transitions from kQueued to kAvailable require a release | |||
| // barrier. This is needed as a waiter may use "state" to | |||
| // independently observe that it's no longer queued. | |||
| // | |||
| // Transitions from kAvailable to kQueued require no barrier, they | |||
| // are externally ordered by the Mutex. | |||
| enum State | |||
| { | |||
| kAvailable, | |||
| kQueued | |||
| }; | |||
| std::atomic<State> state; | |||
| // The wait parameters of the current wait. waitp is null if the | |||
| // thread is not waiting. Transitions from null to non-null must | |||
| // occur before the enqueue commit point (state = kQueued in | |||
| // Enqueue() and CondVarEnqueue()). Transitions from non-null to | |||
| // null must occur after the wait is finished (state = kAvailable in | |||
| // Mutex::Block() and CondVar::WaitCommon()). This field may be | |||
| // changed only by the thread that describes this PerThreadSynch. A | |||
| // special case is Fer(), which calls Enqueue() on another thread, | |||
| // but with an identical SynchWaitParams pointer, thus leaving the | |||
| // pointer unchanged. | |||
| SynchWaitParams* waitp; | |||
| intptr_t readers; // Number of readers in mutex. | |||
| // When priority will next be read (cycles). | |||
| int64_t next_priority_read_cycles; | |||
| // Locks held; used during deadlock detection. | |||
| // Allocated in Synch_GetAllLocks() and freed in ReclaimThreadIdentity(). | |||
| SynchLocksHeld* all_locks; | |||
| }; | |||
| // The instances of this class are allocated in NewThreadIdentity() with an | |||
| // alignment of PerThreadSynch::kAlignment. | |||
| struct ThreadIdentity | |||
| { | |||
| // Must be the first member. The Mutex implementation requires that | |||
| // the PerThreadSynch object associated with each thread is | |||
| // PerThreadSynch::kAlignment aligned. We provide this alignment on | |||
| // ThreadIdentity itself. | |||
| PerThreadSynch per_thread_synch; | |||
| // Private: Reserved for absl::synchronization_internal::Waiter. | |||
| struct WaiterState | |||
| { | |||
| alignas(void*) char data[128]; | |||
| } waiter_state; | |||
| // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). | |||
| std::atomic<int>* blocked_count_ptr; | |||
| // The following variables are mostly read/written just by the | |||
| // thread itself. The only exception is that these are read by | |||
| // a ticker thread as a hint. | |||
| std::atomic<int> ticker; // Tick counter, incremented once per second. | |||
| std::atomic<int> wait_start; // Ticker value when thread started waiting. | |||
| std::atomic<bool> is_idle; // Has thread become idle yet? | |||
| ThreadIdentity* next; | |||
| }; | |||
| // Returns the ThreadIdentity object representing the calling thread; guaranteed | |||
| // to be unique for its lifetime. The returned object will remain valid for the | |||
| // program's lifetime; although it may be re-assigned to a subsequent thread. | |||
| // If one does not exist, return nullptr instead. | |||
| // | |||
| // Does not malloc(*), and is async-signal safe. | |||
| // [*] Technically pthread_setspecific() does malloc on first use; however this | |||
| // is handled internally within tcmalloc's initialization already. | |||
| // | |||
| // New ThreadIdentity objects can be constructed and associated with a thread | |||
| // by calling GetOrCreateCurrentThreadIdentity() in per-thread-sem.h. | |||
| ThreadIdentity* CurrentThreadIdentityIfPresent(); | |||
| using ThreadIdentityReclaimerFunction = void (*)(void*); | |||
| // Sets the current thread identity to the given value. 'reclaimer' is a | |||
| // pointer to the global function for cleaning up instances on thread | |||
| // destruction. | |||
| void SetCurrentThreadIdentity(ThreadIdentity* identity, ThreadIdentityReclaimerFunction reclaimer); | |||
| // Removes the currently associated ThreadIdentity from the running thread. | |||
| // This must be called from inside the ThreadIdentityReclaimerFunction, and only | |||
| // from that function. | |||
| void ClearCurrentThreadIdentity(); | |||
| // May be chosen at compile time via: -DABSL_FORCE_THREAD_IDENTITY_MODE=<mode | |||
| // index> | |||
| #ifdef ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC | |||
| #error ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC cannot be directly set | |||
| #else | |||
| #define ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 0 | |||
| #endif | |||
| #ifdef ABSL_THREAD_IDENTITY_MODE_USE_TLS | |||
| #error ABSL_THREAD_IDENTITY_MODE_USE_TLS cannot be directly set | |||
| #else | |||
| #define ABSL_THREAD_IDENTITY_MODE_USE_TLS 1 | |||
| #endif | |||
| #ifdef ABSL_THREAD_IDENTITY_MODE_USE_CPP11 | |||
| #error ABSL_THREAD_IDENTITY_MODE_USE_CPP11 cannot be directly set | |||
| #else | |||
| #define ABSL_THREAD_IDENTITY_MODE_USE_CPP11 2 | |||
| #endif | |||
| #ifdef ABSL_THREAD_IDENTITY_MODE | |||
| #error ABSL_THREAD_IDENTITY_MODE cannot be directly set | |||
| #elif defined(ABSL_FORCE_THREAD_IDENTITY_MODE) | |||
| #define ABSL_THREAD_IDENTITY_MODE ABSL_FORCE_THREAD_IDENTITY_MODE | |||
| #elif defined(_WIN32) && !defined(__MINGW32__) | |||
| #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 | |||
| #elif defined(__APPLE__) && defined(ABSL_HAVE_THREAD_LOCAL) | |||
| #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_CPP11 | |||
| #elif ABSL_PER_THREAD_TLS && defined(__GOOGLE_GRTE_VERSION__) && \ | |||
| (__GOOGLE_GRTE_VERSION__ >= 20140228L) | |||
| // Support for async-safe TLS was specifically added in GRTEv4. It's not | |||
| // present in the upstream eglibc. | |||
| // Note: Current default for production systems. | |||
| #define ABSL_THREAD_IDENTITY_MODE ABSL_THREAD_IDENTITY_MODE_USE_TLS | |||
| #else | |||
| #define ABSL_THREAD_IDENTITY_MODE \ | |||
| ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC | |||
| #endif | |||
| #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ | |||
| ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 | |||
| #if ABSL_PER_THREAD_TLS | |||
| ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* | |||
| thread_identity_ptr; | |||
| #elif defined(ABSL_HAVE_THREAD_LOCAL) | |||
| ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; | |||
| #else | |||
| #error Thread-local storage not detected on this platform | |||
| #endif | |||
| // thread_local variables cannot be in headers exposed by DLLs or in certain | |||
| // build configurations on Apple platforms. However, it is important for | |||
| // performance reasons in general that `CurrentThreadIdentityIfPresent` be | |||
| // inlined. In the other cases we opt to have the function not be inlined. Note | |||
| // that `CurrentThreadIdentityIfPresent` is declared above so we can exclude | |||
| // this entire inline definition. | |||
| #if !defined(__APPLE__) && !defined(ABSL_BUILD_DLL) && \ | |||
| !defined(ABSL_CONSUME_DLL) | |||
| #define ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 1 | |||
| #endif | |||
| #ifdef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT | |||
| inline ThreadIdentity* CurrentThreadIdentityIfPresent() | |||
| { | |||
| return thread_identity_ptr; | |||
| } | |||
| #endif | |||
| #elif ABSL_THREAD_IDENTITY_MODE != \ | |||
| ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC | |||
| #error Unknown ABSL_THREAD_IDENTITY_MODE | |||
| #endif | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_THREAD_IDENTITY_H_ | |||
| @@ -0,0 +1,77 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ | |||
| #define ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ | |||
| #include <string> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Helper functions that allow throwing exceptions consistently from anywhere. | |||
| // The main use case is for header-based libraries (eg templates), as they will | |||
| // be built by many different targets with their own compiler options. | |||
| // In particular, this will allow a safe way to throw exceptions even if the | |||
| // caller is compiled with -fno-exceptions. This is intended for implementing | |||
| // things like map<>::at(), which the standard documents as throwing an | |||
| // exception on error. | |||
| // | |||
| // Using other techniques like #if tricks could lead to ODR violations. | |||
| // | |||
| // You shouldn't use it unless you're writing code that you know will be built | |||
| // both with and without exceptions and you need to conform to an interface | |||
| // that uses exceptions. | |||
| [[noreturn]] void ThrowStdLogicError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdLogicError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdInvalidArgument(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdInvalidArgument(const char* what_arg); | |||
| [[noreturn]] void ThrowStdDomainError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdDomainError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdLengthError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdLengthError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdOutOfRange(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdOutOfRange(const char* what_arg); | |||
| [[noreturn]] void ThrowStdRuntimeError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdRuntimeError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdRangeError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdRangeError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdOverflowError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdOverflowError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdUnderflowError(const std::string& what_arg); | |||
| [[noreturn]] void ThrowStdUnderflowError(const char* what_arg); | |||
| [[noreturn]] void ThrowStdBadFunctionCall(); | |||
| [[noreturn]] void ThrowStdBadAlloc(); | |||
| // ThrowStdBadArrayNewLength() cannot be consistently supported because | |||
| // std::bad_array_new_length is missing in libstdc++ until 4.9.0. | |||
| // https://gcc.gnu.org/onlinedocs/gcc-4.8.3/libstdc++/api/a01379_source.html | |||
| // https://gcc.gnu.org/onlinedocs/gcc-4.9.0/libstdc++/api/a01327_source.html | |||
| // libcxx (as of 3.2) and msvc (as of 2015) both have it. | |||
| // [[noreturn]] void ThrowStdBadArrayNewLength(); | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_INTERNAL_THROW_DELEGATE_H_ | |||
| @@ -0,0 +1,68 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This file is intended solely for spinlock.h. | |||
| // It provides ThreadSanitizer annotations for custom mutexes. | |||
| // See <sanitizer/tsan_interface.h> for meaning of these annotations. | |||
| #ifndef ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ | |||
| #define ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ | |||
| #include "absl/base/config.h" | |||
| // ABSL_INTERNAL_HAVE_TSAN_INTERFACE | |||
| // Macro intended only for internal use. | |||
| // | |||
| // Checks whether LLVM Thread Sanitizer interfaces are available. | |||
| // First made available in LLVM 5.0 (Sep 2017). | |||
| #ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE | |||
| #error "ABSL_INTERNAL_HAVE_TSAN_INTERFACE cannot be directly set." | |||
| #endif | |||
| #if defined(ABSL_HAVE_THREAD_SANITIZER) && defined(__has_include) | |||
| #if __has_include(<sanitizer/tsan_interface.h>) | |||
| #define ABSL_INTERNAL_HAVE_TSAN_INTERFACE 1 | |||
| #endif | |||
| #endif | |||
| #ifdef ABSL_INTERNAL_HAVE_TSAN_INTERFACE | |||
| #include <sanitizer/tsan_interface.h> | |||
| #define ABSL_TSAN_MUTEX_CREATE __tsan_mutex_create | |||
| #define ABSL_TSAN_MUTEX_DESTROY __tsan_mutex_destroy | |||
| #define ABSL_TSAN_MUTEX_PRE_LOCK __tsan_mutex_pre_lock | |||
| #define ABSL_TSAN_MUTEX_POST_LOCK __tsan_mutex_post_lock | |||
| #define ABSL_TSAN_MUTEX_PRE_UNLOCK __tsan_mutex_pre_unlock | |||
| #define ABSL_TSAN_MUTEX_POST_UNLOCK __tsan_mutex_post_unlock | |||
| #define ABSL_TSAN_MUTEX_PRE_SIGNAL __tsan_mutex_pre_signal | |||
| #define ABSL_TSAN_MUTEX_POST_SIGNAL __tsan_mutex_post_signal | |||
| #define ABSL_TSAN_MUTEX_PRE_DIVERT __tsan_mutex_pre_divert | |||
| #define ABSL_TSAN_MUTEX_POST_DIVERT __tsan_mutex_post_divert | |||
| #else | |||
| #define ABSL_TSAN_MUTEX_CREATE(...) | |||
| #define ABSL_TSAN_MUTEX_DESTROY(...) | |||
| #define ABSL_TSAN_MUTEX_PRE_LOCK(...) | |||
| #define ABSL_TSAN_MUTEX_POST_LOCK(...) | |||
| #define ABSL_TSAN_MUTEX_PRE_UNLOCK(...) | |||
| #define ABSL_TSAN_MUTEX_POST_UNLOCK(...) | |||
| #define ABSL_TSAN_MUTEX_PRE_SIGNAL(...) | |||
| #define ABSL_TSAN_MUTEX_POST_SIGNAL(...) | |||
| #define ABSL_TSAN_MUTEX_PRE_DIVERT(...) | |||
| #define ABSL_TSAN_MUTEX_POST_DIVERT(...) | |||
| #endif | |||
| #endif // ABSL_BASE_INTERNAL_TSAN_MUTEX_INTERFACE_H_ | |||
| @@ -0,0 +1,96 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| #ifndef ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ | |||
| #define ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ | |||
| #include <string.h> | |||
| #include <cstdint> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| // unaligned APIs | |||
| // Portable handling of unaligned loads, stores, and copies. | |||
| // The unaligned API is C++ only. The declarations use C++ features | |||
| // (namespaces, inline) which are absent or incompatible in C. | |||
| #if defined(__cplusplus) | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| inline uint16_t UnalignedLoad16(const void* p) | |||
| { | |||
| uint16_t t; | |||
| memcpy(&t, p, sizeof t); | |||
| return t; | |||
| } | |||
| inline uint32_t UnalignedLoad32(const void* p) | |||
| { | |||
| uint32_t t; | |||
| memcpy(&t, p, sizeof t); | |||
| return t; | |||
| } | |||
| inline uint64_t UnalignedLoad64(const void* p) | |||
| { | |||
| uint64_t t; | |||
| memcpy(&t, p, sizeof t); | |||
| return t; | |||
| } | |||
| inline void UnalignedStore16(void* p, uint16_t v) | |||
| { | |||
| memcpy(p, &v, sizeof v); | |||
| } | |||
| inline void UnalignedStore32(void* p, uint32_t v) | |||
| { | |||
| memcpy(p, &v, sizeof v); | |||
| } | |||
| inline void UnalignedStore64(void* p, uint64_t v) | |||
| { | |||
| memcpy(p, &v, sizeof v); | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #define ABSL_INTERNAL_UNALIGNED_LOAD16(_p) \ | |||
| (absl::base_internal::UnalignedLoad16(_p)) | |||
| #define ABSL_INTERNAL_UNALIGNED_LOAD32(_p) \ | |||
| (absl::base_internal::UnalignedLoad32(_p)) | |||
| #define ABSL_INTERNAL_UNALIGNED_LOAD64(_p) \ | |||
| (absl::base_internal::UnalignedLoad64(_p)) | |||
| #define ABSL_INTERNAL_UNALIGNED_STORE16(_p, _val) \ | |||
| (absl::base_internal::UnalignedStore16(_p, _val)) | |||
| #define ABSL_INTERNAL_UNALIGNED_STORE32(_p, _val) \ | |||
| (absl::base_internal::UnalignedStore32(_p, _val)) | |||
| #define ABSL_INTERNAL_UNALIGNED_STORE64(_p, _val) \ | |||
| (absl::base_internal::UnalignedStore64(_p, _val)) | |||
| #endif // defined(__cplusplus), end of unaligned API | |||
| #endif // ABSL_BASE_INTERNAL_UNALIGNED_ACCESS_H_ | |||
| @@ -0,0 +1,138 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // UnscaledCycleClock | |||
| // An UnscaledCycleClock yields the value and frequency of a cycle counter | |||
| // that increments at a rate that is approximately constant. | |||
| // This class is for internal use only, you should consider using CycleClock | |||
| // instead. | |||
| // | |||
| // Notes: | |||
| // The cycle counter frequency is not necessarily the core clock frequency. | |||
| // That is, CycleCounter cycles are not necessarily "CPU cycles". | |||
| // | |||
| // An arbitrary offset may have been added to the counter at power on. | |||
| // | |||
| // On some platforms, the rate and offset of the counter may differ | |||
| // slightly when read from different CPUs of a multiprocessor. Usually, | |||
| // we try to ensure that the operating system adjusts values periodically | |||
| // so that values agree approximately. If you need stronger guarantees, | |||
| // consider using alternate interfaces. | |||
| // | |||
| // The CPU is not required to maintain the ordering of a cycle counter read | |||
| // with respect to surrounding instructions. | |||
| #ifndef ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ | |||
| #define ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ | |||
| #include <cstdint> | |||
| #if defined(__APPLE__) | |||
| #include <TargetConditionals.h> | |||
| #endif | |||
| #include "absl/base/port.h" | |||
| // The following platforms have an implementation of a hardware counter. | |||
| #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ | |||
| defined(__powerpc__) || defined(__ppc__) || defined(__riscv) || \ | |||
| defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) | |||
| #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 1 | |||
| #else | |||
| #define ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION 0 | |||
| #endif | |||
| // The following platforms often disable access to the hardware | |||
| // counter (through a sandbox) even if the underlying hardware has a | |||
| // usable counter. The CycleTimer interface also requires a *scaled* | |||
| // CycleClock that runs at atleast 1 MHz. We've found some Android | |||
| // ARM64 devices where this is not the case, so we disable it by | |||
| // default on Android ARM64. | |||
| #if defined(__native_client__) || (defined(__APPLE__)) || \ | |||
| (defined(__ANDROID__) && defined(__aarch64__)) | |||
| #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 0 | |||
| #else | |||
| #define ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT 1 | |||
| #endif | |||
| // UnscaledCycleClock is an optional internal feature. | |||
| // Use "#if ABSL_USE_UNSCALED_CYCLECLOCK" to test for its presence. | |||
| // Can be overridden at compile-time via -DABSL_USE_UNSCALED_CYCLECLOCK=0|1 | |||
| #if !defined(ABSL_USE_UNSCALED_CYCLECLOCK) | |||
| #define ABSL_USE_UNSCALED_CYCLECLOCK \ | |||
| (ABSL_HAVE_UNSCALED_CYCLECLOCK_IMPLEMENTATION && \ | |||
| ABSL_USE_UNSCALED_CYCLECLOCK_DEFAULT) | |||
| #endif | |||
| #if ABSL_USE_UNSCALED_CYCLECLOCK | |||
| // This macro can be used to test if UnscaledCycleClock::Frequency() | |||
| // is NominalCPUFrequency() on a particular platform. | |||
| #if (defined(__i386__) || defined(__x86_64__) || defined(__riscv) || defined(_M_IX86) || defined(_M_X64)) | |||
| #define ABSL_INTERNAL_UNSCALED_CYCLECLOCK_FREQUENCY_IS_CPU_FREQUENCY | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace time_internal | |||
| { | |||
| class UnscaledCycleClockWrapperForGetCurrentTime; | |||
| } // namespace time_internal | |||
| namespace base_internal | |||
| { | |||
| class CycleClock; | |||
| class UnscaledCycleClockWrapperForInitializeFrequency; | |||
| class UnscaledCycleClock | |||
| { | |||
| private: | |||
| UnscaledCycleClock() = delete; | |||
| // Return the value of a cycle counter that counts at a rate that is | |||
| // approximately constant. | |||
| static int64_t Now(); | |||
| // Return the how much UnscaledCycleClock::Now() increases per second. | |||
| // This is not necessarily the core CPU clock frequency. | |||
| // It may be the nominal value report by the kernel, rather than a measured | |||
| // value. | |||
| static double Frequency(); | |||
| // Allowed users | |||
| friend class base_internal::CycleClock; | |||
| friend class time_internal::UnscaledCycleClockWrapperForGetCurrentTime; | |||
| friend class base_internal::UnscaledCycleClockWrapperForInitializeFrequency; | |||
| }; | |||
| #if defined(__x86_64__) | |||
| inline int64_t UnscaledCycleClock::Now() | |||
| { | |||
| uint64_t low, high; | |||
| __asm__ volatile("rdtsc" | |||
| : "=a"(low), "=d"(high)); | |||
| return (high << 32) | low; | |||
| } | |||
| #endif | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_USE_UNSCALED_CYCLECLOCK | |||
| #endif // ABSL_BASE_INTERNAL_UNSCALEDCYCLECLOCK_H_ | |||
| @@ -0,0 +1,177 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_BASE_LOG_SEVERITY_H_ | |||
| #define ABSL_BASE_LOG_SEVERITY_H_ | |||
| #include <array> | |||
| #include <ostream> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| // absl::LogSeverity | |||
| // | |||
| // Four severity levels are defined. Logging APIs should terminate the program | |||
| // when a message is logged at severity `kFatal`; the other levels have no | |||
| // special semantics. | |||
| // | |||
| // Values other than the four defined levels (e.g. produced by `static_cast`) | |||
| // are valid, but their semantics when passed to a function, macro, or flag | |||
| // depend on the function, macro, or flag. The usual behavior is to normalize | |||
| // such values to a defined severity level, however in some cases values other | |||
| // than the defined levels are useful for comparison. | |||
| // | |||
| // Example: | |||
| // | |||
| // // Effectively disables all logging: | |||
| // SetMinLogLevel(static_cast<absl::LogSeverity>(100)); | |||
| // | |||
| // Abseil flags may be defined with type `LogSeverity`. Dependency layering | |||
| // constraints require that the `AbslParseFlag()` overload be declared and | |||
| // defined in the flags library itself rather than here. The `AbslUnparseFlag()` | |||
| // overload is defined there as well for consistency. | |||
| // | |||
| // absl::LogSeverity Flag String Representation | |||
| // | |||
| // An `absl::LogSeverity` has a string representation used for parsing | |||
| // command-line flags based on the enumerator name (e.g. `kFatal`) or | |||
| // its unprefixed name (without the `k`) in any case-insensitive form. (E.g. | |||
| // "FATAL", "fatal" or "Fatal" are all valid.) Unparsing such flags produces an | |||
| // unprefixed string representation in all caps (e.g. "FATAL") or an integer. | |||
| // | |||
| // Additionally, the parser accepts arbitrary integers (as if the type were | |||
| // `int`). | |||
| // | |||
| // Examples: | |||
| // | |||
| // --my_log_level=kInfo | |||
| // --my_log_level=INFO | |||
| // --my_log_level=info | |||
| // --my_log_level=0 | |||
| // | |||
| // Unparsing a flag produces the same result as `absl::LogSeverityName()` for | |||
| // the standard levels and a base-ten integer otherwise. | |||
| enum class LogSeverity : int | |||
| { | |||
| kInfo = 0, | |||
| kWarning = 1, | |||
| kError = 2, | |||
| kFatal = 3, | |||
| }; | |||
| // LogSeverities() | |||
| // | |||
| // Returns an iterable of all standard `absl::LogSeverity` values, ordered from | |||
| // least to most severe. | |||
| constexpr std::array<absl::LogSeverity, 4> LogSeverities() | |||
| { | |||
| return {{absl::LogSeverity::kInfo, absl::LogSeverity::kWarning, absl::LogSeverity::kError, absl::LogSeverity::kFatal}}; | |||
| } | |||
| // LogSeverityName() | |||
| // | |||
| // Returns the all-caps string representation (e.g. "INFO") of the specified | |||
| // severity level if it is one of the standard levels and "UNKNOWN" otherwise. | |||
| constexpr const char* LogSeverityName(absl::LogSeverity s) | |||
| { | |||
| return s == absl::LogSeverity::kInfo ? "INFO" : s == absl::LogSeverity::kWarning ? "WARNING" : | |||
| s == absl::LogSeverity::kError ? "ERROR" : | |||
| s == absl::LogSeverity::kFatal ? "FATAL" : | |||
| "UNKNOWN"; | |||
| } | |||
| // NormalizeLogSeverity() | |||
| // | |||
| // Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` | |||
| // normalize to `kError` (**NOT** `kFatal`). | |||
| constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) | |||
| { | |||
| return s < absl::LogSeverity::kInfo ? absl::LogSeverity::kInfo : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : | |||
| s; | |||
| } | |||
| constexpr absl::LogSeverity NormalizeLogSeverity(int s) | |||
| { | |||
| return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s)); | |||
| } | |||
| // operator<< | |||
| // | |||
| // The exact representation of a streamed `absl::LogSeverity` is deliberately | |||
| // unspecified; do not rely on it. | |||
| std::ostream& operator<<(std::ostream& os, absl::LogSeverity s); | |||
| // Enums representing a lower bound for LogSeverity. APIs that only operate on | |||
| // messages of at least a certain level (for example, `SetMinLogLevel()`) use | |||
| // this type to specify that level. absl::LogSeverityAtLeast::kInfinity is | |||
| // a level above all threshold levels and therefore no log message will | |||
| // ever meet this threshold. | |||
| enum class LogSeverityAtLeast : int | |||
| { | |||
| kInfo = static_cast<int>(absl::LogSeverity::kInfo), | |||
| kWarning = static_cast<int>(absl::LogSeverity::kWarning), | |||
| kError = static_cast<int>(absl::LogSeverity::kError), | |||
| kFatal = static_cast<int>(absl::LogSeverity::kFatal), | |||
| kInfinity = 1000, | |||
| }; | |||
| std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtLeast s); | |||
| // Enums representing an upper bound for LogSeverity. APIs that only operate on | |||
| // messages of at most a certain level (for example, buffer all messages at or | |||
| // below a certain level) use this type to specify that level. | |||
| // absl::LogSeverityAtMost::kNegativeInfinity is a level below all threshold | |||
| // levels and therefore will exclude all log messages. | |||
| enum class LogSeverityAtMost : int | |||
| { | |||
| kNegativeInfinity = -1000, | |||
| kInfo = static_cast<int>(absl::LogSeverity::kInfo), | |||
| kWarning = static_cast<int>(absl::LogSeverity::kWarning), | |||
| kError = static_cast<int>(absl::LogSeverity::kError), | |||
| kFatal = static_cast<int>(absl::LogSeverity::kFatal), | |||
| }; | |||
| std::ostream& operator<<(std::ostream& os, absl::LogSeverityAtMost s); | |||
| #define COMPOP(op1, op2, T) \ | |||
| constexpr bool operator op1(absl::T lhs, absl::LogSeverity rhs) \ | |||
| { \ | |||
| return static_cast<absl::LogSeverity>(lhs) op1 rhs; \ | |||
| } \ | |||
| constexpr bool operator op2(absl::LogSeverity lhs, absl::T rhs) \ | |||
| { \ | |||
| return lhs op2 static_cast<absl::LogSeverity>(rhs); \ | |||
| } | |||
| // Comparisons between `LogSeverity` and `LogSeverityAtLeast`/ | |||
| // `LogSeverityAtMost` are only supported in one direction. | |||
| // Valid checks are: | |||
| // LogSeverity >= LogSeverityAtLeast | |||
| // LogSeverity < LogSeverityAtLeast | |||
| // LogSeverity <= LogSeverityAtMost | |||
| // LogSeverity > LogSeverityAtMost | |||
| COMPOP(>, <, LogSeverityAtLeast) | |||
| COMPOP(<=, >=, LogSeverityAtLeast) | |||
| COMPOP(<, >, LogSeverityAtMost) | |||
| COMPOP(>=, <=, LogSeverityAtMost) | |||
| #undef COMPOP | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_LOG_SEVERITY_H_ | |||
| @@ -0,0 +1,165 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: macros.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines the set of language macros used within Abseil code. | |||
| // For the set of macros used to determine supported compilers and platforms, | |||
| // see absl/base/config.h instead. | |||
| // | |||
| // This code is compiled directly on many platforms, including client | |||
| // platforms like Windows, Mac, and embedded systems. Before making | |||
| // any changes here, make sure that you're not breaking any platforms. | |||
| #ifndef ABSL_BASE_MACROS_H_ | |||
| #define ABSL_BASE_MACROS_H_ | |||
| #include <cassert> | |||
| #include <cstddef> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/base/port.h" | |||
| // ABSL_ARRAYSIZE() | |||
| // | |||
| // Returns the number of elements in an array as a compile-time constant, which | |||
| // can be used in defining new arrays. If you use this macro on a pointer by | |||
| // mistake, you will get a compile-time error. | |||
| #define ABSL_ARRAYSIZE(array) \ | |||
| (sizeof(::absl::macros_internal::ArraySizeHelper(array))) | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace macros_internal | |||
| { | |||
| // Note: this internal template function declaration is used by ABSL_ARRAYSIZE. | |||
| // The function doesn't need a definition, as we only use its type. | |||
| template<typename T, size_t N> | |||
| auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; | |||
| } // namespace macros_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| // ABSL_BAD_CALL_IF() | |||
| // | |||
| // Used on a function overload to trap bad calls: any call that matches the | |||
| // overload will cause a compile-time error. This macro uses a clang-specific | |||
| // "enable_if" attribute, as described at | |||
| // https://clang.llvm.org/docs/AttributeReference.html#enable-if | |||
| // | |||
| // Overloads which use this macro should be bracketed by | |||
| // `#ifdef ABSL_BAD_CALL_IF`. | |||
| // | |||
| // Example: | |||
| // | |||
| // int isdigit(int c); | |||
| // #ifdef ABSL_BAD_CALL_IF | |||
| // int isdigit(int c) | |||
| // ABSL_BAD_CALL_IF(c <= -1 || c > 255, | |||
| // "'c' must have the value of an unsigned char or EOF"); | |||
| // #endif // ABSL_BAD_CALL_IF | |||
| #if ABSL_HAVE_ATTRIBUTE(enable_if) | |||
| #define ABSL_BAD_CALL_IF(expr, msg) \ | |||
| __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) | |||
| #endif | |||
| // ABSL_ASSERT() | |||
| // | |||
| // In C++11, `assert` can't be used portably within constexpr functions. | |||
| // ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr | |||
| // functions. Example: | |||
| // | |||
| // constexpr double Divide(double a, double b) { | |||
| // return ABSL_ASSERT(b != 0), a / b; | |||
| // } | |||
| // | |||
| // This macro is inspired by | |||
| // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ | |||
| #if defined(NDEBUG) | |||
| #define ABSL_ASSERT(expr) \ | |||
| (false ? static_cast<void>(expr) : static_cast<void>(0)) | |||
| #else | |||
| #define ABSL_ASSERT(expr) \ | |||
| (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) : [] { assert(false && #expr); }()) // NOLINT | |||
| #endif | |||
| // `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` | |||
| // aborts the program in release mode (when NDEBUG is defined). The | |||
| // implementation should abort the program as quickly as possible and ideally it | |||
| // should not be possible to ignore the abort request. | |||
| #if (ABSL_HAVE_BUILTIN(__builtin_trap) && ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ | |||
| (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_INTERNAL_HARDENING_ABORT() \ | |||
| do \ | |||
| { \ | |||
| __builtin_trap(); \ | |||
| __builtin_unreachable(); \ | |||
| } while (false) | |||
| #else | |||
| #define ABSL_INTERNAL_HARDENING_ABORT() abort() | |||
| #endif | |||
| // ABSL_HARDENING_ASSERT() | |||
| // | |||
| // `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement | |||
| // runtime assertions that should be enabled in hardened builds even when | |||
| // `NDEBUG` is defined. | |||
| // | |||
| // When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to | |||
| // `ABSL_ASSERT()`. | |||
| // | |||
| // See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on | |||
| // hardened mode. | |||
| #if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) | |||
| #define ABSL_HARDENING_ASSERT(expr) \ | |||
| (ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) | |||
| #else | |||
| #define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) | |||
| #endif | |||
| #ifdef ABSL_HAVE_EXCEPTIONS | |||
| #define ABSL_INTERNAL_TRY try | |||
| #define ABSL_INTERNAL_CATCH_ANY catch (...) | |||
| #define ABSL_INTERNAL_RETHROW \ | |||
| do \ | |||
| { \ | |||
| throw; \ | |||
| } while (false) | |||
| #else // ABSL_HAVE_EXCEPTIONS | |||
| #define ABSL_INTERNAL_TRY if (true) | |||
| #define ABSL_INTERNAL_CATCH_ANY else if (false) | |||
| #define ABSL_INTERNAL_RETHROW \ | |||
| do \ | |||
| { \ | |||
| } while (false) | |||
| #endif // ABSL_HAVE_EXCEPTIONS | |||
| // `ABSL_INTERNAL_UNREACHABLE` is an unreachable statement. A program which | |||
| // reaches one has undefined behavior, and the compiler may optimize | |||
| // accordingly. | |||
| #if defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) | |||
| #define ABSL_INTERNAL_UNREACHABLE __builtin_unreachable() | |||
| #elif defined(_MSC_VER) | |||
| #define ABSL_INTERNAL_UNREACHABLE __assume(0) | |||
| #else | |||
| #define ABSL_INTERNAL_UNREACHABLE | |||
| #endif | |||
| #endif // ABSL_BASE_MACROS_H_ | |||
| @@ -0,0 +1,263 @@ | |||
| // | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: optimization.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines portable macros for performance optimization. | |||
| #ifndef ABSL_BASE_OPTIMIZATION_H_ | |||
| #define ABSL_BASE_OPTIMIZATION_H_ | |||
| #include <assert.h> | |||
| #include "absl/base/config.h" | |||
| // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION | |||
| // | |||
| // Instructs the compiler to avoid optimizing tail-call recursion. This macro is | |||
| // useful when you wish to preserve the existing function order within a stack | |||
| // trace for logging, debugging, or profiling purposes. | |||
| // | |||
| // Example: | |||
| // | |||
| // int f() { | |||
| // int result = g(); | |||
| // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); | |||
| // return result; | |||
| // } | |||
| #if defined(__pnacl__) | |||
| #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() \ | |||
| if (volatile int x = 0) \ | |||
| { \ | |||
| (void)x; \ | |||
| } | |||
| #elif defined(__clang__) | |||
| // Clang will not tail call given inline volatile assembly. | |||
| #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") | |||
| #elif defined(__GNUC__) | |||
| // GCC will not tail call given inline volatile assembly. | |||
| #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") | |||
| #elif defined(_MSC_VER) | |||
| #include <intrin.h> | |||
| // The __nop() intrinsic blocks the optimisation. | |||
| #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop() | |||
| #else | |||
| #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() \ | |||
| if (volatile int x = 0) \ | |||
| { \ | |||
| (void)x; \ | |||
| } | |||
| #endif | |||
| // ABSL_CACHELINE_SIZE | |||
| // | |||
| // Explicitly defines the size of the L1 cache for purposes of alignment. | |||
| // Setting the cacheline size allows you to specify that certain objects be | |||
| // aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations. | |||
| // (See below.) | |||
| // | |||
| // NOTE: this macro should be replaced with the following C++17 features, when | |||
| // those are generally available: | |||
| // | |||
| // * `std::hardware_constructive_interference_size` | |||
| // * `std::hardware_destructive_interference_size` | |||
| // | |||
| // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html | |||
| // for more information. | |||
| #if defined(__GNUC__) | |||
| // Cache line alignment | |||
| #if defined(__i386__) || defined(__x86_64__) | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #elif defined(__powerpc64__) | |||
| #define ABSL_CACHELINE_SIZE 128 | |||
| #elif defined(__aarch64__) | |||
| // We would need to read special register ctr_el0 to find out L1 dcache size. | |||
| // This value is a good estimate based on a real aarch64 machine. | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #elif defined(__arm__) | |||
| // Cache line sizes for ARM: These values are not strictly correct since | |||
| // cache line sizes depend on implementations, not architectures. There | |||
| // are even implementations with cache line sizes configurable at boot | |||
| // time. | |||
| #if defined(__ARM_ARCH_5T__) | |||
| #define ABSL_CACHELINE_SIZE 32 | |||
| #elif defined(__ARM_ARCH_7A__) | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #endif | |||
| #endif | |||
| #ifndef ABSL_CACHELINE_SIZE | |||
| // A reasonable default guess. Note that overestimates tend to waste more | |||
| // space, while underestimates tend to waste more time. | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #endif | |||
| // ABSL_CACHELINE_ALIGNED | |||
| // | |||
| // Indicates that the declared object be cache aligned using | |||
| // `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to | |||
| // load a set of related objects in the L1 cache for performance improvements. | |||
| // Cacheline aligning objects properly allows constructive memory sharing and | |||
| // prevents destructive (or "false") memory sharing. | |||
| // | |||
| // NOTE: callers should replace uses of this macro with `alignas()` using | |||
| // `std::hardware_constructive_interference_size` and/or | |||
| // `std::hardware_destructive_interference_size` when C++17 becomes available to | |||
| // them. | |||
| // | |||
| // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html | |||
| // for more information. | |||
| // | |||
| // On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` | |||
| // or `__declspec` attribute. For compilers where this is not known to work, | |||
| // the macro expands to nothing. | |||
| // | |||
| // No further guarantees are made here. The result of applying the macro | |||
| // to variables and types is always implementation-defined. | |||
| // | |||
| // WARNING: It is easy to use this attribute incorrectly, even to the point | |||
| // of causing bugs that are difficult to diagnose, crash, etc. It does not | |||
| // of itself guarantee that objects are aligned to a cache line. | |||
| // | |||
| // NOTE: Some compilers are picky about the locations of annotations such as | |||
| // this attribute, so prefer to put it at the beginning of your declaration. | |||
| // For example, | |||
| // | |||
| // ABSL_CACHELINE_ALIGNED static Foo* foo = ... | |||
| // | |||
| // class ABSL_CACHELINE_ALIGNED Bar { ... | |||
| // | |||
| // Recommendations: | |||
| // | |||
| // 1) Consult compiler documentation; this comment is not kept in sync as | |||
| // toolchains evolve. | |||
| // 2) Verify your use has the intended effect. This often requires inspecting | |||
| // the generated machine code. | |||
| // 3) Prefer applying this attribute to individual variables. Avoid | |||
| // applying it to types. This tends to localize the effect. | |||
| #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) | |||
| #elif defined(_MSC_VER) | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) | |||
| #else | |||
| #define ABSL_CACHELINE_SIZE 64 | |||
| #define ABSL_CACHELINE_ALIGNED | |||
| #endif | |||
| // ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE | |||
| // | |||
| // Enables the compiler to prioritize compilation using static analysis for | |||
| // likely paths within a boolean branch. | |||
| // | |||
| // Example: | |||
| // | |||
| // if (ABSL_PREDICT_TRUE(expression)) { | |||
| // return result; // Faster if more likely | |||
| // } else { | |||
| // return 0; | |||
| // } | |||
| // | |||
| // Compilers can use the information that a certain branch is not likely to be | |||
| // taken (for instance, a CHECK failure) to optimize for the common case in | |||
| // the absence of better information (ie. compiling gcc with `-fprofile-arcs`). | |||
| // | |||
| // Recommendation: Modern CPUs dynamically predict branch execution paths, | |||
| // typically with accuracy greater than 97%. As a result, annotating every | |||
| // branch in a codebase is likely counterproductive; however, annotating | |||
| // specific branches that are both hot and consistently mispredicted is likely | |||
| // to yield performance improvements. | |||
| #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ | |||
| (defined(__GNUC__) && !defined(__clang__)) | |||
| #define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false)) | |||
| #define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) | |||
| #else | |||
| #define ABSL_PREDICT_FALSE(x) (x) | |||
| #define ABSL_PREDICT_TRUE(x) (x) | |||
| #endif | |||
| // ABSL_ASSUME(cond) | |||
| // | |||
| // Informs the compiler that a condition is always true and that it can assume | |||
| // it to be true for optimization purposes. | |||
| // | |||
| // WARNING: If the condition is false, the program can produce undefined and | |||
| // potentially dangerous behavior. | |||
| // | |||
| // In !NDEBUG mode, the condition is checked with an assert(). | |||
| // | |||
| // NOTE: The expression must not have side effects, as it may only be evaluated | |||
| // in some compilation modes and not others. Some compilers may issue a warning | |||
| // if the compiler cannot prove the expression has no side effects. For example, | |||
| // the expression should not use a function call since the compiler cannot prove | |||
| // that a function call does not have side effects. | |||
| // | |||
| // Example: | |||
| // | |||
| // int x = ...; | |||
| // ABSL_ASSUME(x >= 0); | |||
| // // The compiler can optimize the division to a simple right shift using the | |||
| // // assumption specified above. | |||
| // int y = x / 16; | |||
| // | |||
| #if !defined(NDEBUG) | |||
| #define ABSL_ASSUME(cond) assert(cond) | |||
| #elif ABSL_HAVE_BUILTIN(__builtin_assume) | |||
| #define ABSL_ASSUME(cond) __builtin_assume(cond) | |||
| #elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) | |||
| #define ABSL_ASSUME(cond) \ | |||
| do \ | |||
| { \ | |||
| if (!(cond)) \ | |||
| __builtin_unreachable(); \ | |||
| } while (0) | |||
| #elif defined(_MSC_VER) | |||
| #define ABSL_ASSUME(cond) __assume(cond) | |||
| #else | |||
| #define ABSL_ASSUME(cond) \ | |||
| do \ | |||
| { \ | |||
| static_cast<void>(false && (cond)); \ | |||
| } while (0) | |||
| #endif | |||
| // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) | |||
| // This macro forces small unique name on a static file level symbols like | |||
| // static local variables or static functions. This is intended to be used in | |||
| // macro definitions to optimize the cost of generated code. Do NOT use it on | |||
| // symbols exported from translation unit since it may cause a link time | |||
| // conflict. | |||
| // | |||
| // Example: | |||
| // | |||
| // #define MY_MACRO(txt) | |||
| // namespace { | |||
| // char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt; | |||
| // const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); | |||
| // const char* VeryVeryLongFuncName() { return txt; } | |||
| // } | |||
| // | |||
| #if defined(__GNUC__) | |||
| #define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x | |||
| #define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) | |||
| #define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ | |||
| asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__)) | |||
| #else | |||
| #define ABSL_INTERNAL_UNIQUE_SMALL_NAME() | |||
| #endif | |||
| #endif // ABSL_BASE_OPTIMIZATION_H_ | |||
| @@ -0,0 +1,235 @@ | |||
| // Copyright 2019 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: options.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This file contains Abseil configuration options for setting specific | |||
| // implementations instead of letting Abseil determine which implementation to | |||
| // use at compile-time. Setting these options may be useful for package or build | |||
| // managers who wish to guarantee ABI stability within binary builds (which are | |||
| // otherwise difficult to enforce). | |||
| // | |||
| // *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that | |||
| // maintainers of package managers who wish to package Abseil read and | |||
| // understand this file! *** | |||
| // | |||
| // Abseil contains a number of possible configuration endpoints, based on | |||
| // parameters such as the detected platform, language version, or command-line | |||
| // flags used to invoke the underlying binary. As is the case with all | |||
| // libraries, binaries which contain Abseil code must ensure that separate | |||
| // packages use the same compiled copy of Abseil to avoid a diamond dependency | |||
| // problem, which can occur if two packages built with different Abseil | |||
| // configuration settings are linked together. Diamond dependency problems in | |||
| // C++ may manifest as violations to the One Definition Rule (ODR) (resulting in | |||
| // linker errors), or undefined behavior (resulting in crashes). | |||
| // | |||
| // Diamond dependency problems can be avoided if all packages utilize the same | |||
| // exact version of Abseil. Building from source code with the same compilation | |||
| // parameters is the easiest way to avoid such dependency problems. However, for | |||
| // package managers who cannot control such compilation parameters, we are | |||
| // providing the file to allow you to inject ABI (Application Binary Interface) | |||
| // stability across builds. Settings options in this file will neither change | |||
| // API nor ABI, providing a stable copy of Abseil between packages. | |||
| // | |||
| // Care must be taken to keep options within these configurations isolated | |||
| // from any other dynamic settings, such as command-line flags which could alter | |||
| // these options. This file is provided specifically to help build and package | |||
| // managers provide a stable copy of Abseil within their libraries and binaries; | |||
| // other developers should not have need to alter the contents of this file. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // Usage | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // For any particular package release, set the appropriate definitions within | |||
| // this file to whatever value makes the most sense for your package(s). Note | |||
| // that, by default, most of these options, at the moment, affect the | |||
| // implementation of types; future options may affect other implementation | |||
| // details. | |||
| // | |||
| // NOTE: the defaults within this file all assume that Abseil can select the | |||
| // proper Abseil implementation at compile-time, which will not be sufficient | |||
| // to guarantee ABI stability to package managers. | |||
| #ifndef ABSL_BASE_OPTIONS_H_ | |||
| #define ABSL_BASE_OPTIONS_H_ | |||
| // Include a standard library header to allow configuration based on the | |||
| // standard library in use. | |||
| #ifdef __cplusplus | |||
| #include <ciso646> | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Type Compatibility Options | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // ABSL_OPTION_USE_STD_ANY | |||
| // | |||
| // This option controls whether absl::any is implemented as an alias to | |||
| // std::any, or as an independent implementation. | |||
| // | |||
| // A value of 0 means to use Abseil's implementation. This requires only C++11 | |||
| // support, and is expected to work on every toolchain we support. | |||
| // | |||
| // A value of 1 means to use an alias to std::any. This requires that all code | |||
| // using Abseil is built in C++17 mode or later. | |||
| // | |||
| // A value of 2 means to detect the C++ version being used to compile Abseil, | |||
| // and use an alias only if a working std::any is available. This option is | |||
| // useful when you are building your entire program, including all of its | |||
| // dependencies, from source. It should not be used otherwise -- for example, | |||
| // if you are distributing Abseil in a binary package manager -- since in | |||
| // mode 2, absl::any will name a different type, with a different mangled name | |||
| // and binary layout, depending on the compiler flags passed by the end user. | |||
| // For more info, see https://abseil.io/about/design/dropin-types. | |||
| // | |||
| // User code should not inspect this macro. To check in the preprocessor if | |||
| // absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. | |||
| #define ABSL_OPTION_USE_STD_ANY 0 | |||
| // ABSL_OPTION_USE_STD_OPTIONAL | |||
| // | |||
| // This option controls whether absl::optional is implemented as an alias to | |||
| // std::optional, or as an independent implementation. | |||
| // | |||
| // A value of 0 means to use Abseil's implementation. This requires only C++11 | |||
| // support, and is expected to work on every toolchain we support. | |||
| // | |||
| // A value of 1 means to use an alias to std::optional. This requires that all | |||
| // code using Abseil is built in C++17 mode or later. | |||
| // | |||
| // A value of 2 means to detect the C++ version being used to compile Abseil, | |||
| // and use an alias only if a working std::optional is available. This option | |||
| // is useful when you are building your program from source. It should not be | |||
| // used otherwise -- for example, if you are distributing Abseil in a binary | |||
| // package manager -- since in mode 2, absl::optional will name a different | |||
| // type, with a different mangled name and binary layout, depending on the | |||
| // compiler flags passed by the end user. For more info, see | |||
| // https://abseil.io/about/design/dropin-types. | |||
| // User code should not inspect this macro. To check in the preprocessor if | |||
| // absl::optional is a typedef of std::optional, use the feature macro | |||
| // ABSL_USES_STD_OPTIONAL. | |||
| #define ABSL_OPTION_USE_STD_OPTIONAL 0 | |||
| // ABSL_OPTION_USE_STD_STRING_VIEW | |||
| // | |||
| // This option controls whether absl::string_view is implemented as an alias to | |||
| // std::string_view, or as an independent implementation. | |||
| // | |||
| // A value of 0 means to use Abseil's implementation. This requires only C++11 | |||
| // support, and is expected to work on every toolchain we support. | |||
| // | |||
| // A value of 1 means to use an alias to std::string_view. This requires that | |||
| // all code using Abseil is built in C++17 mode or later. | |||
| // | |||
| // A value of 2 means to detect the C++ version being used to compile Abseil, | |||
| // and use an alias only if a working std::string_view is available. This | |||
| // option is useful when you are building your program from source. It should | |||
| // not be used otherwise -- for example, if you are distributing Abseil in a | |||
| // binary package manager -- since in mode 2, absl::string_view will name a | |||
| // different type, with a different mangled name and binary layout, depending on | |||
| // the compiler flags passed by the end user. For more info, see | |||
| // https://abseil.io/about/design/dropin-types. | |||
| // | |||
| // User code should not inspect this macro. To check in the preprocessor if | |||
| // absl::string_view is a typedef of std::string_view, use the feature macro | |||
| // ABSL_USES_STD_STRING_VIEW. | |||
| #define ABSL_OPTION_USE_STD_STRING_VIEW 0 | |||
| // ABSL_OPTION_USE_STD_VARIANT | |||
| // | |||
| // This option controls whether absl::variant is implemented as an alias to | |||
| // std::variant, or as an independent implementation. | |||
| // | |||
| // A value of 0 means to use Abseil's implementation. This requires only C++11 | |||
| // support, and is expected to work on every toolchain we support. | |||
| // | |||
| // A value of 1 means to use an alias to std::variant. This requires that all | |||
| // code using Abseil is built in C++17 mode or later. | |||
| // | |||
| // A value of 2 means to detect the C++ version being used to compile Abseil, | |||
| // and use an alias only if a working std::variant is available. This option | |||
| // is useful when you are building your program from source. It should not be | |||
| // used otherwise -- for example, if you are distributing Abseil in a binary | |||
| // package manager -- since in mode 2, absl::variant will name a different | |||
| // type, with a different mangled name and binary layout, depending on the | |||
| // compiler flags passed by the end user. For more info, see | |||
| // https://abseil.io/about/design/dropin-types. | |||
| // | |||
| // User code should not inspect this macro. To check in the preprocessor if | |||
| // absl::variant is a typedef of std::variant, use the feature macro | |||
| // ABSL_USES_STD_VARIANT. | |||
| #define ABSL_OPTION_USE_STD_VARIANT 0 | |||
| // ABSL_OPTION_USE_INLINE_NAMESPACE | |||
| // ABSL_OPTION_INLINE_NAMESPACE_NAME | |||
| // | |||
| // These options controls whether all entities in the absl namespace are | |||
| // contained within an inner inline namespace. This does not affect the | |||
| // user-visible API of Abseil, but it changes the mangled names of all symbols. | |||
| // | |||
| // This can be useful as a version tag if you are distributing Abseil in | |||
| // precompiled form. This will prevent a binary library build of Abseil with | |||
| // one inline namespace being used with headers configured with a different | |||
| // inline namespace name. Binary packagers are reminded that Abseil does not | |||
| // guarantee any ABI stability in Abseil, so any update of Abseil or | |||
| // configuration change in such a binary package should be combined with a | |||
| // new, unique value for the inline namespace name. | |||
| // | |||
| // A value of 0 means not to use inline namespaces. | |||
| // | |||
| // A value of 1 means to use an inline namespace with the given name inside | |||
| // namespace absl. If this is set, ABSL_OPTION_INLINE_NAMESPACE_NAME must also | |||
| // be changed to a new, unique identifier name. In particular "head" is not | |||
| // allowed. | |||
| #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 | |||
| #define ABSL_OPTION_INLINE_NAMESPACE_NAME lts_20220623 | |||
| // ABSL_OPTION_HARDENED | |||
| // | |||
| // This option enables a "hardened" build in release mode (in this context, | |||
| // release mode is defined as a build where the `NDEBUG` macro is defined). | |||
| // | |||
| // A value of 0 means that "hardened" mode is not enabled. | |||
| // | |||
| // A value of 1 means that "hardened" mode is enabled. | |||
| // | |||
| // Hardened builds have additional security checks enabled when `NDEBUG` is | |||
| // defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a | |||
| // no-op, as well as disabling other bespoke program consistency checks. By | |||
| // defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in | |||
| // release mode. These checks guard against programming errors that may lead to | |||
| // security vulnerabilities. In release mode, when one of these programming | |||
| // errors is encountered, the program will immediately abort, possibly without | |||
| // any attempt at logging. | |||
| // | |||
| // The checks enabled by this option are not free; they do incur runtime cost. | |||
| // | |||
| // The checks enabled by this option are always active when `NDEBUG` is not | |||
| // defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The | |||
| // checks enabled by this option may abort the program in a different way and | |||
| // log additional information when `NDEBUG` is not defined. | |||
| #define ABSL_OPTION_HARDENED 0 | |||
| #endif // ABSL_BASE_OPTIONS_H_ | |||
| @@ -0,0 +1,111 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: policy_checks.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header enforces a minimum set of policies at build time, such as the | |||
| // supported compiler and library versions. Unsupported configurations are | |||
| // reported with `#error`. This enforcement is best effort, so successfully | |||
| // compiling this header does not guarantee a supported configuration. | |||
| #ifndef ABSL_BASE_POLICY_CHECKS_H_ | |||
| #define ABSL_BASE_POLICY_CHECKS_H_ | |||
| // Included for the __GLIBC_PREREQ macro used below. | |||
| #include <limits.h> | |||
| // Included for the _STLPORT_VERSION macro used below. | |||
| #if defined(__cplusplus) | |||
| #include <cstddef> | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Operating System Check | |||
| // ----------------------------------------------------------------------------- | |||
| #if defined(__CYGWIN__) | |||
| #error "Cygwin is not supported." | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Toolchain Check | |||
| // ----------------------------------------------------------------------------- | |||
| // We support MSVC++ 14.0 update 2 and later. | |||
| // This minimum will go up. | |||
| #if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) | |||
| #error "This package requires Visual Studio 2015 Update 2 or higher." | |||
| #endif | |||
| // We support gcc 4.7 and later. | |||
| // This minimum will go up. | |||
| #if defined(__GNUC__) && !defined(__clang__) | |||
| #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) | |||
| #error "This package requires gcc 4.7 or higher." | |||
| #endif | |||
| #endif | |||
| // We support Apple Xcode clang 4.2.1 (version 421.11.65) and later. | |||
| // This corresponds to Apple Xcode version 4.5. | |||
| // This minimum will go up. | |||
| #if defined(__apple_build_version__) && __apple_build_version__ < 4211165 | |||
| #error "This package requires __apple_build_version__ of 4211165 or higher." | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // C++ Version Check | |||
| // ----------------------------------------------------------------------------- | |||
| // Enforce C++11 as the minimum. Note that Visual Studio has not | |||
| // advanced __cplusplus despite being good enough for our purposes, so | |||
| // so we exempt it from the check. | |||
| #if defined(__cplusplus) && !defined(_MSC_VER) | |||
| #if __cplusplus < 201103L | |||
| #error "C++ versions less than C++11 are not supported." | |||
| #endif | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // Standard Library Check | |||
| // ----------------------------------------------------------------------------- | |||
| #if defined(_STLPORT_VERSION) | |||
| #error "STLPort is not supported." | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // `char` Size Check | |||
| // ----------------------------------------------------------------------------- | |||
| // Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a | |||
| // platform where this is not the case, please provide us with the details about | |||
| // your platform so we can consider relaxing this requirement. | |||
| #if CHAR_BIT != 8 | |||
| #error "Abseil assumes CHAR_BIT == 8." | |||
| #endif | |||
| // ----------------------------------------------------------------------------- | |||
| // `int` Size Check | |||
| // ----------------------------------------------------------------------------- | |||
| // Abseil currently assumes that an int is 4 bytes. If you would like to use | |||
| // Abseil on a platform where this is not the case, please provide us with the | |||
| // details about your platform so we can consider relaxing this requirement. | |||
| #if INT_MAX < 2147483647 | |||
| #error "Abseil assumes that int is at least 4 bytes. " | |||
| #endif | |||
| #endif // ABSL_BASE_POLICY_CHECKS_H_ | |||
| @@ -0,0 +1,25 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This files is a forwarding header for other headers containing various | |||
| // portability macros and functions. | |||
| #ifndef ABSL_BASE_PORT_H_ | |||
| #define ABSL_BASE_PORT_H_ | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/optimization.h" | |||
| #endif // ABSL_BASE_PORT_H_ | |||
| @@ -0,0 +1,339 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: thread_annotations.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file contains macro definitions for thread safety annotations | |||
| // that allow developers to document the locking policies of multi-threaded | |||
| // code. The annotations can also help program analysis tools to identify | |||
| // potential thread safety issues. | |||
| // | |||
| // These annotations are implemented using compiler attributes. Using the macros | |||
| // defined here instead of raw attributes allow for portability and future | |||
| // compatibility. | |||
| // | |||
| // When referring to mutexes in the arguments of the attributes, you should | |||
| // use variable names or more complex expressions (e.g. my_object->mutex_) | |||
| // that evaluate to a concrete mutex object whenever possible. If the mutex | |||
| // you want to refer to is not in scope, you may use a member pointer | |||
| // (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object. | |||
| #ifndef ABSL_BASE_THREAD_ANNOTATIONS_H_ | |||
| #define ABSL_BASE_THREAD_ANNOTATIONS_H_ | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/config.h" | |||
| // TODO(mbonadei): Remove after the backward compatibility period. | |||
| #include "absl/base/internal/thread_annotations.h" // IWYU pragma: export | |||
| // ABSL_GUARDED_BY() | |||
| // | |||
| // Documents if a shared field or global variable needs to be protected by a | |||
| // mutex. ABSL_GUARDED_BY() allows the user to specify a particular mutex that | |||
| // should be held when accessing the annotated variable. | |||
| // | |||
| // Although this annotation (and ABSL_PT_GUARDED_BY, below) cannot be applied to | |||
| // local variables, a local variable and its associated mutex can often be | |||
| // combined into a small class or struct, thereby allowing the annotation. | |||
| // | |||
| // Example: | |||
| // | |||
| // class Foo { | |||
| // Mutex mu_; | |||
| // int p1_ ABSL_GUARDED_BY(mu_); | |||
| // ... | |||
| // }; | |||
| #if ABSL_HAVE_ATTRIBUTE(guarded_by) | |||
| #define ABSL_GUARDED_BY(x) __attribute__((guarded_by(x))) | |||
| #else | |||
| #define ABSL_GUARDED_BY(x) | |||
| #endif | |||
| // ABSL_PT_GUARDED_BY() | |||
| // | |||
| // Documents if the memory location pointed to by a pointer should be guarded | |||
| // by a mutex when dereferencing the pointer. | |||
| // | |||
| // Example: | |||
| // class Foo { | |||
| // Mutex mu_; | |||
| // int *p1_ ABSL_PT_GUARDED_BY(mu_); | |||
| // ... | |||
| // }; | |||
| // | |||
| // Note that a pointer variable to a shared memory location could itself be a | |||
| // shared variable. | |||
| // | |||
| // Example: | |||
| // | |||
| // // `q_`, guarded by `mu1_`, points to a shared memory location that is | |||
| // // guarded by `mu2_`: | |||
| // int *q_ ABSL_GUARDED_BY(mu1_) ABSL_PT_GUARDED_BY(mu2_); | |||
| #if ABSL_HAVE_ATTRIBUTE(pt_guarded_by) | |||
| #define ABSL_PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x))) | |||
| #else | |||
| #define ABSL_PT_GUARDED_BY(x) | |||
| #endif | |||
| // ABSL_ACQUIRED_AFTER() / ABSL_ACQUIRED_BEFORE() | |||
| // | |||
| // Documents the acquisition order between locks that can be held | |||
| // simultaneously by a thread. For any two locks that need to be annotated | |||
| // to establish an acquisition order, only one of them needs the annotation. | |||
| // (i.e. You don't have to annotate both locks with both ABSL_ACQUIRED_AFTER | |||
| // and ABSL_ACQUIRED_BEFORE.) | |||
| // | |||
| // As with ABSL_GUARDED_BY, this is only applicable to mutexes that are shared | |||
| // fields or global variables. | |||
| // | |||
| // Example: | |||
| // | |||
| // Mutex m1_; | |||
| // Mutex m2_ ABSL_ACQUIRED_AFTER(m1_); | |||
| #if ABSL_HAVE_ATTRIBUTE(acquired_after) | |||
| #define ABSL_ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_ACQUIRED_AFTER(...) | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(acquired_before) | |||
| #define ABSL_ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_ACQUIRED_BEFORE(...) | |||
| #endif | |||
| // ABSL_EXCLUSIVE_LOCKS_REQUIRED() / ABSL_SHARED_LOCKS_REQUIRED() | |||
| // | |||
| // Documents a function that expects a mutex to be held prior to entry. | |||
| // The mutex is expected to be held both on entry to, and exit from, the | |||
| // function. | |||
| // | |||
| // An exclusive lock allows read-write access to the guarded data member(s), and | |||
| // only one thread can acquire a lock exclusively at any one time. A shared lock | |||
| // allows read-only access, and any number of threads can acquire a shared lock | |||
| // concurrently. | |||
| // | |||
| // Generally, non-const methods should be annotated with | |||
| // ABSL_EXCLUSIVE_LOCKS_REQUIRED, while const methods should be annotated with | |||
| // ABSL_SHARED_LOCKS_REQUIRED. | |||
| // | |||
| // Example: | |||
| // | |||
| // Mutex mu1, mu2; | |||
| // int a ABSL_GUARDED_BY(mu1); | |||
| // int b ABSL_GUARDED_BY(mu2); | |||
| // | |||
| // void foo() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... } | |||
| // void bar() const ABSL_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... } | |||
| #if ABSL_HAVE_ATTRIBUTE(exclusive_locks_required) | |||
| #define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) \ | |||
| __attribute__((exclusive_locks_required(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...) | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(shared_locks_required) | |||
| #define ABSL_SHARED_LOCKS_REQUIRED(...) \ | |||
| __attribute__((shared_locks_required(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_SHARED_LOCKS_REQUIRED(...) | |||
| #endif | |||
| // ABSL_LOCKS_EXCLUDED() | |||
| // | |||
| // Documents the locks that cannot be held by callers of this function, as they | |||
| // might be acquired by this function (Abseil's `Mutex` locks are | |||
| // non-reentrant). | |||
| #if ABSL_HAVE_ATTRIBUTE(locks_excluded) | |||
| #define ABSL_LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_LOCKS_EXCLUDED(...) | |||
| #endif | |||
| // ABSL_LOCK_RETURNED() | |||
| // | |||
| // Documents a function that returns a mutex without acquiring it. For example, | |||
| // a public getter method that returns a pointer to a private mutex should | |||
| // be annotated with ABSL_LOCK_RETURNED. | |||
| #if ABSL_HAVE_ATTRIBUTE(lock_returned) | |||
| #define ABSL_LOCK_RETURNED(x) __attribute__((lock_returned(x))) | |||
| #else | |||
| #define ABSL_LOCK_RETURNED(x) | |||
| #endif | |||
| // ABSL_LOCKABLE | |||
| // | |||
| // Documents if a class/type is a lockable type (such as the `Mutex` class). | |||
| #if ABSL_HAVE_ATTRIBUTE(lockable) | |||
| #define ABSL_LOCKABLE __attribute__((lockable)) | |||
| #else | |||
| #define ABSL_LOCKABLE | |||
| #endif | |||
| // ABSL_SCOPED_LOCKABLE | |||
| // | |||
| // Documents if a class does RAII locking (such as the `MutexLock` class). | |||
| // The constructor should use `LOCK_FUNCTION()` to specify the mutex that is | |||
| // acquired, and the destructor should use `UNLOCK_FUNCTION()` with no | |||
| // arguments; the analysis will assume that the destructor unlocks whatever the | |||
| // constructor locked. | |||
| #if ABSL_HAVE_ATTRIBUTE(scoped_lockable) | |||
| #define ABSL_SCOPED_LOCKABLE __attribute__((scoped_lockable)) | |||
| #else | |||
| #define ABSL_SCOPED_LOCKABLE | |||
| #endif | |||
| // ABSL_EXCLUSIVE_LOCK_FUNCTION() | |||
| // | |||
| // Documents functions that acquire a lock in the body of a function, and do | |||
| // not release it. | |||
| #if ABSL_HAVE_ATTRIBUTE(exclusive_lock_function) | |||
| #define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) \ | |||
| __attribute__((exclusive_lock_function(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_EXCLUSIVE_LOCK_FUNCTION(...) | |||
| #endif | |||
| // ABSL_SHARED_LOCK_FUNCTION() | |||
| // | |||
| // Documents functions that acquire a shared (reader) lock in the body of a | |||
| // function, and do not release it. | |||
| #if ABSL_HAVE_ATTRIBUTE(shared_lock_function) | |||
| #define ABSL_SHARED_LOCK_FUNCTION(...) \ | |||
| __attribute__((shared_lock_function(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_SHARED_LOCK_FUNCTION(...) | |||
| #endif | |||
| // ABSL_UNLOCK_FUNCTION() | |||
| // | |||
| // Documents functions that expect a lock to be held on entry to the function, | |||
| // and release it in the body of the function. | |||
| #if ABSL_HAVE_ATTRIBUTE(unlock_function) | |||
| #define ABSL_UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_UNLOCK_FUNCTION(...) | |||
| #endif | |||
| // ABSL_EXCLUSIVE_TRYLOCK_FUNCTION() / ABSL_SHARED_TRYLOCK_FUNCTION() | |||
| // | |||
| // Documents functions that try to acquire a lock, and return success or failure | |||
| // (or a non-boolean value that can be interpreted as a boolean). | |||
| // The first argument should be `true` for functions that return `true` on | |||
| // success, or `false` for functions that return `false` on success. The second | |||
| // argument specifies the mutex that is locked on success. If unspecified, this | |||
| // mutex is assumed to be `this`. | |||
| #if ABSL_HAVE_ATTRIBUTE(exclusive_trylock_function) | |||
| #define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ | |||
| __attribute__((exclusive_trylock_function(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(...) | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(shared_trylock_function) | |||
| #define ABSL_SHARED_TRYLOCK_FUNCTION(...) \ | |||
| __attribute__((shared_trylock_function(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_SHARED_TRYLOCK_FUNCTION(...) | |||
| #endif | |||
| // ABSL_ASSERT_EXCLUSIVE_LOCK() / ABSL_ASSERT_SHARED_LOCK() | |||
| // | |||
| // Documents functions that dynamically check to see if a lock is held, and fail | |||
| // if it is not held. | |||
| #if ABSL_HAVE_ATTRIBUTE(assert_exclusive_lock) | |||
| #define ABSL_ASSERT_EXCLUSIVE_LOCK(...) \ | |||
| __attribute__((assert_exclusive_lock(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_ASSERT_EXCLUSIVE_LOCK(...) | |||
| #endif | |||
| #if ABSL_HAVE_ATTRIBUTE(assert_shared_lock) | |||
| #define ABSL_ASSERT_SHARED_LOCK(...) \ | |||
| __attribute__((assert_shared_lock(__VA_ARGS__))) | |||
| #else | |||
| #define ABSL_ASSERT_SHARED_LOCK(...) | |||
| #endif | |||
| // ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| // | |||
| // Turns off thread safety checking within the body of a particular function. | |||
| // This annotation is used to mark functions that are known to be correct, but | |||
| // the locking behavior is more complicated than the analyzer can handle. | |||
| #if ABSL_HAVE_ATTRIBUTE(no_thread_safety_analysis) | |||
| #define ABSL_NO_THREAD_SAFETY_ANALYSIS \ | |||
| __attribute__((no_thread_safety_analysis)) | |||
| #else | |||
| #define ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| #endif | |||
| //------------------------------------------------------------------------------ | |||
| // Tool-Supplied Annotations | |||
| //------------------------------------------------------------------------------ | |||
| // ABSL_TS_UNCHECKED should be placed around lock expressions that are not valid | |||
| // C++ syntax, but which are present for documentation purposes. These | |||
| // annotations will be ignored by the analysis. | |||
| #define ABSL_TS_UNCHECKED(x) "" | |||
| // ABSL_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. | |||
| // It is used by automated tools to mark and disable invalid expressions. | |||
| // The annotation should either be fixed, or changed to ABSL_TS_UNCHECKED. | |||
| #define ABSL_TS_FIXME(x) "" | |||
| // Like ABSL_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body | |||
| // of a particular function. However, this attribute is used to mark functions | |||
| // that are incorrect and need to be fixed. It is used by automated tools to | |||
| // avoid breaking the build when the analysis is updated. | |||
| // Code owners are expected to eventually fix the routine. | |||
| #define ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| // Similar to ABSL_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a | |||
| // ABSL_GUARDED_BY annotation that needs to be fixed, because it is producing | |||
| // thread safety warning. It disables the ABSL_GUARDED_BY. | |||
| #define ABSL_GUARDED_BY_FIXME(x) | |||
| // Disables warnings for a single read operation. This can be used to avoid | |||
| // warnings when it is known that the read is not actually involved in a race, | |||
| // but the compiler cannot confirm that. | |||
| #define ABSL_TS_UNCHECKED_READ(x) absl::base_internal::ts_unchecked_read(x) | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace base_internal | |||
| { | |||
| // Takes a reference to a guarded data member, and returns an unguarded | |||
| // reference. | |||
| // Do not use this function directly, use ABSL_TS_UNCHECKED_READ instead. | |||
| template<typename T> | |||
| inline const T& ts_unchecked_read(const T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| { | |||
| return v; | |||
| } | |||
| template<typename T> | |||
| inline T& ts_unchecked_read(T& v) ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| { | |||
| return v; | |||
| } | |||
| } // namespace base_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_BASE_THREAD_ANNOTATIONS_H_ | |||
| @@ -0,0 +1,146 @@ | |||
| // Copyright 2021 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: cleanup.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // `absl::Cleanup` implements the scope guard idiom, invoking the contained | |||
| // callback's `operator()() &&` on scope exit. | |||
| // | |||
| // Example: | |||
| // | |||
| // ``` | |||
| // absl::Status CopyGoodData(const char* source_path, const char* sink_path) { | |||
| // FILE* source_file = fopen(source_path, "r"); | |||
| // if (source_file == nullptr) { | |||
| // return absl::NotFoundError("No source file"); // No cleanups execute | |||
| // } | |||
| // | |||
| // // C++17 style cleanup using class template argument deduction | |||
| // absl::Cleanup source_closer = [source_file] { fclose(source_file); }; | |||
| // | |||
| // FILE* sink_file = fopen(sink_path, "w"); | |||
| // if (sink_file == nullptr) { | |||
| // return absl::NotFoundError("No sink file"); // First cleanup executes | |||
| // } | |||
| // | |||
| // // C++11 style cleanup using the factory function | |||
| // auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); }); | |||
| // | |||
| // Data data; | |||
| // while (ReadData(source_file, &data)) { | |||
| // if (!data.IsGood()) { | |||
| // absl::Status result = absl::FailedPreconditionError("Read bad data"); | |||
| // return result; // Both cleanups execute | |||
| // } | |||
| // SaveData(sink_file, &data); | |||
| // } | |||
| // | |||
| // return absl::OkStatus(); // Both cleanups execute | |||
| // } | |||
| // ``` | |||
| // | |||
| // Methods: | |||
| // | |||
| // `std::move(cleanup).Cancel()` will prevent the callback from executing. | |||
| // | |||
| // `std::move(cleanup).Invoke()` will execute the callback early, before | |||
| // destruction, and prevent the callback from executing in the destructor. | |||
| // | |||
| // Usage: | |||
| // | |||
| // `absl::Cleanup` is not an interface type. It is only intended to be used | |||
| // within the body of a function. It is not a value type and instead models a | |||
| // control flow construct. Check out `defer` in Golang for something similar. | |||
| #ifndef ABSL_CLEANUP_CLEANUP_H_ | |||
| #define ABSL_CLEANUP_CLEANUP_H_ | |||
| #include <utility> | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/cleanup/internal/cleanup.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| template<typename Arg, typename Callback = void()> | |||
| class ABSL_MUST_USE_RESULT Cleanup final | |||
| { | |||
| static_assert(cleanup_internal::WasDeduced<Arg>(), "Explicit template parameters are not supported."); | |||
| static_assert(cleanup_internal::ReturnsVoid<Callback>(), "Callbacks that return values are not supported."); | |||
| public: | |||
| Cleanup(Callback callback) : | |||
| storage_(std::move(callback)) | |||
| { | |||
| } // NOLINT | |||
| Cleanup(Cleanup&& other) = default; | |||
| void Cancel() && | |||
| { | |||
| ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); | |||
| storage_.DestroyCallback(); | |||
| } | |||
| void Invoke() && | |||
| { | |||
| ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged()); | |||
| storage_.InvokeCallback(); | |||
| storage_.DestroyCallback(); | |||
| } | |||
| ~Cleanup() | |||
| { | |||
| if (storage_.IsCallbackEngaged()) | |||
| { | |||
| storage_.InvokeCallback(); | |||
| storage_.DestroyCallback(); | |||
| } | |||
| } | |||
| private: | |||
| cleanup_internal::Storage<Callback> storage_; | |||
| }; | |||
| // `absl::Cleanup c = /* callback */;` | |||
| // | |||
| // C++17 type deduction API for creating an instance of `absl::Cleanup` | |||
| #if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) | |||
| template<typename Callback> | |||
| Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>; | |||
| #endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) | |||
| // `auto c = absl::MakeCleanup(/* callback */);` | |||
| // | |||
| // C++11 type deduction API for creating an instance of `absl::Cleanup` | |||
| template<typename... Args, typename Callback> | |||
| absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) | |||
| { | |||
| static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(), "Explicit template parameters are not supported."); | |||
| static_assert(cleanup_internal::ReturnsVoid<Callback>(), "Callbacks that return values are not supported."); | |||
| return {std::move(callback)}; | |||
| } | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CLEANUP_CLEANUP_H_ | |||
| @@ -0,0 +1,118 @@ | |||
| // Copyright 2021 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_ | |||
| #define ABSL_CLEANUP_INTERNAL_CLEANUP_H_ | |||
| #include <new> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/base/internal/invoke.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/thread_annotations.h" | |||
| #include "absl/utility/utility.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace cleanup_internal | |||
| { | |||
| struct Tag | |||
| { | |||
| }; | |||
| template<typename Arg, typename... Args> | |||
| constexpr bool WasDeduced() | |||
| { | |||
| return (std::is_same<cleanup_internal::Tag, Arg>::value) && | |||
| (sizeof...(Args) == 0); | |||
| } | |||
| template<typename Callback> | |||
| constexpr bool ReturnsVoid() | |||
| { | |||
| return (std::is_same<base_internal::invoke_result_t<Callback>, void>::value); | |||
| } | |||
| template<typename Callback> | |||
| class Storage | |||
| { | |||
| public: | |||
| Storage() = delete; | |||
| explicit Storage(Callback callback) | |||
| { | |||
| // Placement-new into a character buffer is used for eager destruction when | |||
| // the cleanup is invoked or cancelled. To ensure this optimizes well, the | |||
| // behavior is implemented locally instead of using an absl::optional. | |||
| ::new (GetCallbackBuffer()) Callback(std::move(callback)); | |||
| is_callback_engaged_ = true; | |||
| } | |||
| Storage(Storage&& other) | |||
| { | |||
| ABSL_HARDENING_ASSERT(other.IsCallbackEngaged()); | |||
| ::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback())); | |||
| is_callback_engaged_ = true; | |||
| other.DestroyCallback(); | |||
| } | |||
| Storage(const Storage& other) = delete; | |||
| Storage& operator=(Storage&& other) = delete; | |||
| Storage& operator=(const Storage& other) = delete; | |||
| void* GetCallbackBuffer() | |||
| { | |||
| return static_cast<void*>(+callback_buffer_); | |||
| } | |||
| Callback& GetCallback() | |||
| { | |||
| return *reinterpret_cast<Callback*>(GetCallbackBuffer()); | |||
| } | |||
| bool IsCallbackEngaged() const | |||
| { | |||
| return is_callback_engaged_; | |||
| } | |||
| void DestroyCallback() | |||
| { | |||
| is_callback_engaged_ = false; | |||
| GetCallback().~Callback(); | |||
| } | |||
| void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS | |||
| { | |||
| std::move(GetCallback())(); | |||
| } | |||
| private: | |||
| bool is_callback_engaged_; | |||
| alignas(Callback) char callback_buffer_[sizeof(Callback)]; | |||
| }; | |||
| } // namespace cleanup_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CLEANUP_INTERNAL_CLEANUP_H_ | |||
| @@ -0,0 +1,869 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: btree_map.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines B-tree maps: sorted associative containers mapping | |||
| // keys to values. | |||
| // | |||
| // * `absl::btree_map<>` | |||
| // * `absl::btree_multimap<>` | |||
| // | |||
| // These B-tree types are similar to the corresponding types in the STL | |||
| // (`std::map` and `std::multimap`) and generally conform to the STL interfaces | |||
| // of those types. However, because they are implemented using B-trees, they | |||
| // are more efficient in most situations. | |||
| // | |||
| // Unlike `std::map` and `std::multimap`, which are commonly implemented using | |||
| // red-black tree nodes, B-tree maps use more generic B-tree nodes able to hold | |||
| // multiple values per node. Holding multiple values per node often makes | |||
| // B-tree maps perform better than their `std::map` counterparts, because | |||
| // multiple entries can be checked within the same cache hit. | |||
| // | |||
| // However, these types should not be considered drop-in replacements for | |||
| // `std::map` and `std::multimap` as there are some API differences, which are | |||
| // noted in this header file. The most consequential differences with respect to | |||
| // migrating to b-tree from the STL types are listed in the next paragraph. | |||
| // Other API differences are minor. | |||
| // | |||
| // Importantly, insertions and deletions may invalidate outstanding iterators, | |||
| // pointers, and references to elements. Such invalidations are typically only | |||
| // an issue if insertion and deletion operations are interleaved with the use of | |||
| // more than one iterator, pointer, or reference simultaneously. For this | |||
| // reason, `insert()` and `erase()` return a valid iterator at the current | |||
| // position. Another important difference is that key-types must be | |||
| // copy-constructible. | |||
| #ifndef ABSL_CONTAINER_BTREE_MAP_H_ | |||
| #define ABSL_CONTAINER_BTREE_MAP_H_ | |||
| #include "absl/container/internal/btree.h" // IWYU pragma: export | |||
| #include "absl/container/internal/btree_container.h" // IWYU pragma: export | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<typename Key, typename Data, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> | |||
| struct map_params; | |||
| } // namespace container_internal | |||
| // absl::btree_map<> | |||
| // | |||
| // An `absl::btree_map<K, V>` is an ordered associative container of | |||
| // unique keys and associated values designed to be a more efficient replacement | |||
| // for `std::map` (in most cases). | |||
| // | |||
| // Keys are sorted using an (optional) comparison function, which defaults to | |||
| // `std::less<K>`. | |||
| // | |||
| // An `absl::btree_map<K, V>` uses a default allocator of | |||
| // `std::allocator<std::pair<const K, V>>` to allocate (and deallocate) | |||
| // nodes, and construct and destruct values within those nodes. You may | |||
| // instead specify a custom allocator `A` (which in turn requires specifying a | |||
| // custom comparator `C`) as in `absl::btree_map<K, V, C, A>`. | |||
| // | |||
| template<typename Key, typename Value, typename Compare = std::less<Key>, typename Alloc = std::allocator<std::pair<const Key, Value>>> | |||
| class btree_map : public container_internal::btree_map_container<container_internal::btree<container_internal::map_params<Key, Value, Compare, Alloc, /*TargetNodeSize=*/256, | |||
| /*IsMulti=*/false>>> | |||
| { | |||
| using Base = typename btree_map::btree_map_container; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A `btree_map` supports the same overload set as `std::map` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // absl::btree_map<int, std::string> map1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::btree_map<int, std::string> map2 = | |||
| // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::btree_map<int, std::string> map3(map2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // absl::btree_map<int, std::string> map4; | |||
| // map4 = map3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::btree_map<int, std::string> map5(std::move(map4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::btree_map<int, std::string> map6; | |||
| // map6 = std::move(map5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}}; | |||
| // absl::btree_map<int, std::string> map7(v.begin(), v.end()); | |||
| btree_map() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // btree_map::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `btree_map`. | |||
| using Base::begin; | |||
| // btree_map::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `btree_map`. | |||
| using Base::cbegin; | |||
| // btree_map::end() | |||
| // | |||
| // Returns an iterator to the end of the `btree_map`. | |||
| using Base::end; | |||
| // btree_map::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `btree_map`. | |||
| using Base::cend; | |||
| // btree_map::empty() | |||
| // | |||
| // Returns whether or not the `btree_map` is empty. | |||
| using Base::empty; | |||
| // btree_map::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `btree_map` under current memory constraints. This value can be thought | |||
| // of as the largest value of `std::distance(begin(), end())` for a | |||
| // `btree_map<Key, T>`. | |||
| using Base::max_size; | |||
| // btree_map::size() | |||
| // | |||
| // Returns the number of elements currently within the `btree_map`. | |||
| using Base::size; | |||
| // btree_map::clear() | |||
| // | |||
| // Removes all elements from the `btree_map`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| using Base::clear; | |||
| // btree_map::erase() | |||
| // | |||
| // Erases elements within the `btree_map`. If an erase occurs, any references, | |||
| // pointers, or iterators are invalidated. | |||
| // Overloads are listed below. | |||
| // | |||
| // iterator erase(iterator position): | |||
| // iterator erase(const_iterator position): | |||
| // | |||
| // Erases the element at `position` of the `btree_map`, returning | |||
| // the iterator pointing to the element after the one that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning | |||
| // the iterator pointing to the element after the interval that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // template <typename K> size_type erase(const K& key): | |||
| // | |||
| // Erases the element with the matching key, if it exists, returning the | |||
| // number of elements erased (0 or 1). | |||
| using Base::erase; | |||
| // btree_map::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `btree_map`, | |||
| // returning an iterator pointing to the newly inserted element, provided that | |||
| // an element with the given key does not already exist. If an insertion | |||
| // occurs, any references, pointers, or iterators are invalidated. | |||
| // Overloads are listed below. | |||
| // | |||
| // std::pair<iterator,bool> insert(const value_type& value): | |||
| // | |||
| // Inserts a value into the `btree_map`. Returns a pair consisting of an | |||
| // iterator to the inserted element (or to the element that prevented the | |||
| // insertion) and a bool denoting whether the insertion took place. | |||
| // | |||
| // std::pair<iterator,bool> insert(value_type&& value): | |||
| // | |||
| // Inserts a moveable value into the `btree_map`. Returns a pair | |||
| // consisting of an iterator to the inserted element (or to the element that | |||
| // prevented the insertion) and a bool denoting whether the insertion took | |||
| // place. | |||
| // | |||
| // iterator insert(const_iterator hint, const value_type& value): | |||
| // iterator insert(const_iterator hint, value_type&& value): | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element, or to the existing element that prevented the | |||
| // insertion. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // void insert(std::initializer_list<init_type> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| using Base::insert; | |||
| // btree_map::insert_or_assign() | |||
| // | |||
| // Inserts an element of the specified value into the `btree_map` provided | |||
| // that a value with the given key does not already exist, or replaces the | |||
| // corresponding mapped type with the forwarded `obj` argument if a key for | |||
| // that value already exists, returning an iterator pointing to the newly | |||
| // inserted element. Overloads are listed below. | |||
| // | |||
| // pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj): | |||
| // pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj): | |||
| // | |||
| // Inserts/Assigns (or moves) the element of the specified key into the | |||
| // `btree_map`. If the returned bool is true, insertion took place, and if | |||
| // it's false, assignment took place. | |||
| // | |||
| // iterator insert_or_assign(const_iterator hint, | |||
| // const key_type& k, M&& obj): | |||
| // iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj): | |||
| // | |||
| // Inserts/Assigns (or moves) the element of the specified key into the | |||
| // `btree_map` using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. | |||
| using Base::insert_or_assign; | |||
| // btree_map::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_map`, provided that no element with the given key | |||
| // already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. Prefer `try_emplace()` unless your key is not | |||
| // copyable or moveable. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace; | |||
| // btree_map::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_map`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search, and only inserts | |||
| // provided that no element with the given key already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. Prefer `try_emplace()` unless your key is not | |||
| // copyable or moveable. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace_hint; | |||
| // btree_map::try_emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_map`, provided that no element with the given key | |||
| // already exists. Unlike `emplace()`, if an element with the given key | |||
| // already exists, we guarantee that no element is constructed. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. | |||
| // | |||
| // Overloads are listed below. | |||
| // | |||
| // std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args): | |||
| // std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args): | |||
| // | |||
| // Inserts (via copy or move) the element of the specified key into the | |||
| // `btree_map`. | |||
| // | |||
| // iterator try_emplace(const_iterator hint, | |||
| // const key_type& k, Args&&... args): | |||
| // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): | |||
| // | |||
| // Inserts (via copy or move) the element of the specified key into the | |||
| // `btree_map` using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. | |||
| using Base::try_emplace; | |||
| // btree_map::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the element at the indicated position and returns a node handle | |||
| // owning that extracted data. | |||
| // | |||
| // template <typename K> node_type extract(const K& k): | |||
| // | |||
| // Extracts the element with the key matching the passed key value and | |||
| // returns a node handle owning that extracted data. If the `btree_map` | |||
| // does not contain an element with a matching key, this function returns an | |||
| // empty node handle. | |||
| // | |||
| // NOTE: when compiled in an earlier version of C++ than C++17, | |||
| // `node_type::key()` returns a const reference to the key instead of a | |||
| // mutable reference. We cannot safely return a mutable reference without | |||
| // std::launder (which is not available before C++17). | |||
| // | |||
| // NOTE: In this context, `node_type` refers to the C++17 concept of a | |||
| // move-only type that owns and provides access to the elements in associative | |||
| // containers (https://en.cppreference.com/w/cpp/container/node_handle). | |||
| // It does NOT refer to the data layout of the underlying btree. | |||
| using Base::extract; | |||
| // btree_map::merge() | |||
| // | |||
| // Extracts elements from a given `source` btree_map into this | |||
| // `btree_map`. If the destination `btree_map` already contains an | |||
| // element with an equivalent key, that element is not extracted. | |||
| using Base::merge; | |||
| // btree_map::swap(btree_map& other) | |||
| // | |||
| // Exchanges the contents of this `btree_map` with those of the `other` | |||
| // btree_map, avoiding invocation of any move, copy, or swap operations on | |||
| // individual elements. | |||
| // | |||
| // All iterators and references on the `btree_map` remain valid, excepting | |||
| // for the past-the-end iterator, which is invalidated. | |||
| using Base::swap; | |||
| // btree_map::at() | |||
| // | |||
| // Returns a reference to the mapped value of the element with key equivalent | |||
| // to the passed key. | |||
| using Base::at; | |||
| // btree_map::contains() | |||
| // | |||
| // template <typename K> bool contains(const K& key) const: | |||
| // | |||
| // Determines whether an element comparing equal to the given `key` exists | |||
| // within the `btree_map`, returning `true` if so or `false` otherwise. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::contains; | |||
| // btree_map::count() | |||
| // | |||
| // template <typename K> size_type count(const K& key) const: | |||
| // | |||
| // Returns the number of elements comparing equal to the given `key` within | |||
| // the `btree_map`. Note that this function will return either `1` or `0` | |||
| // since duplicate elements are not allowed within a `btree_map`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::count; | |||
| // btree_map::equal_range() | |||
| // | |||
| // Returns a half-open range [first, last), defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the `btree_map`. | |||
| using Base::equal_range; | |||
| // btree_map::find() | |||
| // | |||
| // template <typename K> iterator find(const K& key): | |||
| // template <typename K> const_iterator find(const K& key) const: | |||
| // | |||
| // Finds an element with the passed `key` within the `btree_map`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::find; | |||
| // btree_map::lower_bound() | |||
| // | |||
| // template <typename K> iterator lower_bound(const K& key): | |||
| // template <typename K> const_iterator lower_bound(const K& key) const: | |||
| // | |||
| // Finds the first element with a key that is not less than `key` within the | |||
| // `btree_map`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::lower_bound; | |||
| // btree_map::upper_bound() | |||
| // | |||
| // template <typename K> iterator upper_bound(const K& key): | |||
| // template <typename K> const_iterator upper_bound(const K& key) const: | |||
| // | |||
| // Finds the first element with a key that is greater than `key` within the | |||
| // `btree_map`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::upper_bound; | |||
| // btree_map::operator[]() | |||
| // | |||
| // Returns a reference to the value mapped to the passed key within the | |||
| // `btree_map`, performing an `insert()` if the key does not already | |||
| // exist. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. Otherwise iterators are not affected and references are not | |||
| // invalidated. Overloads are listed below. | |||
| // | |||
| // T& operator[](key_type&& key): | |||
| // T& operator[](const key_type& key): | |||
| // | |||
| // Inserts a value_type object constructed in-place if the element with the | |||
| // given key does not exist. | |||
| using Base::operator[]; | |||
| // btree_map::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `btree_map`. | |||
| using Base::get_allocator; | |||
| // btree_map::key_comp(); | |||
| // | |||
| // Returns the key comparator associated with this `btree_map`. | |||
| using Base::key_comp; | |||
| // btree_map::value_comp(); | |||
| // | |||
| // Returns the value comparator associated with this `btree_map`. | |||
| using Base::value_comp; | |||
| }; | |||
| // absl::swap(absl::btree_map<>, absl::btree_map<>) | |||
| // | |||
| // Swaps the contents of two `absl::btree_map` containers. | |||
| template<typename K, typename V, typename C, typename A> | |||
| void swap(btree_map<K, V, C, A>& x, btree_map<K, V, C, A>& y) | |||
| { | |||
| return x.swap(y); | |||
| } | |||
| // absl::erase_if(absl::btree_map<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate pred from the container. | |||
| // Returns the number of erased elements. | |||
| template<typename K, typename V, typename C, typename A, typename Pred> | |||
| typename btree_map<K, V, C, A>::size_type erase_if( | |||
| btree_map<K, V, C, A>& map, Pred pred | |||
| ) | |||
| { | |||
| return container_internal::btree_access::erase_if(map, std::move(pred)); | |||
| } | |||
| // absl::btree_multimap | |||
| // | |||
| // An `absl::btree_multimap<K, V>` is an ordered associative container of | |||
| // keys and associated values designed to be a more efficient replacement for | |||
| // `std::multimap` (in most cases). Unlike `absl::btree_map`, a B-tree multimap | |||
| // allows multiple elements with equivalent keys. | |||
| // | |||
| // Keys are sorted using an (optional) comparison function, which defaults to | |||
| // `std::less<K>`. | |||
| // | |||
| // An `absl::btree_multimap<K, V>` uses a default allocator of | |||
| // `std::allocator<std::pair<const K, V>>` to allocate (and deallocate) | |||
| // nodes, and construct and destruct values within those nodes. You may | |||
| // instead specify a custom allocator `A` (which in turn requires specifying a | |||
| // custom comparator `C`) as in `absl::btree_multimap<K, V, C, A>`. | |||
| // | |||
| template<typename Key, typename Value, typename Compare = std::less<Key>, typename Alloc = std::allocator<std::pair<const Key, Value>>> | |||
| class btree_multimap : public container_internal::btree_multimap_container<container_internal::btree<container_internal::map_params<Key, Value, Compare, Alloc, /*TargetNodeSize=*/256, | |||
| /*IsMulti=*/true>>> | |||
| { | |||
| using Base = typename btree_multimap::btree_multimap_container; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A `btree_multimap` supports the same overload set as `std::multimap` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // absl::btree_multimap<int, std::string> map1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::btree_multimap<int, std::string> map2 = | |||
| // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::btree_multimap<int, std::string> map3(map2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // absl::btree_multimap<int, std::string> map4; | |||
| // map4 = map3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::btree_multimap<int, std::string> map5(std::move(map4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::btree_multimap<int, std::string> map6; | |||
| // map6 = std::move(map5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}}; | |||
| // absl::btree_multimap<int, std::string> map7(v.begin(), v.end()); | |||
| btree_multimap() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // btree_multimap::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `btree_multimap`. | |||
| using Base::begin; | |||
| // btree_multimap::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `btree_multimap`. | |||
| using Base::cbegin; | |||
| // btree_multimap::end() | |||
| // | |||
| // Returns an iterator to the end of the `btree_multimap`. | |||
| using Base::end; | |||
| // btree_multimap::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `btree_multimap`. | |||
| using Base::cend; | |||
| // btree_multimap::empty() | |||
| // | |||
| // Returns whether or not the `btree_multimap` is empty. | |||
| using Base::empty; | |||
| // btree_multimap::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `btree_multimap` under current memory constraints. This value can be | |||
| // thought of as the largest value of `std::distance(begin(), end())` for a | |||
| // `btree_multimap<Key, T>`. | |||
| using Base::max_size; | |||
| // btree_multimap::size() | |||
| // | |||
| // Returns the number of elements currently within the `btree_multimap`. | |||
| using Base::size; | |||
| // btree_multimap::clear() | |||
| // | |||
| // Removes all elements from the `btree_multimap`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| using Base::clear; | |||
| // btree_multimap::erase() | |||
| // | |||
| // Erases elements within the `btree_multimap`. If an erase occurs, any | |||
| // references, pointers, or iterators are invalidated. | |||
| // Overloads are listed below. | |||
| // | |||
| // iterator erase(iterator position): | |||
| // iterator erase(const_iterator position): | |||
| // | |||
| // Erases the element at `position` of the `btree_multimap`, returning | |||
| // the iterator pointing to the element after the one that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning | |||
| // the iterator pointing to the element after the interval that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // template <typename K> size_type erase(const K& key): | |||
| // | |||
| // Erases the elements matching the key, if any exist, returning the | |||
| // number of elements erased. | |||
| using Base::erase; | |||
| // btree_multimap::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `btree_multimap`, | |||
| // returning an iterator pointing to the newly inserted element. | |||
| // Any references, pointers, or iterators are invalidated. Overloads are | |||
| // listed below. | |||
| // | |||
| // iterator insert(const value_type& value): | |||
| // | |||
| // Inserts a value into the `btree_multimap`, returning an iterator to the | |||
| // inserted element. | |||
| // | |||
| // iterator insert(value_type&& value): | |||
| // | |||
| // Inserts a moveable value into the `btree_multimap`, returning an iterator | |||
| // to the inserted element. | |||
| // | |||
| // iterator insert(const_iterator hint, const value_type& value): | |||
| // iterator insert(const_iterator hint, value_type&& value): | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // void insert(std::initializer_list<init_type> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| using Base::insert; | |||
| // btree_multimap::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_multimap`. Any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace; | |||
| // btree_multimap::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_multimap`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search. | |||
| // | |||
| // Any references, pointers, or iterators are invalidated. | |||
| using Base::emplace_hint; | |||
| // btree_multimap::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the element at the indicated position and returns a node handle | |||
| // owning that extracted data. | |||
| // | |||
| // template <typename K> node_type extract(const K& k): | |||
| // | |||
| // Extracts the element with the key matching the passed key value and | |||
| // returns a node handle owning that extracted data. If the `btree_multimap` | |||
| // does not contain an element with a matching key, this function returns an | |||
| // empty node handle. | |||
| // | |||
| // NOTE: when compiled in an earlier version of C++ than C++17, | |||
| // `node_type::key()` returns a const reference to the key instead of a | |||
| // mutable reference. We cannot safely return a mutable reference without | |||
| // std::launder (which is not available before C++17). | |||
| // | |||
| // NOTE: In this context, `node_type` refers to the C++17 concept of a | |||
| // move-only type that owns and provides access to the elements in associative | |||
| // containers (https://en.cppreference.com/w/cpp/container/node_handle). | |||
| // It does NOT refer to the data layout of the underlying btree. | |||
| using Base::extract; | |||
| // btree_multimap::merge() | |||
| // | |||
| // Extracts all elements from a given `source` btree_multimap into this | |||
| // `btree_multimap`. | |||
| using Base::merge; | |||
| // btree_multimap::swap(btree_multimap& other) | |||
| // | |||
| // Exchanges the contents of this `btree_multimap` with those of the `other` | |||
| // btree_multimap, avoiding invocation of any move, copy, or swap operations | |||
| // on individual elements. | |||
| // | |||
| // All iterators and references on the `btree_multimap` remain valid, | |||
| // excepting for the past-the-end iterator, which is invalidated. | |||
| using Base::swap; | |||
| // btree_multimap::contains() | |||
| // | |||
| // template <typename K> bool contains(const K& key) const: | |||
| // | |||
| // Determines whether an element comparing equal to the given `key` exists | |||
| // within the `btree_multimap`, returning `true` if so or `false` otherwise. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::contains; | |||
| // btree_multimap::count() | |||
| // | |||
| // template <typename K> size_type count(const K& key) const: | |||
| // | |||
| // Returns the number of elements comparing equal to the given `key` within | |||
| // the `btree_multimap`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::count; | |||
| // btree_multimap::equal_range() | |||
| // | |||
| // Returns a half-open range [first, last), defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the | |||
| // `btree_multimap`. | |||
| using Base::equal_range; | |||
| // btree_multimap::find() | |||
| // | |||
| // template <typename K> iterator find(const K& key): | |||
| // template <typename K> const_iterator find(const K& key) const: | |||
| // | |||
| // Finds an element with the passed `key` within the `btree_multimap`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::find; | |||
| // btree_multimap::lower_bound() | |||
| // | |||
| // template <typename K> iterator lower_bound(const K& key): | |||
| // template <typename K> const_iterator lower_bound(const K& key) const: | |||
| // | |||
| // Finds the first element with a key that is not less than `key` within the | |||
| // `btree_multimap`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::lower_bound; | |||
| // btree_multimap::upper_bound() | |||
| // | |||
| // template <typename K> iterator upper_bound(const K& key): | |||
| // template <typename K> const_iterator upper_bound(const K& key) const: | |||
| // | |||
| // Finds the first element with a key that is greater than `key` within the | |||
| // `btree_multimap`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the map has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::upper_bound; | |||
| // btree_multimap::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `btree_multimap`. | |||
| using Base::get_allocator; | |||
| // btree_multimap::key_comp(); | |||
| // | |||
| // Returns the key comparator associated with this `btree_multimap`. | |||
| using Base::key_comp; | |||
| // btree_multimap::value_comp(); | |||
| // | |||
| // Returns the value comparator associated with this `btree_multimap`. | |||
| using Base::value_comp; | |||
| }; | |||
| // absl::swap(absl::btree_multimap<>, absl::btree_multimap<>) | |||
| // | |||
| // Swaps the contents of two `absl::btree_multimap` containers. | |||
| template<typename K, typename V, typename C, typename A> | |||
| void swap(btree_multimap<K, V, C, A>& x, btree_multimap<K, V, C, A>& y) | |||
| { | |||
| return x.swap(y); | |||
| } | |||
| // absl::erase_if(absl::btree_multimap<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate pred from the container. | |||
| // Returns the number of erased elements. | |||
| template<typename K, typename V, typename C, typename A, typename Pred> | |||
| typename btree_multimap<K, V, C, A>::size_type erase_if( | |||
| btree_multimap<K, V, C, A>& map, Pred pred | |||
| ) | |||
| { | |||
| return container_internal::btree_access::erase_if(map, std::move(pred)); | |||
| } | |||
| namespace container_internal | |||
| { | |||
| // A parameters structure for holding the type parameters for a btree_map. | |||
| // Compare and Alloc should be nothrow copy-constructible. | |||
| template<typename Key, typename Data, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> | |||
| struct map_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti, | |||
| /*IsMap=*/true, | |||
| map_slot_policy<Key, Data>> | |||
| { | |||
| using super_type = typename map_params::common_params; | |||
| using mapped_type = Data; | |||
| // This type allows us to move keys when it is safe to do so. It is safe | |||
| // for maps in which value_type and mutable_value_type are layout compatible. | |||
| using slot_policy = typename super_type::slot_policy; | |||
| using slot_type = typename super_type::slot_type; | |||
| using value_type = typename super_type::value_type; | |||
| using init_type = typename super_type::init_type; | |||
| template<typename V> | |||
| static auto key(const V& value) -> decltype(value.first) | |||
| { | |||
| return value.first; | |||
| } | |||
| static const Key& key(const slot_type* s) | |||
| { | |||
| return slot_policy::key(s); | |||
| } | |||
| static const Key& key(slot_type* s) | |||
| { | |||
| return slot_policy::key(s); | |||
| } | |||
| // For use in node handle. | |||
| static auto mutable_key(slot_type* s) | |||
| -> decltype(slot_policy::mutable_key(s)) | |||
| { | |||
| return slot_policy::mutable_key(s); | |||
| } | |||
| static mapped_type& value(value_type* value) | |||
| { | |||
| return value->second; | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_BTREE_MAP_H_ | |||
| @@ -0,0 +1,816 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: btree_set.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines B-tree sets: sorted associative containers of | |||
| // values. | |||
| // | |||
| // * `absl::btree_set<>` | |||
| // * `absl::btree_multiset<>` | |||
| // | |||
| // These B-tree types are similar to the corresponding types in the STL | |||
| // (`std::set` and `std::multiset`) and generally conform to the STL interfaces | |||
| // of those types. However, because they are implemented using B-trees, they | |||
| // are more efficient in most situations. | |||
| // | |||
| // Unlike `std::set` and `std::multiset`, which are commonly implemented using | |||
| // red-black tree nodes, B-tree sets use more generic B-tree nodes able to hold | |||
| // multiple values per node. Holding multiple values per node often makes | |||
| // B-tree sets perform better than their `std::set` counterparts, because | |||
| // multiple entries can be checked within the same cache hit. | |||
| // | |||
| // However, these types should not be considered drop-in replacements for | |||
| // `std::set` and `std::multiset` as there are some API differences, which are | |||
| // noted in this header file. The most consequential differences with respect to | |||
| // migrating to b-tree from the STL types are listed in the next paragraph. | |||
| // Other API differences are minor. | |||
| // | |||
| // Importantly, insertions and deletions may invalidate outstanding iterators, | |||
| // pointers, and references to elements. Such invalidations are typically only | |||
| // an issue if insertion and deletion operations are interleaved with the use of | |||
| // more than one iterator, pointer, or reference simultaneously. For this | |||
| // reason, `insert()` and `erase()` return a valid iterator at the current | |||
| // position. | |||
| #ifndef ABSL_CONTAINER_BTREE_SET_H_ | |||
| #define ABSL_CONTAINER_BTREE_SET_H_ | |||
| #include "absl/container/internal/btree.h" // IWYU pragma: export | |||
| #include "absl/container/internal/btree_container.h" // IWYU pragma: export | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<typename Key> | |||
| struct set_slot_policy; | |||
| template<typename Key, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> | |||
| struct set_params; | |||
| } // namespace container_internal | |||
| // absl::btree_set<> | |||
| // | |||
| // An `absl::btree_set<K>` is an ordered associative container of unique key | |||
| // values designed to be a more efficient replacement for `std::set` (in most | |||
| // cases). | |||
| // | |||
| // Keys are sorted using an (optional) comparison function, which defaults to | |||
| // `std::less<K>`. | |||
| // | |||
| // An `absl::btree_set<K>` uses a default allocator of `std::allocator<K>` to | |||
| // allocate (and deallocate) nodes, and construct and destruct values within | |||
| // those nodes. You may instead specify a custom allocator `A` (which in turn | |||
| // requires specifying a custom comparator `C`) as in | |||
| // `absl::btree_set<K, C, A>`. | |||
| // | |||
| template<typename Key, typename Compare = std::less<Key>, typename Alloc = std::allocator<Key>> | |||
| class btree_set : public container_internal::btree_set_container<container_internal::btree<container_internal::set_params<Key, Compare, Alloc, /*TargetNodeSize=*/256, | |||
| /*IsMulti=*/false>>> | |||
| { | |||
| using Base = typename btree_set::btree_set_container; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A `btree_set` supports the same overload set as `std::set` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // absl::btree_set<std::string> set1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::btree_set<std::string> set2 = | |||
| // {{"huey"}, {"dewey"}, {"louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::btree_set<std::string> set3(set2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // absl::btree_set<std::string> set4; | |||
| // set4 = set3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::btree_set<std::string> set5(std::move(set4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::btree_set<std::string> set6; | |||
| // set6 = std::move(set5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::string> v = {"a", "b"}; | |||
| // absl::btree_set<std::string> set7(v.begin(), v.end()); | |||
| btree_set() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // btree_set::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `btree_set`. | |||
| using Base::begin; | |||
| // btree_set::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `btree_set`. | |||
| using Base::cbegin; | |||
| // btree_set::end() | |||
| // | |||
| // Returns an iterator to the end of the `btree_set`. | |||
| using Base::end; | |||
| // btree_set::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `btree_set`. | |||
| using Base::cend; | |||
| // btree_set::empty() | |||
| // | |||
| // Returns whether or not the `btree_set` is empty. | |||
| using Base::empty; | |||
| // btree_set::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `btree_set` under current memory constraints. This value can be thought | |||
| // of as the largest value of `std::distance(begin(), end())` for a | |||
| // `btree_set<Key>`. | |||
| using Base::max_size; | |||
| // btree_set::size() | |||
| // | |||
| // Returns the number of elements currently within the `btree_set`. | |||
| using Base::size; | |||
| // btree_set::clear() | |||
| // | |||
| // Removes all elements from the `btree_set`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| using Base::clear; | |||
| // btree_set::erase() | |||
| // | |||
| // Erases elements within the `btree_set`. Overloads are listed below. | |||
| // | |||
| // iterator erase(iterator position): | |||
| // iterator erase(const_iterator position): | |||
| // | |||
| // Erases the element at `position` of the `btree_set`, returning | |||
| // the iterator pointing to the element after the one that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning | |||
| // the iterator pointing to the element after the interval that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // template <typename K> size_type erase(const K& key): | |||
| // | |||
| // Erases the element with the matching key, if it exists, returning the | |||
| // number of elements erased (0 or 1). | |||
| using Base::erase; | |||
| // btree_set::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `btree_set`, | |||
| // returning an iterator pointing to the newly inserted element, provided that | |||
| // an element with the given key does not already exist. If an insertion | |||
| // occurs, any references, pointers, or iterators are invalidated. | |||
| // Overloads are listed below. | |||
| // | |||
| // std::pair<iterator,bool> insert(const value_type& value): | |||
| // | |||
| // Inserts a value into the `btree_set`. Returns a pair consisting of an | |||
| // iterator to the inserted element (or to the element that prevented the | |||
| // insertion) and a bool denoting whether the insertion took place. | |||
| // | |||
| // std::pair<iterator,bool> insert(value_type&& value): | |||
| // | |||
| // Inserts a moveable value into the `btree_set`. Returns a pair | |||
| // consisting of an iterator to the inserted element (or to the element that | |||
| // prevented the insertion) and a bool denoting whether the insertion took | |||
| // place. | |||
| // | |||
| // iterator insert(const_iterator hint, const value_type& value): | |||
| // iterator insert(const_iterator hint, value_type&& value): | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element, or to the existing element that prevented the | |||
| // insertion. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // void insert(std::initializer_list<init_type> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| using Base::insert; | |||
| // btree_set::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_set`, provided that no element with the given key | |||
| // already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace; | |||
| // btree_set::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_set`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search, and only inserts | |||
| // provided that no element with the given key already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. | |||
| // | |||
| // If an insertion occurs, any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace_hint; | |||
| // btree_set::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the element at the indicated position and returns a node handle | |||
| // owning that extracted data. | |||
| // | |||
| // template <typename K> node_type extract(const K& k): | |||
| // | |||
| // Extracts the element with the key matching the passed key value and | |||
| // returns a node handle owning that extracted data. If the `btree_set` | |||
| // does not contain an element with a matching key, this function returns an | |||
| // empty node handle. | |||
| // | |||
| // NOTE: In this context, `node_type` refers to the C++17 concept of a | |||
| // move-only type that owns and provides access to the elements in associative | |||
| // containers (https://en.cppreference.com/w/cpp/container/node_handle). | |||
| // It does NOT refer to the data layout of the underlying btree. | |||
| using Base::extract; | |||
| // btree_set::merge() | |||
| // | |||
| // Extracts elements from a given `source` btree_set into this | |||
| // `btree_set`. If the destination `btree_set` already contains an | |||
| // element with an equivalent key, that element is not extracted. | |||
| using Base::merge; | |||
| // btree_set::swap(btree_set& other) | |||
| // | |||
| // Exchanges the contents of this `btree_set` with those of the `other` | |||
| // btree_set, avoiding invocation of any move, copy, or swap operations on | |||
| // individual elements. | |||
| // | |||
| // All iterators and references on the `btree_set` remain valid, excepting | |||
| // for the past-the-end iterator, which is invalidated. | |||
| using Base::swap; | |||
| // btree_set::contains() | |||
| // | |||
| // template <typename K> bool contains(const K& key) const: | |||
| // | |||
| // Determines whether an element comparing equal to the given `key` exists | |||
| // within the `btree_set`, returning `true` if so or `false` otherwise. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::contains; | |||
| // btree_set::count() | |||
| // | |||
| // template <typename K> size_type count(const K& key) const: | |||
| // | |||
| // Returns the number of elements comparing equal to the given `key` within | |||
| // the `btree_set`. Note that this function will return either `1` or `0` | |||
| // since duplicate elements are not allowed within a `btree_set`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::count; | |||
| // btree_set::equal_range() | |||
| // | |||
| // Returns a closed range [first, last], defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the | |||
| // `btree_set`. | |||
| using Base::equal_range; | |||
| // btree_set::find() | |||
| // | |||
| // template <typename K> iterator find(const K& key): | |||
| // template <typename K> const_iterator find(const K& key) const: | |||
| // | |||
| // Finds an element with the passed `key` within the `btree_set`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::find; | |||
| // btree_set::lower_bound() | |||
| // | |||
| // template <typename K> iterator lower_bound(const K& key): | |||
| // template <typename K> const_iterator lower_bound(const K& key) const: | |||
| // | |||
| // Finds the first element that is not less than `key` within the `btree_set`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::lower_bound; | |||
| // btree_set::upper_bound() | |||
| // | |||
| // template <typename K> iterator upper_bound(const K& key): | |||
| // template <typename K> const_iterator upper_bound(const K& key) const: | |||
| // | |||
| // Finds the first element that is greater than `key` within the `btree_set`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::upper_bound; | |||
| // btree_set::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `btree_set`. | |||
| using Base::get_allocator; | |||
| // btree_set::key_comp(); | |||
| // | |||
| // Returns the key comparator associated with this `btree_set`. | |||
| using Base::key_comp; | |||
| // btree_set::value_comp(); | |||
| // | |||
| // Returns the value comparator associated with this `btree_set`. The keys to | |||
| // sort the elements are the values themselves, therefore `value_comp` and its | |||
| // sibling member function `key_comp` are equivalent. | |||
| using Base::value_comp; | |||
| }; | |||
| // absl::swap(absl::btree_set<>, absl::btree_set<>) | |||
| // | |||
| // Swaps the contents of two `absl::btree_set` containers. | |||
| template<typename K, typename C, typename A> | |||
| void swap(btree_set<K, C, A>& x, btree_set<K, C, A>& y) | |||
| { | |||
| return x.swap(y); | |||
| } | |||
| // absl::erase_if(absl::btree_set<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate pred from the container. | |||
| // Returns the number of erased elements. | |||
| template<typename K, typename C, typename A, typename Pred> | |||
| typename btree_set<K, C, A>::size_type erase_if(btree_set<K, C, A>& set, Pred pred) | |||
| { | |||
| return container_internal::btree_access::erase_if(set, std::move(pred)); | |||
| } | |||
| // absl::btree_multiset<> | |||
| // | |||
| // An `absl::btree_multiset<K>` is an ordered associative container of | |||
| // keys and associated values designed to be a more efficient replacement | |||
| // for `std::multiset` (in most cases). Unlike `absl::btree_set`, a B-tree | |||
| // multiset allows equivalent elements. | |||
| // | |||
| // Keys are sorted using an (optional) comparison function, which defaults to | |||
| // `std::less<K>`. | |||
| // | |||
| // An `absl::btree_multiset<K>` uses a default allocator of `std::allocator<K>` | |||
| // to allocate (and deallocate) nodes, and construct and destruct values within | |||
| // those nodes. You may instead specify a custom allocator `A` (which in turn | |||
| // requires specifying a custom comparator `C`) as in | |||
| // `absl::btree_multiset<K, C, A>`. | |||
| // | |||
| template<typename Key, typename Compare = std::less<Key>, typename Alloc = std::allocator<Key>> | |||
| class btree_multiset : public container_internal::btree_multiset_container<container_internal::btree<container_internal::set_params<Key, Compare, Alloc, /*TargetNodeSize=*/256, | |||
| /*IsMulti=*/true>>> | |||
| { | |||
| using Base = typename btree_multiset::btree_multiset_container; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A `btree_multiset` supports the same overload set as `std::set` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // absl::btree_multiset<std::string> set1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::btree_multiset<std::string> set2 = | |||
| // {{"huey"}, {"dewey"}, {"louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::btree_multiset<std::string> set3(set2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // absl::btree_multiset<std::string> set4; | |||
| // set4 = set3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::btree_multiset<std::string> set5(std::move(set4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::btree_multiset<std::string> set6; | |||
| // set6 = std::move(set5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::string> v = {"a", "b"}; | |||
| // absl::btree_multiset<std::string> set7(v.begin(), v.end()); | |||
| btree_multiset() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // btree_multiset::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `btree_multiset`. | |||
| using Base::begin; | |||
| // btree_multiset::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `btree_multiset`. | |||
| using Base::cbegin; | |||
| // btree_multiset::end() | |||
| // | |||
| // Returns an iterator to the end of the `btree_multiset`. | |||
| using Base::end; | |||
| // btree_multiset::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `btree_multiset`. | |||
| using Base::cend; | |||
| // btree_multiset::empty() | |||
| // | |||
| // Returns whether or not the `btree_multiset` is empty. | |||
| using Base::empty; | |||
| // btree_multiset::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `btree_multiset` under current memory constraints. This value can be | |||
| // thought of as the largest value of `std::distance(begin(), end())` for a | |||
| // `btree_multiset<Key>`. | |||
| using Base::max_size; | |||
| // btree_multiset::size() | |||
| // | |||
| // Returns the number of elements currently within the `btree_multiset`. | |||
| using Base::size; | |||
| // btree_multiset::clear() | |||
| // | |||
| // Removes all elements from the `btree_multiset`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| using Base::clear; | |||
| // btree_multiset::erase() | |||
| // | |||
| // Erases elements within the `btree_multiset`. Overloads are listed below. | |||
| // | |||
| // iterator erase(iterator position): | |||
| // iterator erase(const_iterator position): | |||
| // | |||
| // Erases the element at `position` of the `btree_multiset`, returning | |||
| // the iterator pointing to the element after the one that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning | |||
| // the iterator pointing to the element after the interval that was erased | |||
| // (or end() if none exists). | |||
| // | |||
| // template <typename K> size_type erase(const K& key): | |||
| // | |||
| // Erases the elements matching the key, if any exist, returning the | |||
| // number of elements erased. | |||
| using Base::erase; | |||
| // btree_multiset::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `btree_multiset`, | |||
| // returning an iterator pointing to the newly inserted element. | |||
| // Any references, pointers, or iterators are invalidated. Overloads are | |||
| // listed below. | |||
| // | |||
| // iterator insert(const value_type& value): | |||
| // | |||
| // Inserts a value into the `btree_multiset`, returning an iterator to the | |||
| // inserted element. | |||
| // | |||
| // iterator insert(value_type&& value): | |||
| // | |||
| // Inserts a moveable value into the `btree_multiset`, returning an iterator | |||
| // to the inserted element. | |||
| // | |||
| // iterator insert(const_iterator hint, const value_type& value): | |||
| // iterator insert(const_iterator hint, value_type&& value): | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // void insert(std::initializer_list<init_type> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| using Base::insert; | |||
| // btree_multiset::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_multiset`. Any references, pointers, or iterators are | |||
| // invalidated. | |||
| using Base::emplace; | |||
| // btree_multiset::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `btree_multiset`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search. | |||
| // | |||
| // Any references, pointers, or iterators are invalidated. | |||
| using Base::emplace_hint; | |||
| // btree_multiset::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the element at the indicated position and returns a node handle | |||
| // owning that extracted data. | |||
| // | |||
| // template <typename K> node_type extract(const K& k): | |||
| // | |||
| // Extracts the element with the key matching the passed key value and | |||
| // returns a node handle owning that extracted data. If the `btree_multiset` | |||
| // does not contain an element with a matching key, this function returns an | |||
| // empty node handle. | |||
| // | |||
| // NOTE: In this context, `node_type` refers to the C++17 concept of a | |||
| // move-only type that owns and provides access to the elements in associative | |||
| // containers (https://en.cppreference.com/w/cpp/container/node_handle). | |||
| // It does NOT refer to the data layout of the underlying btree. | |||
| using Base::extract; | |||
| // btree_multiset::merge() | |||
| // | |||
| // Extracts all elements from a given `source` btree_multiset into this | |||
| // `btree_multiset`. | |||
| using Base::merge; | |||
| // btree_multiset::swap(btree_multiset& other) | |||
| // | |||
| // Exchanges the contents of this `btree_multiset` with those of the `other` | |||
| // btree_multiset, avoiding invocation of any move, copy, or swap operations | |||
| // on individual elements. | |||
| // | |||
| // All iterators and references on the `btree_multiset` remain valid, | |||
| // excepting for the past-the-end iterator, which is invalidated. | |||
| using Base::swap; | |||
| // btree_multiset::contains() | |||
| // | |||
| // template <typename K> bool contains(const K& key) const: | |||
| // | |||
| // Determines whether an element comparing equal to the given `key` exists | |||
| // within the `btree_multiset`, returning `true` if so or `false` otherwise. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::contains; | |||
| // btree_multiset::count() | |||
| // | |||
| // template <typename K> size_type count(const K& key) const: | |||
| // | |||
| // Returns the number of elements comparing equal to the given `key` within | |||
| // the `btree_multiset`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::count; | |||
| // btree_multiset::equal_range() | |||
| // | |||
| // Returns a closed range [first, last], defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the | |||
| // `btree_multiset`. | |||
| using Base::equal_range; | |||
| // btree_multiset::find() | |||
| // | |||
| // template <typename K> iterator find(const K& key): | |||
| // template <typename K> const_iterator find(const K& key) const: | |||
| // | |||
| // Finds an element with the passed `key` within the `btree_multiset`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::find; | |||
| // btree_multiset::lower_bound() | |||
| // | |||
| // template <typename K> iterator lower_bound(const K& key): | |||
| // template <typename K> const_iterator lower_bound(const K& key) const: | |||
| // | |||
| // Finds the first element that is not less than `key` within the | |||
| // `btree_multiset`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::lower_bound; | |||
| // btree_multiset::upper_bound() | |||
| // | |||
| // template <typename K> iterator upper_bound(const K& key): | |||
| // template <typename K> const_iterator upper_bound(const K& key) const: | |||
| // | |||
| // Finds the first element that is greater than `key` within the | |||
| // `btree_multiset`. | |||
| // | |||
| // Supports heterogeneous lookup, provided that the set has a compatible | |||
| // heterogeneous comparator. | |||
| using Base::upper_bound; | |||
| // btree_multiset::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `btree_multiset`. | |||
| using Base::get_allocator; | |||
| // btree_multiset::key_comp(); | |||
| // | |||
| // Returns the key comparator associated with this `btree_multiset`. | |||
| using Base::key_comp; | |||
| // btree_multiset::value_comp(); | |||
| // | |||
| // Returns the value comparator associated with this `btree_multiset`. The | |||
| // keys to sort the elements are the values themselves, therefore `value_comp` | |||
| // and its sibling member function `key_comp` are equivalent. | |||
| using Base::value_comp; | |||
| }; | |||
| // absl::swap(absl::btree_multiset<>, absl::btree_multiset<>) | |||
| // | |||
| // Swaps the contents of two `absl::btree_multiset` containers. | |||
| template<typename K, typename C, typename A> | |||
| void swap(btree_multiset<K, C, A>& x, btree_multiset<K, C, A>& y) | |||
| { | |||
| return x.swap(y); | |||
| } | |||
| // absl::erase_if(absl::btree_multiset<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate pred from the container. | |||
| // Returns the number of erased elements. | |||
| template<typename K, typename C, typename A, typename Pred> | |||
| typename btree_multiset<K, C, A>::size_type erase_if( | |||
| btree_multiset<K, C, A>& set, Pred pred | |||
| ) | |||
| { | |||
| return container_internal::btree_access::erase_if(set, std::move(pred)); | |||
| } | |||
| namespace container_internal | |||
| { | |||
| // This type implements the necessary functions from the | |||
| // absl::container_internal::slot_type interface for btree_(multi)set. | |||
| template<typename Key> | |||
| struct set_slot_policy | |||
| { | |||
| using slot_type = Key; | |||
| using value_type = Key; | |||
| using mutable_value_type = Key; | |||
| static value_type& element(slot_type* slot) | |||
| { | |||
| return *slot; | |||
| } | |||
| static const value_type& element(const slot_type* slot) | |||
| { | |||
| return *slot; | |||
| } | |||
| template<typename Alloc, class... Args> | |||
| static void construct(Alloc* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| absl::allocator_traits<Alloc>::construct(*alloc, slot, std::forward<Args>(args)...); | |||
| } | |||
| template<typename Alloc> | |||
| static void construct(Alloc* alloc, slot_type* slot, slot_type* other) | |||
| { | |||
| absl::allocator_traits<Alloc>::construct(*alloc, slot, std::move(*other)); | |||
| } | |||
| template<typename Alloc> | |||
| static void construct(Alloc* alloc, slot_type* slot, const slot_type* other) | |||
| { | |||
| absl::allocator_traits<Alloc>::construct(*alloc, slot, *other); | |||
| } | |||
| template<typename Alloc> | |||
| static void destroy(Alloc* alloc, slot_type* slot) | |||
| { | |||
| absl::allocator_traits<Alloc>::destroy(*alloc, slot); | |||
| } | |||
| template<typename Alloc> | |||
| static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| construct(alloc, new_slot, old_slot); | |||
| destroy(alloc, old_slot); | |||
| } | |||
| }; | |||
| // A parameters structure for holding the type parameters for a btree_set. | |||
| // Compare and Alloc should be nothrow copy-constructible. | |||
| template<typename Key, typename Compare, typename Alloc, int TargetNodeSize, bool IsMulti> | |||
| struct set_params : common_params<Key, Compare, Alloc, TargetNodeSize, IsMulti, | |||
| /*IsMap=*/false, | |||
| set_slot_policy<Key>> | |||
| { | |||
| using value_type = Key; | |||
| using slot_type = typename set_params::common_params::slot_type; | |||
| template<typename V> | |||
| static const V& key(const V& value) | |||
| { | |||
| return value; | |||
| } | |||
| static const Key& key(const slot_type* slot) | |||
| { | |||
| return *slot; | |||
| } | |||
| static const Key& key(slot_type* slot) | |||
| { | |||
| return *slot; | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_BTREE_SET_H_ | |||
| @@ -0,0 +1,215 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_BTREE_TEST_H_ | |||
| #define ABSL_CONTAINER_BTREE_TEST_H_ | |||
| #include <algorithm> | |||
| #include <cassert> | |||
| #include <random> | |||
| #include <string> | |||
| #include <utility> | |||
| #include <vector> | |||
| #include "absl/container/btree_map.h" | |||
| #include "absl/container/btree_set.h" | |||
| #include "absl/container/flat_hash_set.h" | |||
| #include "absl/strings/cord.h" | |||
| #include "absl/time/time.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // Like remove_const but propagates the removal through std::pair. | |||
| template<typename T> | |||
| struct remove_pair_const | |||
| { | |||
| using type = typename std::remove_const<T>::type; | |||
| }; | |||
| template<typename T, typename U> | |||
| struct remove_pair_const<std::pair<T, U>> | |||
| { | |||
| using type = std::pair<typename remove_pair_const<T>::type, typename remove_pair_const<U>::type>; | |||
| }; | |||
| // Utility class to provide an accessor for a key given a value. The default | |||
| // behavior is to treat the value as a pair and return the first element. | |||
| template<typename K, typename V> | |||
| struct KeyOfValue | |||
| { | |||
| struct type | |||
| { | |||
| const K& operator()(const V& p) const | |||
| { | |||
| return p.first; | |||
| } | |||
| }; | |||
| }; | |||
| // Partial specialization of KeyOfValue class for when the key and value are | |||
| // the same type such as in set<> and btree_set<>. | |||
| template<typename K> | |||
| struct KeyOfValue<K, K> | |||
| { | |||
| struct type | |||
| { | |||
| const K& operator()(const K& k) const | |||
| { | |||
| return k; | |||
| } | |||
| }; | |||
| }; | |||
| inline char* GenerateDigits(char buf[16], unsigned val, unsigned maxval) | |||
| { | |||
| assert(val <= maxval); | |||
| constexpr unsigned kBase = 64; // avoid integer division. | |||
| unsigned p = 15; | |||
| buf[p--] = 0; | |||
| while (maxval > 0) | |||
| { | |||
| buf[p--] = ' ' + (val % kBase); | |||
| val /= kBase; | |||
| maxval /= kBase; | |||
| } | |||
| return buf + p + 1; | |||
| } | |||
| template<typename K> | |||
| struct Generator | |||
| { | |||
| int maxval; | |||
| explicit Generator(int m) : | |||
| maxval(m) | |||
| { | |||
| } | |||
| K operator()(int i) const | |||
| { | |||
| assert(i <= maxval); | |||
| return K(i); | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<absl::Time> | |||
| { | |||
| int maxval; | |||
| explicit Generator(int m) : | |||
| maxval(m) | |||
| { | |||
| } | |||
| absl::Time operator()(int i) const | |||
| { | |||
| return absl::FromUnixMillis(i); | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<std::string> | |||
| { | |||
| int maxval; | |||
| explicit Generator(int m) : | |||
| maxval(m) | |||
| { | |||
| } | |||
| std::string operator()(int i) const | |||
| { | |||
| char buf[16]; | |||
| return GenerateDigits(buf, i, maxval); | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<Cord> | |||
| { | |||
| int maxval; | |||
| explicit Generator(int m) : | |||
| maxval(m) | |||
| { | |||
| } | |||
| Cord operator()(int i) const | |||
| { | |||
| char buf[16]; | |||
| return Cord(GenerateDigits(buf, i, maxval)); | |||
| } | |||
| }; | |||
| template<typename T, typename U> | |||
| struct Generator<std::pair<T, U>> | |||
| { | |||
| Generator<typename remove_pair_const<T>::type> tgen; | |||
| Generator<typename remove_pair_const<U>::type> ugen; | |||
| explicit Generator(int m) : | |||
| tgen(m), | |||
| ugen(m) | |||
| { | |||
| } | |||
| std::pair<T, U> operator()(int i) const | |||
| { | |||
| return std::make_pair(tgen(i), ugen(i)); | |||
| } | |||
| }; | |||
| // Generate n values for our tests and benchmarks. Value range is [0, maxval]. | |||
| inline std::vector<int> GenerateNumbersWithSeed(int n, int maxval, int seed) | |||
| { | |||
| // NOTE: Some tests rely on generated numbers not changing between test runs. | |||
| // We use std::minstd_rand0 because it is well-defined, but don't use | |||
| // std::uniform_int_distribution because platforms use different algorithms. | |||
| std::minstd_rand0 rng(seed); | |||
| std::vector<int> values; | |||
| absl::flat_hash_set<int> unique_values; | |||
| if (values.size() < n) | |||
| { | |||
| for (int i = values.size(); i < n; i++) | |||
| { | |||
| int value; | |||
| do | |||
| { | |||
| value = static_cast<int>(rng()) % (maxval + 1); | |||
| } while (!unique_values.insert(value).second); | |||
| values.push_back(value); | |||
| } | |||
| } | |||
| return values; | |||
| } | |||
| // Generates n values in the range [0, maxval]. | |||
| template<typename V> | |||
| std::vector<V> GenerateValuesWithSeed(int n, int maxval, int seed) | |||
| { | |||
| const std::vector<int> nums = GenerateNumbersWithSeed(n, maxval, seed); | |||
| Generator<V> gen(maxval); | |||
| std::vector<V> vec; | |||
| vec.reserve(n); | |||
| for (int i = 0; i < n; i++) | |||
| { | |||
| vec.push_back(gen(nums[i])); | |||
| } | |||
| return vec; | |||
| } | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_BTREE_TEST_H_ | |||
| @@ -0,0 +1,653 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: fixed_array.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // A `FixedArray<T>` represents a non-resizable array of `T` where the length of | |||
| // the array can be determined at run-time. It is a good replacement for | |||
| // non-standard and deprecated uses of `alloca()` and variable length arrays | |||
| // within the GCC extension. (See | |||
| // https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html). | |||
| // | |||
| // `FixedArray` allocates small arrays inline, keeping performance fast by | |||
| // avoiding heap operations. It also helps reduce the chances of | |||
| // accidentally overflowing your stack if large input is passed to | |||
| // your function. | |||
| #ifndef ABSL_CONTAINER_FIXED_ARRAY_H_ | |||
| #define ABSL_CONTAINER_FIXED_ARRAY_H_ | |||
| #include <algorithm> | |||
| #include <cassert> | |||
| #include <cstddef> | |||
| #include <initializer_list> | |||
| #include <iterator> | |||
| #include <limits> | |||
| #include <memory> | |||
| #include <new> | |||
| #include <type_traits> | |||
| #include "absl/algorithm/algorithm.h" | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/dynamic_annotations.h" | |||
| #include "absl/base/internal/throw_delegate.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/base/port.h" | |||
| #include "absl/container/internal/compressed_tuple.h" | |||
| #include "absl/memory/memory.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| constexpr static auto kFixedArrayUseDefault = static_cast<size_t>(-1); | |||
| // ----------------------------------------------------------------------------- | |||
| // FixedArray | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // A `FixedArray` provides a run-time fixed-size array, allocating a small array | |||
| // inline for efficiency. | |||
| // | |||
| // Most users should not specify an `inline_elements` argument and let | |||
| // `FixedArray` automatically determine the number of elements | |||
| // to store inline based on `sizeof(T)`. If `inline_elements` is specified, the | |||
| // `FixedArray` implementation will use inline storage for arrays with a | |||
| // length <= `inline_elements`. | |||
| // | |||
| // Note that a `FixedArray` constructed with a `size_type` argument will | |||
| // default-initialize its values by leaving trivially constructible types | |||
| // uninitialized (e.g. int, int[4], double), and others default-constructed. | |||
| // This matches the behavior of c-style arrays and `std::array`, but not | |||
| // `std::vector`. | |||
| template<typename T, size_t N = kFixedArrayUseDefault, typename A = std::allocator<T>> | |||
| class FixedArray | |||
| { | |||
| static_assert(!std::is_array<T>::value || std::extent<T>::value > 0, "Arrays with unknown bounds cannot be used with FixedArray."); | |||
| static constexpr size_t kInlineBytesDefault = 256; | |||
| using AllocatorTraits = std::allocator_traits<A>; | |||
| // std::iterator_traits isn't guaranteed to be SFINAE-friendly until C++17, | |||
| // but this seems to be mostly pedantic. | |||
| template<typename Iterator> | |||
| using EnableIfForwardIterator = absl::enable_if_t<std::is_convertible< | |||
| typename std::iterator_traits<Iterator>::iterator_category, | |||
| std::forward_iterator_tag>::value>; | |||
| static constexpr bool NoexceptCopyable() | |||
| { | |||
| return std::is_nothrow_copy_constructible<StorageElement>::value && | |||
| absl::allocator_is_nothrow<allocator_type>::value; | |||
| } | |||
| static constexpr bool NoexceptMovable() | |||
| { | |||
| return std::is_nothrow_move_constructible<StorageElement>::value && | |||
| absl::allocator_is_nothrow<allocator_type>::value; | |||
| } | |||
| static constexpr bool DefaultConstructorIsNonTrivial() | |||
| { | |||
| return !absl::is_trivially_default_constructible<StorageElement>::value; | |||
| } | |||
| public: | |||
| using allocator_type = typename AllocatorTraits::allocator_type; | |||
| using value_type = typename AllocatorTraits::value_type; | |||
| using pointer = typename AllocatorTraits::pointer; | |||
| using const_pointer = typename AllocatorTraits::const_pointer; | |||
| using reference = value_type&; | |||
| using const_reference = const value_type&; | |||
| using size_type = typename AllocatorTraits::size_type; | |||
| using difference_type = typename AllocatorTraits::difference_type; | |||
| using iterator = pointer; | |||
| using const_iterator = const_pointer; | |||
| using reverse_iterator = std::reverse_iterator<iterator>; | |||
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |||
| static constexpr size_type inline_elements = | |||
| (N == kFixedArrayUseDefault ? kInlineBytesDefault / sizeof(value_type) : static_cast<size_type>(N)); | |||
| FixedArray( | |||
| const FixedArray& other, | |||
| const allocator_type& a = allocator_type() | |||
| ) noexcept(NoexceptCopyable()) : | |||
| FixedArray(other.begin(), other.end(), a) | |||
| { | |||
| } | |||
| FixedArray( | |||
| FixedArray&& other, | |||
| const allocator_type& a = allocator_type() | |||
| ) noexcept(NoexceptMovable()) : | |||
| FixedArray(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), a) | |||
| { | |||
| } | |||
| // Creates an array object that can store `n` elements. | |||
| // Note that trivially constructible elements will be uninitialized. | |||
| explicit FixedArray(size_type n, const allocator_type& a = allocator_type()) : | |||
| storage_(n, a) | |||
| { | |||
| if (DefaultConstructorIsNonTrivial()) | |||
| { | |||
| memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), storage_.end()); | |||
| } | |||
| } | |||
| // Creates an array initialized with `n` copies of `val`. | |||
| FixedArray(size_type n, const value_type& val, const allocator_type& a = allocator_type()) : | |||
| storage_(n, a) | |||
| { | |||
| memory_internal::ConstructRange(storage_.alloc(), storage_.begin(), storage_.end(), val); | |||
| } | |||
| // Creates an array initialized with the size and contents of `init_list`. | |||
| FixedArray(std::initializer_list<value_type> init_list, const allocator_type& a = allocator_type()) : | |||
| FixedArray(init_list.begin(), init_list.end(), a) | |||
| { | |||
| } | |||
| // Creates an array initialized with the elements from the input | |||
| // range. The array's size will always be `std::distance(first, last)`. | |||
| // REQUIRES: Iterator must be a forward_iterator or better. | |||
| template<typename Iterator, EnableIfForwardIterator<Iterator>* = nullptr> | |||
| FixedArray(Iterator first, Iterator last, const allocator_type& a = allocator_type()) : | |||
| storage_(std::distance(first, last), a) | |||
| { | |||
| memory_internal::CopyRange(storage_.alloc(), storage_.begin(), first, last); | |||
| } | |||
| ~FixedArray() noexcept | |||
| { | |||
| for (auto* cur = storage_.begin(); cur != storage_.end(); ++cur) | |||
| { | |||
| AllocatorTraits::destroy(storage_.alloc(), cur); | |||
| } | |||
| } | |||
| // Assignments are deleted because they break the invariant that the size of a | |||
| // `FixedArray` never changes. | |||
| void operator=(FixedArray&&) = delete; | |||
| void operator=(const FixedArray&) = delete; | |||
| // FixedArray::size() | |||
| // | |||
| // Returns the length of the fixed array. | |||
| size_type size() const | |||
| { | |||
| return storage_.size(); | |||
| } | |||
| // FixedArray::max_size() | |||
| // | |||
| // Returns the largest possible value of `std::distance(begin(), end())` for a | |||
| // `FixedArray<T>`. This is equivalent to the most possible addressable bytes | |||
| // over the number of bytes taken by T. | |||
| constexpr size_type max_size() const | |||
| { | |||
| return (std::numeric_limits<difference_type>::max)() / sizeof(value_type); | |||
| } | |||
| // FixedArray::empty() | |||
| // | |||
| // Returns whether or not the fixed array is empty. | |||
| bool empty() const | |||
| { | |||
| return size() == 0; | |||
| } | |||
| // FixedArray::memsize() | |||
| // | |||
| // Returns the memory size of the fixed array in bytes. | |||
| size_t memsize() const | |||
| { | |||
| return size() * sizeof(value_type); | |||
| } | |||
| // FixedArray::data() | |||
| // | |||
| // Returns a const T* pointer to elements of the `FixedArray`. This pointer | |||
| // can be used to access (but not modify) the contained elements. | |||
| const_pointer data() const | |||
| { | |||
| return AsValueType(storage_.begin()); | |||
| } | |||
| // Overload of FixedArray::data() to return a T* pointer to elements of the | |||
| // fixed array. This pointer can be used to access and modify the contained | |||
| // elements. | |||
| pointer data() | |||
| { | |||
| return AsValueType(storage_.begin()); | |||
| } | |||
| // FixedArray::operator[] | |||
| // | |||
| // Returns a reference the ith element of the fixed array. | |||
| // REQUIRES: 0 <= i < size() | |||
| reference operator[](size_type i) | |||
| { | |||
| ABSL_HARDENING_ASSERT(i < size()); | |||
| return data()[i]; | |||
| } | |||
| // Overload of FixedArray::operator()[] to return a const reference to the | |||
| // ith element of the fixed array. | |||
| // REQUIRES: 0 <= i < size() | |||
| const_reference operator[](size_type i) const | |||
| { | |||
| ABSL_HARDENING_ASSERT(i < size()); | |||
| return data()[i]; | |||
| } | |||
| // FixedArray::at | |||
| // | |||
| // Bounds-checked access. Returns a reference to the ith element of the fixed | |||
| // array, or throws std::out_of_range | |||
| reference at(size_type i) | |||
| { | |||
| if (ABSL_PREDICT_FALSE(i >= size())) | |||
| { | |||
| base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); | |||
| } | |||
| return data()[i]; | |||
| } | |||
| // Overload of FixedArray::at() to return a const reference to the ith element | |||
| // of the fixed array. | |||
| const_reference at(size_type i) const | |||
| { | |||
| if (ABSL_PREDICT_FALSE(i >= size())) | |||
| { | |||
| base_internal::ThrowStdOutOfRange("FixedArray::at failed bounds check"); | |||
| } | |||
| return data()[i]; | |||
| } | |||
| // FixedArray::front() | |||
| // | |||
| // Returns a reference to the first element of the fixed array. | |||
| reference front() | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[0]; | |||
| } | |||
| // Overload of FixedArray::front() to return a reference to the first element | |||
| // of a fixed array of const values. | |||
| const_reference front() const | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[0]; | |||
| } | |||
| // FixedArray::back() | |||
| // | |||
| // Returns a reference to the last element of the fixed array. | |||
| reference back() | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[size() - 1]; | |||
| } | |||
| // Overload of FixedArray::back() to return a reference to the last element | |||
| // of a fixed array of const values. | |||
| const_reference back() const | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[size() - 1]; | |||
| } | |||
| // FixedArray::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the fixed array. | |||
| iterator begin() | |||
| { | |||
| return data(); | |||
| } | |||
| // Overload of FixedArray::begin() to return a const iterator to the | |||
| // beginning of the fixed array. | |||
| const_iterator begin() const | |||
| { | |||
| return data(); | |||
| } | |||
| // FixedArray::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the fixed array. | |||
| const_iterator cbegin() const | |||
| { | |||
| return begin(); | |||
| } | |||
| // FixedArray::end() | |||
| // | |||
| // Returns an iterator to the end of the fixed array. | |||
| iterator end() | |||
| { | |||
| return data() + size(); | |||
| } | |||
| // Overload of FixedArray::end() to return a const iterator to the end of the | |||
| // fixed array. | |||
| const_iterator end() const | |||
| { | |||
| return data() + size(); | |||
| } | |||
| // FixedArray::cend() | |||
| // | |||
| // Returns a const iterator to the end of the fixed array. | |||
| const_iterator cend() const | |||
| { | |||
| return end(); | |||
| } | |||
| // FixedArray::rbegin() | |||
| // | |||
| // Returns a reverse iterator from the end of the fixed array. | |||
| reverse_iterator rbegin() | |||
| { | |||
| return reverse_iterator(end()); | |||
| } | |||
| // Overload of FixedArray::rbegin() to return a const reverse iterator from | |||
| // the end of the fixed array. | |||
| const_reverse_iterator rbegin() const | |||
| { | |||
| return const_reverse_iterator(end()); | |||
| } | |||
| // FixedArray::crbegin() | |||
| // | |||
| // Returns a const reverse iterator from the end of the fixed array. | |||
| const_reverse_iterator crbegin() const | |||
| { | |||
| return rbegin(); | |||
| } | |||
| // FixedArray::rend() | |||
| // | |||
| // Returns a reverse iterator from the beginning of the fixed array. | |||
| reverse_iterator rend() | |||
| { | |||
| return reverse_iterator(begin()); | |||
| } | |||
| // Overload of FixedArray::rend() for returning a const reverse iterator | |||
| // from the beginning of the fixed array. | |||
| const_reverse_iterator rend() const | |||
| { | |||
| return const_reverse_iterator(begin()); | |||
| } | |||
| // FixedArray::crend() | |||
| // | |||
| // Returns a reverse iterator from the beginning of the fixed array. | |||
| const_reverse_iterator crend() const | |||
| { | |||
| return rend(); | |||
| } | |||
| // FixedArray::fill() | |||
| // | |||
| // Assigns the given `value` to all elements in the fixed array. | |||
| void fill(const value_type& val) | |||
| { | |||
| std::fill(begin(), end(), val); | |||
| } | |||
| // Relational operators. Equality operators are elementwise using | |||
| // `operator==`, while order operators order FixedArrays lexicographically. | |||
| friend bool operator==(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return absl::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); | |||
| } | |||
| friend bool operator!=(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return !(lhs == rhs); | |||
| } | |||
| friend bool operator<(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); | |||
| } | |||
| friend bool operator>(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return rhs < lhs; | |||
| } | |||
| friend bool operator<=(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return !(rhs < lhs); | |||
| } | |||
| friend bool operator>=(const FixedArray& lhs, const FixedArray& rhs) | |||
| { | |||
| return !(lhs < rhs); | |||
| } | |||
| template<typename H> | |||
| friend H AbslHashValue(H h, const FixedArray& v) | |||
| { | |||
| return H::combine(H::combine_contiguous(std::move(h), v.data(), v.size()), v.size()); | |||
| } | |||
| private: | |||
| // StorageElement | |||
| // | |||
| // For FixedArrays with a C-style-array value_type, StorageElement is a POD | |||
| // wrapper struct called StorageElementWrapper that holds the value_type | |||
| // instance inside. This is needed for construction and destruction of the | |||
| // entire array regardless of how many dimensions it has. For all other cases, | |||
| // StorageElement is just an alias of value_type. | |||
| // | |||
| // Maintainer's Note: The simpler solution would be to simply wrap value_type | |||
| // in a struct whether it's an array or not. That causes some paranoid | |||
| // diagnostics to misfire, believing that 'data()' returns a pointer to a | |||
| // single element, rather than the packed array that it really is. | |||
| // e.g.: | |||
| // | |||
| // FixedArray<char> buf(1); | |||
| // sprintf(buf.data(), "foo"); | |||
| // | |||
| // error: call to int __builtin___sprintf_chk(etc...) | |||
| // will always overflow destination buffer [-Werror] | |||
| // | |||
| template<typename OuterT, typename InnerT = absl::remove_extent_t<OuterT>, size_t InnerN = std::extent<OuterT>::value> | |||
| struct StorageElementWrapper | |||
| { | |||
| InnerT array[InnerN]; | |||
| }; | |||
| using StorageElement = | |||
| absl::conditional_t<std::is_array<value_type>::value, StorageElementWrapper<value_type>, value_type>; | |||
| static pointer AsValueType(pointer ptr) | |||
| { | |||
| return ptr; | |||
| } | |||
| static pointer AsValueType(StorageElementWrapper<value_type>* ptr) | |||
| { | |||
| return std::addressof(ptr->array); | |||
| } | |||
| static_assert(sizeof(StorageElement) == sizeof(value_type), ""); | |||
| static_assert(alignof(StorageElement) == alignof(value_type), ""); | |||
| class NonEmptyInlinedStorage | |||
| { | |||
| public: | |||
| StorageElement* data() | |||
| { | |||
| return reinterpret_cast<StorageElement*>(buff_); | |||
| } | |||
| void AnnotateConstruct(size_type n); | |||
| void AnnotateDestruct(size_type n); | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| void* RedzoneBegin() | |||
| { | |||
| return &redzone_begin_; | |||
| } | |||
| void* RedzoneEnd() | |||
| { | |||
| return &redzone_end_ + 1; | |||
| } | |||
| #endif // ABSL_HAVE_ADDRESS_SANITIZER | |||
| private: | |||
| ABSL_ADDRESS_SANITIZER_REDZONE(redzone_begin_); | |||
| alignas(StorageElement) char buff_[sizeof(StorageElement[inline_elements])]; | |||
| ABSL_ADDRESS_SANITIZER_REDZONE(redzone_end_); | |||
| }; | |||
| class EmptyInlinedStorage | |||
| { | |||
| public: | |||
| StorageElement* data() | |||
| { | |||
| return nullptr; | |||
| } | |||
| void AnnotateConstruct(size_type) | |||
| { | |||
| } | |||
| void AnnotateDestruct(size_type) | |||
| { | |||
| } | |||
| }; | |||
| using InlinedStorage = | |||
| absl::conditional_t<inline_elements == 0, EmptyInlinedStorage, NonEmptyInlinedStorage>; | |||
| // Storage | |||
| // | |||
| // An instance of Storage manages the inline and out-of-line memory for | |||
| // instances of FixedArray. This guarantees that even when construction of | |||
| // individual elements fails in the FixedArray constructor body, the | |||
| // destructor for Storage will still be called and out-of-line memory will be | |||
| // properly deallocated. | |||
| // | |||
| class Storage : public InlinedStorage | |||
| { | |||
| public: | |||
| Storage(size_type n, const allocator_type& a) : | |||
| size_alloc_(n, a), | |||
| data_(InitializeData()) | |||
| { | |||
| } | |||
| ~Storage() noexcept | |||
| { | |||
| if (UsingInlinedStorage(size())) | |||
| { | |||
| InlinedStorage::AnnotateDestruct(size()); | |||
| } | |||
| else | |||
| { | |||
| AllocatorTraits::deallocate(alloc(), AsValueType(begin()), size()); | |||
| } | |||
| } | |||
| size_type size() const | |||
| { | |||
| return size_alloc_.template get<0>(); | |||
| } | |||
| StorageElement* begin() const | |||
| { | |||
| return data_; | |||
| } | |||
| StorageElement* end() const | |||
| { | |||
| return begin() + size(); | |||
| } | |||
| allocator_type& alloc() | |||
| { | |||
| return size_alloc_.template get<1>(); | |||
| } | |||
| private: | |||
| static bool UsingInlinedStorage(size_type n) | |||
| { | |||
| return n <= inline_elements; | |||
| } | |||
| StorageElement* InitializeData() | |||
| { | |||
| if (UsingInlinedStorage(size())) | |||
| { | |||
| InlinedStorage::AnnotateConstruct(size()); | |||
| return InlinedStorage::data(); | |||
| } | |||
| else | |||
| { | |||
| return reinterpret_cast<StorageElement*>( | |||
| AllocatorTraits::allocate(alloc(), size()) | |||
| ); | |||
| } | |||
| } | |||
| // `CompressedTuple` takes advantage of EBCO for stateless `allocator_type`s | |||
| container_internal::CompressedTuple<size_type, allocator_type> size_alloc_; | |||
| StorageElement* data_; | |||
| }; | |||
| Storage storage_; | |||
| }; | |||
| #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL | |||
| template<typename T, size_t N, typename A> | |||
| constexpr size_t FixedArray<T, N, A>::kInlineBytesDefault; | |||
| template<typename T, size_t N, typename A> | |||
| constexpr typename FixedArray<T, N, A>::size_type | |||
| FixedArray<T, N, A>::inline_elements; | |||
| #endif | |||
| template<typename T, size_t N, typename A> | |||
| void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateConstruct( | |||
| typename FixedArray<T, N, A>::size_type n | |||
| ) | |||
| { | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| if (!n) | |||
| return; | |||
| ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), RedzoneEnd(), data() + n); | |||
| ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), data(), RedzoneBegin()); | |||
| #endif // ABSL_HAVE_ADDRESS_SANITIZER | |||
| static_cast<void>(n); // Mark used when not in asan mode | |||
| } | |||
| template<typename T, size_t N, typename A> | |||
| void FixedArray<T, N, A>::NonEmptyInlinedStorage::AnnotateDestruct( | |||
| typename FixedArray<T, N, A>::size_type n | |||
| ) | |||
| { | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| if (!n) | |||
| return; | |||
| ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(data(), RedzoneEnd(), data() + n, RedzoneEnd()); | |||
| ABSL_ANNOTATE_CONTIGUOUS_CONTAINER(RedzoneBegin(), data(), RedzoneBegin(), data()); | |||
| #endif // ABSL_HAVE_ADDRESS_SANITIZER | |||
| static_cast<void>(n); // Mark used when not in asan mode | |||
| } | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_FIXED_ARRAY_H_ | |||
| @@ -0,0 +1,634 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: flat_hash_map.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // An `absl::flat_hash_map<K, V>` is an unordered associative container of | |||
| // unique keys and associated values designed to be a more efficient replacement | |||
| // for `std::unordered_map`. Like `unordered_map`, search, insertion, and | |||
| // deletion of map elements can be done as an `O(1)` operation. However, | |||
| // `flat_hash_map` (and other unordered associative containers known as the | |||
| // collection of Abseil "Swiss tables") contain other optimizations that result | |||
| // in both memory and computation advantages. | |||
| // | |||
| // In most cases, your default choice for a hash map should be a map of type | |||
| // `flat_hash_map`. | |||
| #ifndef ABSL_CONTAINER_FLAT_HASH_MAP_H_ | |||
| #define ABSL_CONTAINER_FLAT_HASH_MAP_H_ | |||
| #include <cstddef> | |||
| #include <new> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/algorithm/container.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/container/internal/container_memory.h" | |||
| #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export | |||
| #include "absl/container/internal/raw_hash_map.h" // IWYU pragma: export | |||
| #include "absl/memory/memory.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class K, class V> | |||
| struct FlatHashMapPolicy; | |||
| } // namespace container_internal | |||
| // ----------------------------------------------------------------------------- | |||
| // absl::flat_hash_map | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // An `absl::flat_hash_map<K, V>` is an unordered associative container which | |||
| // has been optimized for both speed and memory footprint in most common use | |||
| // cases. Its interface is similar to that of `std::unordered_map<K, V>` with | |||
| // the following notable differences: | |||
| // | |||
| // * Requires keys that are CopyConstructible | |||
| // * Requires values that are MoveConstructible | |||
| // * Supports heterogeneous lookup, through `find()`, `operator[]()` and | |||
| // `insert()`, provided that the map is provided a compatible heterogeneous | |||
| // hashing function and equality operator. | |||
| // * Invalidates any references and pointers to elements within the table after | |||
| // `rehash()`. | |||
| // * Contains a `capacity()` member function indicating the number of element | |||
| // slots (open, deleted, and empty) within the hash map. | |||
| // * Returns `void` from the `erase(iterator)` overload. | |||
| // | |||
| // By default, `flat_hash_map` uses the `absl::Hash` hashing framework. | |||
| // All fundamental and Abseil types that support the `absl::Hash` framework have | |||
| // a compatible equality operator for comparing insertions into `flat_hash_map`. | |||
| // If your type is not yet supported by the `absl::Hash` framework, see | |||
| // absl/hash/hash.h for information on extending Abseil hashing to user-defined | |||
| // types. | |||
| // | |||
| // Using `absl::flat_hash_map` at interface boundaries in dynamically loaded | |||
| // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may | |||
| // be randomized across dynamically loaded libraries. | |||
| // | |||
| // NOTE: A `flat_hash_map` stores its value types directly inside its | |||
| // implementation array to avoid memory indirection. Because a `flat_hash_map` | |||
| // is designed to move data when rehashed, map values will not retain pointer | |||
| // stability. If you require pointer stability, or if your values are large, | |||
| // consider using `absl::flat_hash_map<Key, std::unique_ptr<Value>>` instead. | |||
| // If your types are not moveable or you require pointer stability for keys, | |||
| // consider `absl::node_hash_map`. | |||
| // | |||
| // Example: | |||
| // | |||
| // // Create a flat hash map of three strings (that map to strings) | |||
| // absl::flat_hash_map<std::string, std::string> ducks = | |||
| // {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; | |||
| // | |||
| // // Insert a new element into the flat hash map | |||
| // ducks.insert({"d", "donald"}); | |||
| // | |||
| // // Force a rehash of the flat hash map | |||
| // ducks.rehash(0); | |||
| // | |||
| // // Find the element with the key "b" | |||
| // std::string search_key = "b"; | |||
| // auto result = ducks.find(search_key); | |||
| // if (result != ducks.end()) { | |||
| // std::cout << "Result: " << result->second << std::endl; | |||
| // } | |||
| template<class K, class V, class Hash = absl::container_internal::hash_default_hash<K>, class Eq = absl::container_internal::hash_default_eq<K>, class Allocator = std::allocator<std::pair<const K, V>>> | |||
| class flat_hash_map : public absl::container_internal::raw_hash_map<absl::container_internal::FlatHashMapPolicy<K, V>, Hash, Eq, Allocator> | |||
| { | |||
| using Base = typename flat_hash_map::raw_hash_map; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A flat_hash_map supports the same overload set as `std::unordered_map` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // // No allocation for the table's elements is made. | |||
| // absl::flat_hash_map<int, std::string> map1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::flat_hash_map<int, std::string> map2 = | |||
| // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::flat_hash_map<int, std::string> map3(map2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // // Hash functor and Comparator are copied as well | |||
| // absl::flat_hash_map<int, std::string> map4; | |||
| // map4 = map3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::flat_hash_map<int, std::string> map5(std::move(map4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::flat_hash_map<int, std::string> map6; | |||
| // map6 = std::move(map5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::pair<int, std::string>> v = {{1, "a"}, {2, "b"}}; | |||
| // absl::flat_hash_map<int, std::string> map7(v.begin(), v.end()); | |||
| flat_hash_map() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // flat_hash_map::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `flat_hash_map`. | |||
| using Base::begin; | |||
| // flat_hash_map::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `flat_hash_map`. | |||
| using Base::cbegin; | |||
| // flat_hash_map::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `flat_hash_map`. | |||
| using Base::cend; | |||
| // flat_hash_map::end() | |||
| // | |||
| // Returns an iterator to the end of the `flat_hash_map`. | |||
| using Base::end; | |||
| // flat_hash_map::capacity() | |||
| // | |||
| // Returns the number of element slots (assigned, deleted, and empty) | |||
| // available within the `flat_hash_map`. | |||
| // | |||
| // NOTE: this member function is particular to `absl::flat_hash_map` and is | |||
| // not provided in the `std::unordered_map` API. | |||
| using Base::capacity; | |||
| // flat_hash_map::empty() | |||
| // | |||
| // Returns whether or not the `flat_hash_map` is empty. | |||
| using Base::empty; | |||
| // flat_hash_map::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `flat_hash_map` under current memory constraints. This value can be thought | |||
| // of the largest value of `std::distance(begin(), end())` for a | |||
| // `flat_hash_map<K, V>`. | |||
| using Base::max_size; | |||
| // flat_hash_map::size() | |||
| // | |||
| // Returns the number of elements currently within the `flat_hash_map`. | |||
| using Base::size; | |||
| // flat_hash_map::clear() | |||
| // | |||
| // Removes all elements from the `flat_hash_map`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| // | |||
| // NOTE: this operation may shrink the underlying buffer. To avoid shrinking | |||
| // the underlying buffer call `erase(begin(), end())`. | |||
| using Base::clear; | |||
| // flat_hash_map::erase() | |||
| // | |||
| // Erases elements within the `flat_hash_map`. Erasing does not trigger a | |||
| // rehash. Overloads are listed below. | |||
| // | |||
| // void erase(const_iterator pos): | |||
| // | |||
| // Erases the element at `position` of the `flat_hash_map`, returning | |||
| // `void`. | |||
| // | |||
| // NOTE: returning `void` in this case is different than that of STL | |||
| // containers in general and `std::unordered_map` in particular (which | |||
| // return an iterator to the element following the erased element). If that | |||
| // iterator is needed, simply post increment the iterator: | |||
| // | |||
| // map.erase(it++); | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning an | |||
| // iterator pointing to `last`. | |||
| // | |||
| // size_type erase(const key_type& key): | |||
| // | |||
| // Erases the element with the matching key, if it exists, returning the | |||
| // number of elements erased (0 or 1). | |||
| using Base::erase; | |||
| // flat_hash_map::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `flat_hash_map`, | |||
| // returning an iterator pointing to the newly inserted element, provided that | |||
| // an element with the given key does not already exist. If rehashing occurs | |||
| // due to the insertion, all iterators are invalidated. Overloads are listed | |||
| // below. | |||
| // | |||
| // std::pair<iterator,bool> insert(const init_type& value): | |||
| // | |||
| // Inserts a value into the `flat_hash_map`. Returns a pair consisting of an | |||
| // iterator to the inserted element (or to the element that prevented the | |||
| // insertion) and a bool denoting whether the insertion took place. | |||
| // | |||
| // std::pair<iterator,bool> insert(T&& value): | |||
| // std::pair<iterator,bool> insert(init_type&& value): | |||
| // | |||
| // Inserts a moveable value into the `flat_hash_map`. Returns a pair | |||
| // consisting of an iterator to the inserted element (or to the element that | |||
| // prevented the insertion) and a bool denoting whether the insertion took | |||
| // place. | |||
| // | |||
| // iterator insert(const_iterator hint, const init_type& value): | |||
| // iterator insert(const_iterator hint, T&& value): | |||
| // iterator insert(const_iterator hint, init_type&& value); | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element, or to the existing element that prevented the | |||
| // insertion. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // NOTE: Although the STL does not specify which element may be inserted if | |||
| // multiple keys compare equivalently, for `flat_hash_map` we guarantee the | |||
| // first match is inserted. | |||
| // | |||
| // void insert(std::initializer_list<init_type> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| // | |||
| // NOTE: Although the STL does not specify which element may be inserted if | |||
| // multiple keys compare equivalently within the initializer list, for | |||
| // `flat_hash_map` we guarantee the first match is inserted. | |||
| using Base::insert; | |||
| // flat_hash_map::insert_or_assign() | |||
| // | |||
| // Inserts an element of the specified value into the `flat_hash_map` provided | |||
| // that a value with the given key does not already exist, or replaces it with | |||
| // the element value if a key for that value already exists, returning an | |||
| // iterator pointing to the newly inserted element. If rehashing occurs due | |||
| // to the insertion, all existing iterators are invalidated. Overloads are | |||
| // listed below. | |||
| // | |||
| // pair<iterator, bool> insert_or_assign(const init_type& k, T&& obj): | |||
| // pair<iterator, bool> insert_or_assign(init_type&& k, T&& obj): | |||
| // | |||
| // Inserts/Assigns (or moves) the element of the specified key into the | |||
| // `flat_hash_map`. | |||
| // | |||
| // iterator insert_or_assign(const_iterator hint, | |||
| // const init_type& k, T&& obj): | |||
| // iterator insert_or_assign(const_iterator hint, init_type&& k, T&& obj): | |||
| // | |||
| // Inserts/Assigns (or moves) the element of the specified key into the | |||
| // `flat_hash_map` using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. | |||
| using Base::insert_or_assign; | |||
| // flat_hash_map::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `flat_hash_map`, provided that no element with the given key | |||
| // already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. Prefer `try_emplace()` unless your key is not | |||
| // copyable or moveable. | |||
| // | |||
| // If rehashing occurs due to the insertion, all iterators are invalidated. | |||
| using Base::emplace; | |||
| // flat_hash_map::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `flat_hash_map`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search, and only inserts | |||
| // provided that no element with the given key already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. Prefer `try_emplace()` unless your key is not | |||
| // copyable or moveable. | |||
| // | |||
| // If rehashing occurs due to the insertion, all iterators are invalidated. | |||
| using Base::emplace_hint; | |||
| // flat_hash_map::try_emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `flat_hash_map`, provided that no element with the given key | |||
| // already exists. Unlike `emplace()`, if an element with the given key | |||
| // already exists, we guarantee that no element is constructed. | |||
| // | |||
| // If rehashing occurs due to the insertion, all iterators are invalidated. | |||
| // Overloads are listed below. | |||
| // | |||
| // pair<iterator, bool> try_emplace(const key_type& k, Args&&... args): | |||
| // pair<iterator, bool> try_emplace(key_type&& k, Args&&... args): | |||
| // | |||
| // Inserts (via copy or move) the element of the specified key into the | |||
| // `flat_hash_map`. | |||
| // | |||
| // iterator try_emplace(const_iterator hint, | |||
| // const key_type& k, Args&&... args): | |||
| // iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args): | |||
| // | |||
| // Inserts (via copy or move) the element of the specified key into the | |||
| // `flat_hash_map` using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. | |||
| // | |||
| // All `try_emplace()` overloads make the same guarantees regarding rvalue | |||
| // arguments as `std::unordered_map::try_emplace()`, namely that these | |||
| // functions will not move from rvalue arguments if insertions do not happen. | |||
| using Base::try_emplace; | |||
| // flat_hash_map::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the key,value pair of the element at the indicated position and | |||
| // returns a node handle owning that extracted data. | |||
| // | |||
| // node_type extract(const key_type& x): | |||
| // | |||
| // Extracts the key,value pair of the element with a key matching the passed | |||
| // key value and returns a node handle owning that extracted data. If the | |||
| // `flat_hash_map` does not contain an element with a matching key, this | |||
| // function returns an empty node handle. | |||
| // | |||
| // NOTE: when compiled in an earlier version of C++ than C++17, | |||
| // `node_type::key()` returns a const reference to the key instead of a | |||
| // mutable reference. We cannot safely return a mutable reference without | |||
| // std::launder (which is not available before C++17). | |||
| using Base::extract; | |||
| // flat_hash_map::merge() | |||
| // | |||
| // Extracts elements from a given `source` flat hash map into this | |||
| // `flat_hash_map`. If the destination `flat_hash_map` already contains an | |||
| // element with an equivalent key, that element is not extracted. | |||
| using Base::merge; | |||
| // flat_hash_map::swap(flat_hash_map& other) | |||
| // | |||
| // Exchanges the contents of this `flat_hash_map` with those of the `other` | |||
| // flat hash map, avoiding invocation of any move, copy, or swap operations on | |||
| // individual elements. | |||
| // | |||
| // All iterators and references on the `flat_hash_map` remain valid, excepting | |||
| // for the past-the-end iterator, which is invalidated. | |||
| // | |||
| // `swap()` requires that the flat hash map's hashing and key equivalence | |||
| // functions be Swappable, and are exchanged using unqualified calls to | |||
| // non-member `swap()`. If the map's allocator has | |||
| // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value` | |||
| // set to `true`, the allocators are also exchanged using an unqualified call | |||
| // to non-member `swap()`; otherwise, the allocators are not swapped. | |||
| using Base::swap; | |||
| // flat_hash_map::rehash(count) | |||
| // | |||
| // Rehashes the `flat_hash_map`, setting the number of slots to be at least | |||
| // the passed value. If the new number of slots increases the load factor more | |||
| // than the current maximum load factor | |||
| // (`count` < `size()` / `max_load_factor()`), then the new number of slots | |||
| // will be at least `size()` / `max_load_factor()`. | |||
| // | |||
| // To force a rehash, pass rehash(0). | |||
| // | |||
| // NOTE: unlike behavior in `std::unordered_map`, references are also | |||
| // invalidated upon a `rehash()`. | |||
| using Base::rehash; | |||
| // flat_hash_map::reserve(count) | |||
| // | |||
| // Sets the number of slots in the `flat_hash_map` to the number needed to | |||
| // accommodate at least `count` total elements without exceeding the current | |||
| // maximum load factor, and may rehash the container if needed. | |||
| using Base::reserve; | |||
| // flat_hash_map::at() | |||
| // | |||
| // Returns a reference to the mapped value of the element with key equivalent | |||
| // to the passed key. | |||
| using Base::at; | |||
| // flat_hash_map::contains() | |||
| // | |||
| // Determines whether an element with a key comparing equal to the given `key` | |||
| // exists within the `flat_hash_map`, returning `true` if so or `false` | |||
| // otherwise. | |||
| using Base::contains; | |||
| // flat_hash_map::count(const Key& key) const | |||
| // | |||
| // Returns the number of elements with a key comparing equal to the given | |||
| // `key` within the `flat_hash_map`. note that this function will return | |||
| // either `1` or `0` since duplicate keys are not allowed within a | |||
| // `flat_hash_map`. | |||
| using Base::count; | |||
| // flat_hash_map::equal_range() | |||
| // | |||
| // Returns a closed range [first, last], defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the | |||
| // `flat_hash_map`. | |||
| using Base::equal_range; | |||
| // flat_hash_map::find() | |||
| // | |||
| // Finds an element with the passed `key` within the `flat_hash_map`. | |||
| using Base::find; | |||
| // flat_hash_map::operator[]() | |||
| // | |||
| // Returns a reference to the value mapped to the passed key within the | |||
| // `flat_hash_map`, performing an `insert()` if the key does not already | |||
| // exist. | |||
| // | |||
| // If an insertion occurs and results in a rehashing of the container, all | |||
| // iterators are invalidated. Otherwise iterators are not affected and | |||
| // references are not invalidated. Overloads are listed below. | |||
| // | |||
| // T& operator[](const Key& key): | |||
| // | |||
| // Inserts an init_type object constructed in-place if the element with the | |||
| // given key does not exist. | |||
| // | |||
| // T& operator[](Key&& key): | |||
| // | |||
| // Inserts an init_type object constructed in-place provided that an element | |||
| // with the given key does not exist. | |||
| using Base::operator[]; | |||
| // flat_hash_map::bucket_count() | |||
| // | |||
| // Returns the number of "buckets" within the `flat_hash_map`. Note that | |||
| // because a flat hash map contains all elements within its internal storage, | |||
| // this value simply equals the current capacity of the `flat_hash_map`. | |||
| using Base::bucket_count; | |||
| // flat_hash_map::load_factor() | |||
| // | |||
| // Returns the current load factor of the `flat_hash_map` (the average number | |||
| // of slots occupied with a value within the hash map). | |||
| using Base::load_factor; | |||
| // flat_hash_map::max_load_factor() | |||
| // | |||
| // Manages the maximum load factor of the `flat_hash_map`. Overloads are | |||
| // listed below. | |||
| // | |||
| // float flat_hash_map::max_load_factor() | |||
| // | |||
| // Returns the current maximum load factor of the `flat_hash_map`. | |||
| // | |||
| // void flat_hash_map::max_load_factor(float ml) | |||
| // | |||
| // Sets the maximum load factor of the `flat_hash_map` to the passed value. | |||
| // | |||
| // NOTE: This overload is provided only for API compatibility with the STL; | |||
| // `flat_hash_map` will ignore any set load factor and manage its rehashing | |||
| // internally as an implementation detail. | |||
| using Base::max_load_factor; | |||
| // flat_hash_map::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `flat_hash_map`. | |||
| using Base::get_allocator; | |||
| // flat_hash_map::hash_function() | |||
| // | |||
| // Returns the hashing function used to hash the keys within this | |||
| // `flat_hash_map`. | |||
| using Base::hash_function; | |||
| // flat_hash_map::key_eq() | |||
| // | |||
| // Returns the function used for comparing keys equality. | |||
| using Base::key_eq; | |||
| }; | |||
| // erase_if(flat_hash_map<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate `pred` from the container `c`. | |||
| // Returns the number of erased elements. | |||
| template<typename K, typename V, typename H, typename E, typename A, typename Predicate> | |||
| typename flat_hash_map<K, V, H, E, A>::size_type erase_if( | |||
| flat_hash_map<K, V, H, E, A>& c, Predicate pred | |||
| ) | |||
| { | |||
| return container_internal::EraseIf(pred, &c); | |||
| } | |||
| namespace container_internal | |||
| { | |||
| template<class K, class V> | |||
| struct FlatHashMapPolicy | |||
| { | |||
| using slot_policy = container_internal::map_slot_policy<K, V>; | |||
| using slot_type = typename slot_policy::slot_type; | |||
| using key_type = K; | |||
| using mapped_type = V; | |||
| using init_type = std::pair</*non const*/ key_type, mapped_type>; | |||
| template<class Allocator, class... Args> | |||
| static void construct(Allocator* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| slot_policy::construct(alloc, slot, std::forward<Args>(args)...); | |||
| } | |||
| template<class Allocator> | |||
| static void destroy(Allocator* alloc, slot_type* slot) | |||
| { | |||
| slot_policy::destroy(alloc, slot); | |||
| } | |||
| template<class Allocator> | |||
| static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| slot_policy::transfer(alloc, new_slot, old_slot); | |||
| } | |||
| template<class F, class... Args> | |||
| static decltype(absl::container_internal::DecomposePair( | |||
| std::declval<F>(), std::declval<Args>()... | |||
| )) | |||
| apply(F&& f, Args&&... args) | |||
| { | |||
| return absl::container_internal::DecomposePair(std::forward<F>(f), std::forward<Args>(args)...); | |||
| } | |||
| static size_t space_used(const slot_type*) | |||
| { | |||
| return 0; | |||
| } | |||
| static std::pair<const K, V>& element(slot_type* slot) | |||
| { | |||
| return slot->value; | |||
| } | |||
| static V& value(std::pair<const K, V>* kv) | |||
| { | |||
| return kv->second; | |||
| } | |||
| static const V& value(const std::pair<const K, V>* kv) | |||
| { | |||
| return kv->second; | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| namespace container_algorithm_internal | |||
| { | |||
| // Specialization of trait in absl/algorithm/container.h | |||
| template<class Key, class T, class Hash, class KeyEqual, class Allocator> | |||
| struct IsUnorderedContainer< | |||
| absl::flat_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type | |||
| { | |||
| }; | |||
| } // namespace container_algorithm_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_FLAT_HASH_MAP_H_ | |||
| @@ -0,0 +1,527 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: flat_hash_set.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // An `absl::flat_hash_set<T>` is an unordered associative container designed to | |||
| // be a more efficient replacement for `std::unordered_set`. Like | |||
| // `unordered_set`, search, insertion, and deletion of set elements can be done | |||
| // as an `O(1)` operation. However, `flat_hash_set` (and other unordered | |||
| // associative containers known as the collection of Abseil "Swiss tables") | |||
| // contain other optimizations that result in both memory and computation | |||
| // advantages. | |||
| // | |||
| // In most cases, your default choice for a hash set should be a set of type | |||
| // `flat_hash_set`. | |||
| #ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ | |||
| #define ABSL_CONTAINER_FLAT_HASH_SET_H_ | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/algorithm/container.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/container/internal/container_memory.h" | |||
| #include "absl/container/internal/hash_function_defaults.h" // IWYU pragma: export | |||
| #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export | |||
| #include "absl/memory/memory.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<typename T> | |||
| struct FlatHashSetPolicy; | |||
| } // namespace container_internal | |||
| // ----------------------------------------------------------------------------- | |||
| // absl::flat_hash_set | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // An `absl::flat_hash_set<T>` is an unordered associative container which has | |||
| // been optimized for both speed and memory footprint in most common use cases. | |||
| // Its interface is similar to that of `std::unordered_set<T>` with the | |||
| // following notable differences: | |||
| // | |||
| // * Requires keys that are CopyConstructible | |||
| // * Supports heterogeneous lookup, through `find()` and `insert()`, provided | |||
| // that the set is provided a compatible heterogeneous hashing function and | |||
| // equality operator. | |||
| // * Invalidates any references and pointers to elements within the table after | |||
| // `rehash()`. | |||
| // * Contains a `capacity()` member function indicating the number of element | |||
| // slots (open, deleted, and empty) within the hash set. | |||
| // * Returns `void` from the `erase(iterator)` overload. | |||
| // | |||
| // By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All | |||
| // fundamental and Abseil types that support the `absl::Hash` framework have a | |||
| // compatible equality operator for comparing insertions into `flat_hash_set`. | |||
| // If your type is not yet supported by the `absl::Hash` framework, see | |||
| // absl/hash/hash.h for information on extending Abseil hashing to user-defined | |||
| // types. | |||
| // | |||
| // Using `absl::flat_hash_set` at interface boundaries in dynamically loaded | |||
| // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may | |||
| // be randomized across dynamically loaded libraries. | |||
| // | |||
| // NOTE: A `flat_hash_set` stores its keys directly inside its implementation | |||
| // array to avoid memory indirection. Because a `flat_hash_set` is designed to | |||
| // move data when rehashed, set keys will not retain pointer stability. If you | |||
| // require pointer stability, consider using | |||
| // `absl::flat_hash_set<std::unique_ptr<T>>`. If your type is not moveable and | |||
| // you require pointer stability, consider `absl::node_hash_set` instead. | |||
| // | |||
| // Example: | |||
| // | |||
| // // Create a flat hash set of three strings | |||
| // absl::flat_hash_set<std::string> ducks = | |||
| // {"huey", "dewey", "louie"}; | |||
| // | |||
| // // Insert a new element into the flat hash set | |||
| // ducks.insert("donald"); | |||
| // | |||
| // // Force a rehash of the flat hash set | |||
| // ducks.rehash(0); | |||
| // | |||
| // // See if "dewey" is present | |||
| // if (ducks.contains("dewey")) { | |||
| // std::cout << "We found dewey!" << std::endl; | |||
| // } | |||
| template<class T, class Hash = absl::container_internal::hash_default_hash<T>, class Eq = absl::container_internal::hash_default_eq<T>, class Allocator = std::allocator<T>> | |||
| class flat_hash_set : public absl::container_internal::raw_hash_set<absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> | |||
| { | |||
| using Base = typename flat_hash_set::raw_hash_set; | |||
| public: | |||
| // Constructors and Assignment Operators | |||
| // | |||
| // A flat_hash_set supports the same overload set as `std::unordered_set` | |||
| // for construction and assignment: | |||
| // | |||
| // * Default constructor | |||
| // | |||
| // // No allocation for the table's elements is made. | |||
| // absl::flat_hash_set<std::string> set1; | |||
| // | |||
| // * Initializer List constructor | |||
| // | |||
| // absl::flat_hash_set<std::string> set2 = | |||
| // {{"huey"}, {"dewey"}, {"louie"},}; | |||
| // | |||
| // * Copy constructor | |||
| // | |||
| // absl::flat_hash_set<std::string> set3(set2); | |||
| // | |||
| // * Copy assignment operator | |||
| // | |||
| // // Hash functor and Comparator are copied as well | |||
| // absl::flat_hash_set<std::string> set4; | |||
| // set4 = set3; | |||
| // | |||
| // * Move constructor | |||
| // | |||
| // // Move is guaranteed efficient | |||
| // absl::flat_hash_set<std::string> set5(std::move(set4)); | |||
| // | |||
| // * Move assignment operator | |||
| // | |||
| // // May be efficient if allocators are compatible | |||
| // absl::flat_hash_set<std::string> set6; | |||
| // set6 = std::move(set5); | |||
| // | |||
| // * Range constructor | |||
| // | |||
| // std::vector<std::string> v = {"a", "b"}; | |||
| // absl::flat_hash_set<std::string> set7(v.begin(), v.end()); | |||
| flat_hash_set() | |||
| { | |||
| } | |||
| using Base::Base; | |||
| // flat_hash_set::begin() | |||
| // | |||
| // Returns an iterator to the beginning of the `flat_hash_set`. | |||
| using Base::begin; | |||
| // flat_hash_set::cbegin() | |||
| // | |||
| // Returns a const iterator to the beginning of the `flat_hash_set`. | |||
| using Base::cbegin; | |||
| // flat_hash_set::cend() | |||
| // | |||
| // Returns a const iterator to the end of the `flat_hash_set`. | |||
| using Base::cend; | |||
| // flat_hash_set::end() | |||
| // | |||
| // Returns an iterator to the end of the `flat_hash_set`. | |||
| using Base::end; | |||
| // flat_hash_set::capacity() | |||
| // | |||
| // Returns the number of element slots (assigned, deleted, and empty) | |||
| // available within the `flat_hash_set`. | |||
| // | |||
| // NOTE: this member function is particular to `absl::flat_hash_set` and is | |||
| // not provided in the `std::unordered_set` API. | |||
| using Base::capacity; | |||
| // flat_hash_set::empty() | |||
| // | |||
| // Returns whether or not the `flat_hash_set` is empty. | |||
| using Base::empty; | |||
| // flat_hash_set::max_size() | |||
| // | |||
| // Returns the largest theoretical possible number of elements within a | |||
| // `flat_hash_set` under current memory constraints. This value can be thought | |||
| // of the largest value of `std::distance(begin(), end())` for a | |||
| // `flat_hash_set<T>`. | |||
| using Base::max_size; | |||
| // flat_hash_set::size() | |||
| // | |||
| // Returns the number of elements currently within the `flat_hash_set`. | |||
| using Base::size; | |||
| // flat_hash_set::clear() | |||
| // | |||
| // Removes all elements from the `flat_hash_set`. Invalidates any references, | |||
| // pointers, or iterators referring to contained elements. | |||
| // | |||
| // NOTE: this operation may shrink the underlying buffer. To avoid shrinking | |||
| // the underlying buffer call `erase(begin(), end())`. | |||
| using Base::clear; | |||
| // flat_hash_set::erase() | |||
| // | |||
| // Erases elements within the `flat_hash_set`. Erasing does not trigger a | |||
| // rehash. Overloads are listed below. | |||
| // | |||
| // void erase(const_iterator pos): | |||
| // | |||
| // Erases the element at `position` of the `flat_hash_set`, returning | |||
| // `void`. | |||
| // | |||
| // NOTE: returning `void` in this case is different than that of STL | |||
| // containers in general and `std::unordered_set` in particular (which | |||
| // return an iterator to the element following the erased element). If that | |||
| // iterator is needed, simply post increment the iterator: | |||
| // | |||
| // set.erase(it++); | |||
| // | |||
| // iterator erase(const_iterator first, const_iterator last): | |||
| // | |||
| // Erases the elements in the open interval [`first`, `last`), returning an | |||
| // iterator pointing to `last`. | |||
| // | |||
| // size_type erase(const key_type& key): | |||
| // | |||
| // Erases the element with the matching key, if it exists, returning the | |||
| // number of elements erased (0 or 1). | |||
| using Base::erase; | |||
| // flat_hash_set::insert() | |||
| // | |||
| // Inserts an element of the specified value into the `flat_hash_set`, | |||
| // returning an iterator pointing to the newly inserted element, provided that | |||
| // an element with the given key does not already exist. If rehashing occurs | |||
| // due to the insertion, all iterators are invalidated. Overloads are listed | |||
| // below. | |||
| // | |||
| // std::pair<iterator,bool> insert(const T& value): | |||
| // | |||
| // Inserts a value into the `flat_hash_set`. Returns a pair consisting of an | |||
| // iterator to the inserted element (or to the element that prevented the | |||
| // insertion) and a bool denoting whether the insertion took place. | |||
| // | |||
| // std::pair<iterator,bool> insert(T&& value): | |||
| // | |||
| // Inserts a moveable value into the `flat_hash_set`. Returns a pair | |||
| // consisting of an iterator to the inserted element (or to the element that | |||
| // prevented the insertion) and a bool denoting whether the insertion took | |||
| // place. | |||
| // | |||
| // iterator insert(const_iterator hint, const T& value): | |||
| // iterator insert(const_iterator hint, T&& value): | |||
| // | |||
| // Inserts a value, using the position of `hint` as a non-binding suggestion | |||
| // for where to begin the insertion search. Returns an iterator to the | |||
| // inserted element, or to the existing element that prevented the | |||
| // insertion. | |||
| // | |||
| // void insert(InputIterator first, InputIterator last): | |||
| // | |||
| // Inserts a range of values [`first`, `last`). | |||
| // | |||
| // NOTE: Although the STL does not specify which element may be inserted if | |||
| // multiple keys compare equivalently, for `flat_hash_set` we guarantee the | |||
| // first match is inserted. | |||
| // | |||
| // void insert(std::initializer_list<T> ilist): | |||
| // | |||
| // Inserts the elements within the initializer list `ilist`. | |||
| // | |||
| // NOTE: Although the STL does not specify which element may be inserted if | |||
| // multiple keys compare equivalently within the initializer list, for | |||
| // `flat_hash_set` we guarantee the first match is inserted. | |||
| using Base::insert; | |||
| // flat_hash_set::emplace() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `flat_hash_set`, provided that no element with the given key | |||
| // already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. | |||
| // | |||
| // If rehashing occurs due to the insertion, all iterators are invalidated. | |||
| using Base::emplace; | |||
| // flat_hash_set::emplace_hint() | |||
| // | |||
| // Inserts an element of the specified value by constructing it in-place | |||
| // within the `flat_hash_set`, using the position of `hint` as a non-binding | |||
| // suggestion for where to begin the insertion search, and only inserts | |||
| // provided that no element with the given key already exists. | |||
| // | |||
| // The element may be constructed even if there already is an element with the | |||
| // key in the container, in which case the newly constructed element will be | |||
| // destroyed immediately. | |||
| // | |||
| // If rehashing occurs due to the insertion, all iterators are invalidated. | |||
| using Base::emplace_hint; | |||
| // flat_hash_set::extract() | |||
| // | |||
| // Extracts the indicated element, erasing it in the process, and returns it | |||
| // as a C++17-compatible node handle. Overloads are listed below. | |||
| // | |||
| // node_type extract(const_iterator position): | |||
| // | |||
| // Extracts the element at the indicated position and returns a node handle | |||
| // owning that extracted data. | |||
| // | |||
| // node_type extract(const key_type& x): | |||
| // | |||
| // Extracts the element with the key matching the passed key value and | |||
| // returns a node handle owning that extracted data. If the `flat_hash_set` | |||
| // does not contain an element with a matching key, this function returns an | |||
| // empty node handle. | |||
| using Base::extract; | |||
| // flat_hash_set::merge() | |||
| // | |||
| // Extracts elements from a given `source` flat hash set into this | |||
| // `flat_hash_set`. If the destination `flat_hash_set` already contains an | |||
| // element with an equivalent key, that element is not extracted. | |||
| using Base::merge; | |||
| // flat_hash_set::swap(flat_hash_set& other) | |||
| // | |||
| // Exchanges the contents of this `flat_hash_set` with those of the `other` | |||
| // flat hash set, avoiding invocation of any move, copy, or swap operations on | |||
| // individual elements. | |||
| // | |||
| // All iterators and references on the `flat_hash_set` remain valid, excepting | |||
| // for the past-the-end iterator, which is invalidated. | |||
| // | |||
| // `swap()` requires that the flat hash set's hashing and key equivalence | |||
| // functions be Swappable, and are exchaged using unqualified calls to | |||
| // non-member `swap()`. If the set's allocator has | |||
| // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value` | |||
| // set to `true`, the allocators are also exchanged using an unqualified call | |||
| // to non-member `swap()`; otherwise, the allocators are not swapped. | |||
| using Base::swap; | |||
| // flat_hash_set::rehash(count) | |||
| // | |||
| // Rehashes the `flat_hash_set`, setting the number of slots to be at least | |||
| // the passed value. If the new number of slots increases the load factor more | |||
| // than the current maximum load factor | |||
| // (`count` < `size()` / `max_load_factor()`), then the new number of slots | |||
| // will be at least `size()` / `max_load_factor()`. | |||
| // | |||
| // To force a rehash, pass rehash(0). | |||
| // | |||
| // NOTE: unlike behavior in `std::unordered_set`, references are also | |||
| // invalidated upon a `rehash()`. | |||
| using Base::rehash; | |||
| // flat_hash_set::reserve(count) | |||
| // | |||
| // Sets the number of slots in the `flat_hash_set` to the number needed to | |||
| // accommodate at least `count` total elements without exceeding the current | |||
| // maximum load factor, and may rehash the container if needed. | |||
| using Base::reserve; | |||
| // flat_hash_set::contains() | |||
| // | |||
| // Determines whether an element comparing equal to the given `key` exists | |||
| // within the `flat_hash_set`, returning `true` if so or `false` otherwise. | |||
| using Base::contains; | |||
| // flat_hash_set::count(const Key& key) const | |||
| // | |||
| // Returns the number of elements comparing equal to the given `key` within | |||
| // the `flat_hash_set`. note that this function will return either `1` or `0` | |||
| // since duplicate elements are not allowed within a `flat_hash_set`. | |||
| using Base::count; | |||
| // flat_hash_set::equal_range() | |||
| // | |||
| // Returns a closed range [first, last], defined by a `std::pair` of two | |||
| // iterators, containing all elements with the passed key in the | |||
| // `flat_hash_set`. | |||
| using Base::equal_range; | |||
| // flat_hash_set::find() | |||
| // | |||
| // Finds an element with the passed `key` within the `flat_hash_set`. | |||
| using Base::find; | |||
| // flat_hash_set::bucket_count() | |||
| // | |||
| // Returns the number of "buckets" within the `flat_hash_set`. Note that | |||
| // because a flat hash set contains all elements within its internal storage, | |||
| // this value simply equals the current capacity of the `flat_hash_set`. | |||
| using Base::bucket_count; | |||
| // flat_hash_set::load_factor() | |||
| // | |||
| // Returns the current load factor of the `flat_hash_set` (the average number | |||
| // of slots occupied with a value within the hash set). | |||
| using Base::load_factor; | |||
| // flat_hash_set::max_load_factor() | |||
| // | |||
| // Manages the maximum load factor of the `flat_hash_set`. Overloads are | |||
| // listed below. | |||
| // | |||
| // float flat_hash_set::max_load_factor() | |||
| // | |||
| // Returns the current maximum load factor of the `flat_hash_set`. | |||
| // | |||
| // void flat_hash_set::max_load_factor(float ml) | |||
| // | |||
| // Sets the maximum load factor of the `flat_hash_set` to the passed value. | |||
| // | |||
| // NOTE: This overload is provided only for API compatibility with the STL; | |||
| // `flat_hash_set` will ignore any set load factor and manage its rehashing | |||
| // internally as an implementation detail. | |||
| using Base::max_load_factor; | |||
| // flat_hash_set::get_allocator() | |||
| // | |||
| // Returns the allocator function associated with this `flat_hash_set`. | |||
| using Base::get_allocator; | |||
| // flat_hash_set::hash_function() | |||
| // | |||
| // Returns the hashing function used to hash the keys within this | |||
| // `flat_hash_set`. | |||
| using Base::hash_function; | |||
| // flat_hash_set::key_eq() | |||
| // | |||
| // Returns the function used for comparing keys equality. | |||
| using Base::key_eq; | |||
| }; | |||
| // erase_if(flat_hash_set<>, Pred) | |||
| // | |||
| // Erases all elements that satisfy the predicate `pred` from the container `c`. | |||
| // Returns the number of erased elements. | |||
| template<typename T, typename H, typename E, typename A, typename Predicate> | |||
| typename flat_hash_set<T, H, E, A>::size_type erase_if( | |||
| flat_hash_set<T, H, E, A>& c, Predicate pred | |||
| ) | |||
| { | |||
| return container_internal::EraseIf(pred, &c); | |||
| } | |||
| namespace container_internal | |||
| { | |||
| template<class T> | |||
| struct FlatHashSetPolicy | |||
| { | |||
| using slot_type = T; | |||
| using key_type = T; | |||
| using init_type = T; | |||
| using constant_iterators = std::true_type; | |||
| template<class Allocator, class... Args> | |||
| static void construct(Allocator* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| absl::allocator_traits<Allocator>::construct(*alloc, slot, std::forward<Args>(args)...); | |||
| } | |||
| template<class Allocator> | |||
| static void destroy(Allocator* alloc, slot_type* slot) | |||
| { | |||
| absl::allocator_traits<Allocator>::destroy(*alloc, slot); | |||
| } | |||
| template<class Allocator> | |||
| static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| construct(alloc, new_slot, std::move(*old_slot)); | |||
| destroy(alloc, old_slot); | |||
| } | |||
| static T& element(slot_type* slot) | |||
| { | |||
| return *slot; | |||
| } | |||
| template<class F, class... Args> | |||
| static decltype(absl::container_internal::DecomposeValue( | |||
| std::declval<F>(), std::declval<Args>()... | |||
| )) | |||
| apply(F&& f, Args&&... args) | |||
| { | |||
| return absl::container_internal::DecomposeValue( | |||
| std::forward<F>(f), std::forward<Args>(args)... | |||
| ); | |||
| } | |||
| static size_t space_used(const T*) | |||
| { | |||
| return 0; | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| namespace container_algorithm_internal | |||
| { | |||
| // Specialization of trait in absl/algorithm/container.h | |||
| template<class Key, class Hash, class KeyEqual, class Allocator> | |||
| struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> : std::true_type | |||
| { | |||
| }; | |||
| } // namespace container_algorithm_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_FLAT_HASH_SET_H_ | |||
| @@ -0,0 +1,984 @@ | |||
| // Copyright 2019 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: inlined_vector.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file contains the declaration and definition of an "inlined | |||
| // vector" which behaves in an equivalent fashion to a `std::vector`, except | |||
| // that storage for small sequences of the vector are provided inline without | |||
| // requiring any heap allocation. | |||
| // | |||
| // An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of | |||
| // its template parameters. Instances where `size() <= N` hold contained | |||
| // elements in inline space. Typically `N` is very small so that sequences that | |||
| // are expected to be short do not require allocations. | |||
| // | |||
| // An `absl::InlinedVector` does not usually require a specific allocator. If | |||
| // the inlined vector grows beyond its initial constraints, it will need to | |||
| // allocate (as any normal `std::vector` would). This is usually performed with | |||
| // the default allocator (defined as `std::allocator<T>`). Optionally, a custom | |||
| // allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`. | |||
| #ifndef ABSL_CONTAINER_INLINED_VECTOR_H_ | |||
| #define ABSL_CONTAINER_INLINED_VECTOR_H_ | |||
| #include <algorithm> | |||
| #include <cstddef> | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| #include <initializer_list> | |||
| #include <iterator> | |||
| #include <memory> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/algorithm/algorithm.h" | |||
| #include "absl/base/internal/throw_delegate.h" | |||
| #include "absl/base/macros.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/base/port.h" | |||
| #include "absl/container/internal/inlined_vector.h" | |||
| #include "absl/memory/memory.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| // ----------------------------------------------------------------------------- | |||
| // InlinedVector | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // An `absl::InlinedVector` is designed to be a drop-in replacement for | |||
| // `std::vector` for use cases where the vector's size is sufficiently small | |||
| // that it can be inlined. If the inlined vector does grow beyond its estimated | |||
| // capacity, it will trigger an initial allocation on the heap, and will behave | |||
| // as a `std::vector`. The API of the `absl::InlinedVector` within this file is | |||
| // designed to cover the same API footprint as covered by `std::vector`. | |||
| template<typename T, size_t N, typename A = std::allocator<T>> | |||
| class InlinedVector | |||
| { | |||
| static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); | |||
| using Storage = inlined_vector_internal::Storage<T, N, A>; | |||
| template<typename TheA> | |||
| using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>; | |||
| template<typename TheA> | |||
| using MoveIterator = inlined_vector_internal::MoveIterator<TheA>; | |||
| template<typename TheA> | |||
| using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>; | |||
| template<typename TheA, typename Iterator> | |||
| using IteratorValueAdapter = | |||
| inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>; | |||
| template<typename TheA> | |||
| using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>; | |||
| template<typename TheA> | |||
| using DefaultValueAdapter = | |||
| inlined_vector_internal::DefaultValueAdapter<TheA>; | |||
| template<typename Iterator> | |||
| using EnableIfAtLeastForwardIterator = absl::enable_if_t< | |||
| inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, | |||
| int>; | |||
| template<typename Iterator> | |||
| using DisableIfAtLeastForwardIterator = absl::enable_if_t< | |||
| !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, | |||
| int>; | |||
| public: | |||
| using allocator_type = A; | |||
| using value_type = inlined_vector_internal::ValueType<A>; | |||
| using pointer = inlined_vector_internal::Pointer<A>; | |||
| using const_pointer = inlined_vector_internal::ConstPointer<A>; | |||
| using size_type = inlined_vector_internal::SizeType<A>; | |||
| using difference_type = inlined_vector_internal::DifferenceType<A>; | |||
| using reference = inlined_vector_internal::Reference<A>; | |||
| using const_reference = inlined_vector_internal::ConstReference<A>; | |||
| using iterator = inlined_vector_internal::Iterator<A>; | |||
| using const_iterator = inlined_vector_internal::ConstIterator<A>; | |||
| using reverse_iterator = inlined_vector_internal::ReverseIterator<A>; | |||
| using const_reverse_iterator = | |||
| inlined_vector_internal::ConstReverseIterator<A>; | |||
| // --------------------------------------------------------------------------- | |||
| // InlinedVector Constructors and Destructor | |||
| // --------------------------------------------------------------------------- | |||
| // Creates an empty inlined vector with a value-initialized allocator. | |||
| InlinedVector() noexcept(noexcept(allocator_type())) : | |||
| storage_() | |||
| { | |||
| } | |||
| // Creates an empty inlined vector with a copy of `allocator`. | |||
| explicit InlinedVector(const allocator_type& allocator) noexcept | |||
| : | |||
| storage_(allocator) | |||
| { | |||
| } | |||
| // Creates an inlined vector with `n` copies of `value_type()`. | |||
| explicit InlinedVector(size_type n, const allocator_type& allocator = allocator_type()) : | |||
| storage_(allocator) | |||
| { | |||
| storage_.Initialize(DefaultValueAdapter<A>(), n); | |||
| } | |||
| // Creates an inlined vector with `n` copies of `v`. | |||
| InlinedVector(size_type n, const_reference v, const allocator_type& allocator = allocator_type()) : | |||
| storage_(allocator) | |||
| { | |||
| storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n); | |||
| } | |||
| // Creates an inlined vector with copies of the elements of `list`. | |||
| InlinedVector(std::initializer_list<value_type> list, const allocator_type& allocator = allocator_type()) : | |||
| InlinedVector(list.begin(), list.end(), allocator) | |||
| { | |||
| } | |||
| // Creates an inlined vector with elements constructed from the provided | |||
| // forward iterator range [`first`, `last`). | |||
| // | |||
| // NOTE: the `enable_if` prevents ambiguous interpretation between a call to | |||
| // this constructor with two integral arguments and a call to the above | |||
| // `InlinedVector(size_type, const_reference)` constructor. | |||
| template<typename ForwardIterator, EnableIfAtLeastForwardIterator<ForwardIterator> = 0> | |||
| InlinedVector(ForwardIterator first, ForwardIterator last, const allocator_type& allocator = allocator_type()) : | |||
| storage_(allocator) | |||
| { | |||
| storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first), static_cast<size_t>(std::distance(first, last))); | |||
| } | |||
| // Creates an inlined vector with elements constructed from the provided input | |||
| // iterator range [`first`, `last`). | |||
| template<typename InputIterator, DisableIfAtLeastForwardIterator<InputIterator> = 0> | |||
| InlinedVector(InputIterator first, InputIterator last, const allocator_type& allocator = allocator_type()) : | |||
| storage_(allocator) | |||
| { | |||
| std::copy(first, last, std::back_inserter(*this)); | |||
| } | |||
| // Creates an inlined vector by copying the contents of `other` using | |||
| // `other`'s allocator. | |||
| InlinedVector(const InlinedVector& other) : | |||
| InlinedVector(other, other.storage_.GetAllocator()) | |||
| { | |||
| } | |||
| // Creates an inlined vector by copying the contents of `other` using the | |||
| // provided `allocator`. | |||
| InlinedVector(const InlinedVector& other, const allocator_type& allocator) : | |||
| storage_(allocator) | |||
| { | |||
| if (other.empty()) | |||
| { | |||
| // Empty; nothing to do. | |||
| } | |||
| else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) | |||
| { | |||
| // Memcpy-able and do not need allocation. | |||
| storage_.MemcpyFrom(other.storage_); | |||
| } | |||
| else | |||
| { | |||
| storage_.InitFrom(other.storage_); | |||
| } | |||
| } | |||
| // Creates an inlined vector by moving in the contents of `other` without | |||
| // allocating. If `other` contains allocated memory, the newly-created inlined | |||
| // vector will take ownership of that memory. However, if `other` does not | |||
| // contain allocated memory, the newly-created inlined vector will perform | |||
| // element-wise move construction of the contents of `other`. | |||
| // | |||
| // NOTE: since no allocation is performed for the inlined vector in either | |||
| // case, the `noexcept(...)` specification depends on whether moving the | |||
| // underlying objects can throw. It is assumed assumed that... | |||
| // a) move constructors should only throw due to allocation failure. | |||
| // b) if `value_type`'s move constructor allocates, it uses the same | |||
| // allocation function as the inlined vector's allocator. | |||
| // Thus, the move constructor is non-throwing if the allocator is non-throwing | |||
| // or `value_type`'s move constructor is specified as `noexcept`. | |||
| InlinedVector(InlinedVector&& other) noexcept( | |||
| absl::allocator_is_nothrow<allocator_type>::value || | |||
| std::is_nothrow_move_constructible<value_type>::value | |||
| ) : | |||
| storage_(other.storage_.GetAllocator()) | |||
| { | |||
| if (IsMemcpyOk<A>::value) | |||
| { | |||
| storage_.MemcpyFrom(other.storage_); | |||
| other.storage_.SetInlinedSize(0); | |||
| } | |||
| else if (other.storage_.GetIsAllocated()) | |||
| { | |||
| storage_.SetAllocation({other.storage_.GetAllocatedData(), other.storage_.GetAllocatedCapacity()}); | |||
| storage_.SetAllocatedSize(other.storage_.GetSize()); | |||
| other.storage_.SetInlinedSize(0); | |||
| } | |||
| else | |||
| { | |||
| IteratorValueAdapter<A, MoveIterator<A>> other_values( | |||
| MoveIterator<A>(other.storage_.GetInlinedData()) | |||
| ); | |||
| inlined_vector_internal::ConstructElements<A>( | |||
| storage_.GetAllocator(), storage_.GetInlinedData(), other_values, other.storage_.GetSize() | |||
| ); | |||
| storage_.SetInlinedSize(other.storage_.GetSize()); | |||
| } | |||
| } | |||
| // Creates an inlined vector by moving in the contents of `other` with a copy | |||
| // of `allocator`. | |||
| // | |||
| // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other` | |||
| // contains allocated memory, this move constructor will still allocate. Since | |||
| // allocation is performed, this constructor can only be `noexcept` if the | |||
| // specified allocator is also `noexcept`. | |||
| InlinedVector( | |||
| InlinedVector&& other, | |||
| const allocator_type& | |||
| allocator | |||
| ) noexcept(absl::allocator_is_nothrow<allocator_type>::value) : | |||
| storage_(allocator) | |||
| { | |||
| if (IsMemcpyOk<A>::value) | |||
| { | |||
| storage_.MemcpyFrom(other.storage_); | |||
| other.storage_.SetInlinedSize(0); | |||
| } | |||
| else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) && other.storage_.GetIsAllocated()) | |||
| { | |||
| storage_.SetAllocation({other.storage_.GetAllocatedData(), other.storage_.GetAllocatedCapacity()}); | |||
| storage_.SetAllocatedSize(other.storage_.GetSize()); | |||
| other.storage_.SetInlinedSize(0); | |||
| } | |||
| else | |||
| { | |||
| storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(other.data())), other.size()); | |||
| } | |||
| } | |||
| ~InlinedVector() | |||
| { | |||
| } | |||
| // --------------------------------------------------------------------------- | |||
| // InlinedVector Member Accessors | |||
| // --------------------------------------------------------------------------- | |||
| // `InlinedVector::empty()` | |||
| // | |||
| // Returns whether the inlined vector contains no elements. | |||
| bool empty() const noexcept | |||
| { | |||
| return !size(); | |||
| } | |||
| // `InlinedVector::size()` | |||
| // | |||
| // Returns the number of elements in the inlined vector. | |||
| size_type size() const noexcept | |||
| { | |||
| return storage_.GetSize(); | |||
| } | |||
| // `InlinedVector::max_size()` | |||
| // | |||
| // Returns the maximum number of elements the inlined vector can hold. | |||
| size_type max_size() const noexcept | |||
| { | |||
| // One bit of the size storage is used to indicate whether the inlined | |||
| // vector contains allocated memory. As a result, the maximum size that the | |||
| // inlined vector can express is half of the max for `size_type`. | |||
| return (std::numeric_limits<size_type>::max)() / 2; | |||
| } | |||
| // `InlinedVector::capacity()` | |||
| // | |||
| // Returns the number of elements that could be stored in the inlined vector | |||
| // without requiring a reallocation. | |||
| // | |||
| // NOTE: for most inlined vectors, `capacity()` should be equal to the | |||
| // template parameter `N`. For inlined vectors which exceed this capacity, | |||
| // they will no longer be inlined and `capacity()` will equal the capactity of | |||
| // the allocated memory. | |||
| size_type capacity() const noexcept | |||
| { | |||
| return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity() : storage_.GetInlinedCapacity(); | |||
| } | |||
| // `InlinedVector::data()` | |||
| // | |||
| // Returns a `pointer` to the elements of the inlined vector. This pointer | |||
| // can be used to access and modify the contained elements. | |||
| // | |||
| // NOTE: only elements within [`data()`, `data() + size()`) are valid. | |||
| pointer data() noexcept | |||
| { | |||
| return storage_.GetIsAllocated() ? storage_.GetAllocatedData() : storage_.GetInlinedData(); | |||
| } | |||
| // Overload of `InlinedVector::data()` that returns a `const_pointer` to the | |||
| // elements of the inlined vector. This pointer can be used to access but not | |||
| // modify the contained elements. | |||
| // | |||
| // NOTE: only elements within [`data()`, `data() + size()`) are valid. | |||
| const_pointer data() const noexcept | |||
| { | |||
| return storage_.GetIsAllocated() ? storage_.GetAllocatedData() : storage_.GetInlinedData(); | |||
| } | |||
| // `InlinedVector::operator[](...)` | |||
| // | |||
| // Returns a `reference` to the `i`th element of the inlined vector. | |||
| reference operator[](size_type i) | |||
| { | |||
| ABSL_HARDENING_ASSERT(i < size()); | |||
| return data()[i]; | |||
| } | |||
| // Overload of `InlinedVector::operator[](...)` that returns a | |||
| // `const_reference` to the `i`th element of the inlined vector. | |||
| const_reference operator[](size_type i) const | |||
| { | |||
| ABSL_HARDENING_ASSERT(i < size()); | |||
| return data()[i]; | |||
| } | |||
| // `InlinedVector::at(...)` | |||
| // | |||
| // Returns a `reference` to the `i`th element of the inlined vector. | |||
| // | |||
| // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, | |||
| // in both debug and non-debug builds, `std::out_of_range` will be thrown. | |||
| reference at(size_type i) | |||
| { | |||
| if (ABSL_PREDICT_FALSE(i >= size())) | |||
| { | |||
| base_internal::ThrowStdOutOfRange( | |||
| "`InlinedVector::at(size_type)` failed bounds check" | |||
| ); | |||
| } | |||
| return data()[i]; | |||
| } | |||
| // Overload of `InlinedVector::at(...)` that returns a `const_reference` to | |||
| // the `i`th element of the inlined vector. | |||
| // | |||
| // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`, | |||
| // in both debug and non-debug builds, `std::out_of_range` will be thrown. | |||
| const_reference at(size_type i) const | |||
| { | |||
| if (ABSL_PREDICT_FALSE(i >= size())) | |||
| { | |||
| base_internal::ThrowStdOutOfRange( | |||
| "`InlinedVector::at(size_type) const` failed bounds check" | |||
| ); | |||
| } | |||
| return data()[i]; | |||
| } | |||
| // `InlinedVector::front()` | |||
| // | |||
| // Returns a `reference` to the first element of the inlined vector. | |||
| reference front() | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[0]; | |||
| } | |||
| // Overload of `InlinedVector::front()` that returns a `const_reference` to | |||
| // the first element of the inlined vector. | |||
| const_reference front() const | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[0]; | |||
| } | |||
| // `InlinedVector::back()` | |||
| // | |||
| // Returns a `reference` to the last element of the inlined vector. | |||
| reference back() | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[size() - 1]; | |||
| } | |||
| // Overload of `InlinedVector::back()` that returns a `const_reference` to the | |||
| // last element of the inlined vector. | |||
| const_reference back() const | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| return data()[size() - 1]; | |||
| } | |||
| // `InlinedVector::begin()` | |||
| // | |||
| // Returns an `iterator` to the beginning of the inlined vector. | |||
| iterator begin() noexcept | |||
| { | |||
| return data(); | |||
| } | |||
| // Overload of `InlinedVector::begin()` that returns a `const_iterator` to | |||
| // the beginning of the inlined vector. | |||
| const_iterator begin() const noexcept | |||
| { | |||
| return data(); | |||
| } | |||
| // `InlinedVector::end()` | |||
| // | |||
| // Returns an `iterator` to the end of the inlined vector. | |||
| iterator end() noexcept | |||
| { | |||
| return data() + size(); | |||
| } | |||
| // Overload of `InlinedVector::end()` that returns a `const_iterator` to the | |||
| // end of the inlined vector. | |||
| const_iterator end() const noexcept | |||
| { | |||
| return data() + size(); | |||
| } | |||
| // `InlinedVector::cbegin()` | |||
| // | |||
| // Returns a `const_iterator` to the beginning of the inlined vector. | |||
| const_iterator cbegin() const noexcept | |||
| { | |||
| return begin(); | |||
| } | |||
| // `InlinedVector::cend()` | |||
| // | |||
| // Returns a `const_iterator` to the end of the inlined vector. | |||
| const_iterator cend() const noexcept | |||
| { | |||
| return end(); | |||
| } | |||
| // `InlinedVector::rbegin()` | |||
| // | |||
| // Returns a `reverse_iterator` from the end of the inlined vector. | |||
| reverse_iterator rbegin() noexcept | |||
| { | |||
| return reverse_iterator(end()); | |||
| } | |||
| // Overload of `InlinedVector::rbegin()` that returns a | |||
| // `const_reverse_iterator` from the end of the inlined vector. | |||
| const_reverse_iterator rbegin() const noexcept | |||
| { | |||
| return const_reverse_iterator(end()); | |||
| } | |||
| // `InlinedVector::rend()` | |||
| // | |||
| // Returns a `reverse_iterator` from the beginning of the inlined vector. | |||
| reverse_iterator rend() noexcept | |||
| { | |||
| return reverse_iterator(begin()); | |||
| } | |||
| // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator` | |||
| // from the beginning of the inlined vector. | |||
| const_reverse_iterator rend() const noexcept | |||
| { | |||
| return const_reverse_iterator(begin()); | |||
| } | |||
| // `InlinedVector::crbegin()` | |||
| // | |||
| // Returns a `const_reverse_iterator` from the end of the inlined vector. | |||
| const_reverse_iterator crbegin() const noexcept | |||
| { | |||
| return rbegin(); | |||
| } | |||
| // `InlinedVector::crend()` | |||
| // | |||
| // Returns a `const_reverse_iterator` from the beginning of the inlined | |||
| // vector. | |||
| const_reverse_iterator crend() const noexcept | |||
| { | |||
| return rend(); | |||
| } | |||
| // `InlinedVector::get_allocator()` | |||
| // | |||
| // Returns a copy of the inlined vector's allocator. | |||
| allocator_type get_allocator() const | |||
| { | |||
| return storage_.GetAllocator(); | |||
| } | |||
| // --------------------------------------------------------------------------- | |||
| // InlinedVector Member Mutators | |||
| // --------------------------------------------------------------------------- | |||
| // `InlinedVector::operator=(...)` | |||
| // | |||
| // Replaces the elements of the inlined vector with copies of the elements of | |||
| // `list`. | |||
| InlinedVector& operator=(std::initializer_list<value_type> list) | |||
| { | |||
| assign(list.begin(), list.end()); | |||
| return *this; | |||
| } | |||
| // Overload of `InlinedVector::operator=(...)` that replaces the elements of | |||
| // the inlined vector with copies of the elements of `other`. | |||
| InlinedVector& operator=(const InlinedVector& other) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(this != std::addressof(other))) | |||
| { | |||
| const_pointer other_data = other.data(); | |||
| assign(other_data, other_data + other.size()); | |||
| } | |||
| return *this; | |||
| } | |||
| // Overload of `InlinedVector::operator=(...)` that moves the elements of | |||
| // `other` into the inlined vector. | |||
| // | |||
| // NOTE: as a result of calling this overload, `other` is left in a valid but | |||
| // unspecified state. | |||
| InlinedVector& operator=(InlinedVector&& other) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(this != std::addressof(other))) | |||
| { | |||
| if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) | |||
| { | |||
| inlined_vector_internal::DestroyAdapter<A>::DestroyElements( | |||
| storage_.GetAllocator(), data(), size() | |||
| ); | |||
| storage_.DeallocateIfAllocated(); | |||
| storage_.MemcpyFrom(other.storage_); | |||
| other.storage_.SetInlinedSize(0); | |||
| } | |||
| else | |||
| { | |||
| storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(other.storage_.GetInlinedData())), other.size()); | |||
| } | |||
| } | |||
| return *this; | |||
| } | |||
| // `InlinedVector::assign(...)` | |||
| // | |||
| // Replaces the contents of the inlined vector with `n` copies of `v`. | |||
| void assign(size_type n, const_reference v) | |||
| { | |||
| storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n); | |||
| } | |||
| // Overload of `InlinedVector::assign(...)` that replaces the contents of the | |||
| // inlined vector with copies of the elements of `list`. | |||
| void assign(std::initializer_list<value_type> list) | |||
| { | |||
| assign(list.begin(), list.end()); | |||
| } | |||
| // Overload of `InlinedVector::assign(...)` to replace the contents of the | |||
| // inlined vector with the range [`first`, `last`). | |||
| // | |||
| // NOTE: this overload is for iterators that are "forward" category or better. | |||
| template<typename ForwardIterator, EnableIfAtLeastForwardIterator<ForwardIterator> = 0> | |||
| void assign(ForwardIterator first, ForwardIterator last) | |||
| { | |||
| storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first), static_cast<size_t>(std::distance(first, last))); | |||
| } | |||
| // Overload of `InlinedVector::assign(...)` to replace the contents of the | |||
| // inlined vector with the range [`first`, `last`). | |||
| // | |||
| // NOTE: this overload is for iterators that are "input" category. | |||
| template<typename InputIterator, DisableIfAtLeastForwardIterator<InputIterator> = 0> | |||
| void assign(InputIterator first, InputIterator last) | |||
| { | |||
| size_type i = 0; | |||
| for (; i < size() && first != last; ++i, static_cast<void>(++first)) | |||
| { | |||
| data()[i] = *first; | |||
| } | |||
| erase(data() + i, data() + size()); | |||
| std::copy(first, last, std::back_inserter(*this)); | |||
| } | |||
| // `InlinedVector::resize(...)` | |||
| // | |||
| // Resizes the inlined vector to contain `n` elements. | |||
| // | |||
| // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n` | |||
| // is larger than `size()`, new elements are value-initialized. | |||
| void resize(size_type n) | |||
| { | |||
| ABSL_HARDENING_ASSERT(n <= max_size()); | |||
| storage_.Resize(DefaultValueAdapter<A>(), n); | |||
| } | |||
| // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to | |||
| // contain `n` elements. | |||
| // | |||
| // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n` | |||
| // is larger than `size()`, new elements are copied-constructed from `v`. | |||
| void resize(size_type n, const_reference v) | |||
| { | |||
| ABSL_HARDENING_ASSERT(n <= max_size()); | |||
| storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n); | |||
| } | |||
| // `InlinedVector::insert(...)` | |||
| // | |||
| // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly | |||
| // inserted element. | |||
| iterator insert(const_iterator pos, const_reference v) | |||
| { | |||
| return emplace(pos, v); | |||
| } | |||
| // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using | |||
| // move semantics, returning an `iterator` to the newly inserted element. | |||
| iterator insert(const_iterator pos, value_type&& v) | |||
| { | |||
| return emplace(pos, std::move(v)); | |||
| } | |||
| // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies | |||
| // of `v` starting at `pos`, returning an `iterator` pointing to the first of | |||
| // the newly inserted elements. | |||
| iterator insert(const_iterator pos, size_type n, const_reference v) | |||
| { | |||
| ABSL_HARDENING_ASSERT(pos >= begin()); | |||
| ABSL_HARDENING_ASSERT(pos <= end()); | |||
| if (ABSL_PREDICT_TRUE(n != 0)) | |||
| { | |||
| value_type dealias = v; | |||
| // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2 | |||
| // It appears that GCC thinks that since `pos` is a const pointer and may | |||
| // point to uninitialized memory at this point, a warning should be | |||
| // issued. But `pos` is actually only used to compute an array index to | |||
| // write to. | |||
| #if !defined(__clang__) && defined(__GNUC__) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" | |||
| #endif | |||
| return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)), n); | |||
| #if !defined(__clang__) && defined(__GNUC__) | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| return const_cast<iterator>(pos); | |||
| } | |||
| } | |||
| // Overload of `InlinedVector::insert(...)` that inserts copies of the | |||
| // elements of `list` starting at `pos`, returning an `iterator` pointing to | |||
| // the first of the newly inserted elements. | |||
| iterator insert(const_iterator pos, std::initializer_list<value_type> list) | |||
| { | |||
| return insert(pos, list.begin(), list.end()); | |||
| } | |||
| // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, | |||
| // `last`) starting at `pos`, returning an `iterator` pointing to the first | |||
| // of the newly inserted elements. | |||
| // | |||
| // NOTE: this overload is for iterators that are "forward" category or better. | |||
| template<typename ForwardIterator, EnableIfAtLeastForwardIterator<ForwardIterator> = 0> | |||
| iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) | |||
| { | |||
| ABSL_HARDENING_ASSERT(pos >= begin()); | |||
| ABSL_HARDENING_ASSERT(pos <= end()); | |||
| if (ABSL_PREDICT_TRUE(first != last)) | |||
| { | |||
| return storage_.Insert(pos, IteratorValueAdapter<A, ForwardIterator>(first), std::distance(first, last)); | |||
| } | |||
| else | |||
| { | |||
| return const_cast<iterator>(pos); | |||
| } | |||
| } | |||
| // Overload of `InlinedVector::insert(...)` that inserts the range [`first`, | |||
| // `last`) starting at `pos`, returning an `iterator` pointing to the first | |||
| // of the newly inserted elements. | |||
| // | |||
| // NOTE: this overload is for iterators that are "input" category. | |||
| template<typename InputIterator, DisableIfAtLeastForwardIterator<InputIterator> = 0> | |||
| iterator insert(const_iterator pos, InputIterator first, InputIterator last) | |||
| { | |||
| ABSL_HARDENING_ASSERT(pos >= begin()); | |||
| ABSL_HARDENING_ASSERT(pos <= end()); | |||
| size_type index = std::distance(cbegin(), pos); | |||
| for (size_type i = index; first != last; ++i, static_cast<void>(++first)) | |||
| { | |||
| insert(data() + i, *first); | |||
| } | |||
| return iterator(data() + index); | |||
| } | |||
| // `InlinedVector::emplace(...)` | |||
| // | |||
| // Constructs and inserts an element using `args...` in the inlined vector at | |||
| // `pos`, returning an `iterator` pointing to the newly emplaced element. | |||
| template<typename... Args> | |||
| iterator emplace(const_iterator pos, Args&&... args) | |||
| { | |||
| ABSL_HARDENING_ASSERT(pos >= begin()); | |||
| ABSL_HARDENING_ASSERT(pos <= end()); | |||
| value_type dealias(std::forward<Args>(args)...); | |||
| return storage_.Insert(pos, IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(std::addressof(dealias))), 1); | |||
| } | |||
| // `InlinedVector::emplace_back(...)` | |||
| // | |||
| // Constructs and inserts an element using `args...` in the inlined vector at | |||
| // `end()`, returning a `reference` to the newly emplaced element. | |||
| template<typename... Args> | |||
| reference emplace_back(Args&&... args) | |||
| { | |||
| return storage_.EmplaceBack(std::forward<Args>(args)...); | |||
| } | |||
| // `InlinedVector::push_back(...)` | |||
| // | |||
| // Inserts a copy of `v` in the inlined vector at `end()`. | |||
| void push_back(const_reference v) | |||
| { | |||
| static_cast<void>(emplace_back(v)); | |||
| } | |||
| // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` | |||
| // using move semantics. | |||
| void push_back(value_type&& v) | |||
| { | |||
| static_cast<void>(emplace_back(std::move(v))); | |||
| } | |||
| // `InlinedVector::pop_back()` | |||
| // | |||
| // Destroys the element at `back()`, reducing the size by `1`. | |||
| void pop_back() noexcept | |||
| { | |||
| ABSL_HARDENING_ASSERT(!empty()); | |||
| AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1)); | |||
| storage_.SubtractSize(1); | |||
| } | |||
| // `InlinedVector::erase(...)` | |||
| // | |||
| // Erases the element at `pos`, returning an `iterator` pointing to where the | |||
| // erased element was located. | |||
| // | |||
| // NOTE: may return `end()`, which is not dereferencable. | |||
| iterator erase(const_iterator pos) | |||
| { | |||
| ABSL_HARDENING_ASSERT(pos >= begin()); | |||
| ABSL_HARDENING_ASSERT(pos < end()); | |||
| return storage_.Erase(pos, pos + 1); | |||
| } | |||
| // Overload of `InlinedVector::erase(...)` that erases every element in the | |||
| // range [`from`, `to`), returning an `iterator` pointing to where the first | |||
| // erased element was located. | |||
| // | |||
| // NOTE: may return `end()`, which is not dereferencable. | |||
| iterator erase(const_iterator from, const_iterator to) | |||
| { | |||
| ABSL_HARDENING_ASSERT(from >= begin()); | |||
| ABSL_HARDENING_ASSERT(from <= to); | |||
| ABSL_HARDENING_ASSERT(to <= end()); | |||
| if (ABSL_PREDICT_TRUE(from != to)) | |||
| { | |||
| return storage_.Erase(from, to); | |||
| } | |||
| else | |||
| { | |||
| return const_cast<iterator>(from); | |||
| } | |||
| } | |||
| // `InlinedVector::clear()` | |||
| // | |||
| // Destroys all elements in the inlined vector, setting the size to `0` and | |||
| // deallocating any held memory. | |||
| void clear() noexcept | |||
| { | |||
| inlined_vector_internal::DestroyAdapter<A>::DestroyElements( | |||
| storage_.GetAllocator(), data(), size() | |||
| ); | |||
| storage_.DeallocateIfAllocated(); | |||
| storage_.SetInlinedSize(0); | |||
| } | |||
| // `InlinedVector::reserve(...)` | |||
| // | |||
| // Ensures that there is enough room for at least `n` elements. | |||
| void reserve(size_type n) | |||
| { | |||
| storage_.Reserve(n); | |||
| } | |||
| // `InlinedVector::shrink_to_fit()` | |||
| // | |||
| // Attempts to reduce memory usage by moving elements to (or keeping elements | |||
| // in) the smallest available buffer sufficient for containing `size()` | |||
| // elements. | |||
| // | |||
| // If `size()` is sufficiently small, the elements will be moved into (or kept | |||
| // in) the inlined space. | |||
| void shrink_to_fit() | |||
| { | |||
| if (storage_.GetIsAllocated()) | |||
| { | |||
| storage_.ShrinkToFit(); | |||
| } | |||
| } | |||
| // `InlinedVector::swap(...)` | |||
| // | |||
| // Swaps the contents of the inlined vector with `other`. | |||
| void swap(InlinedVector& other) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(this != std::addressof(other))) | |||
| { | |||
| storage_.Swap(std::addressof(other.storage_)); | |||
| } | |||
| } | |||
| private: | |||
| template<typename H, typename TheT, size_t TheN, typename TheA> | |||
| friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a); | |||
| Storage storage_; | |||
| }; | |||
| // ----------------------------------------------------------------------------- | |||
| // InlinedVector Non-Member Functions | |||
| // ----------------------------------------------------------------------------- | |||
| // `swap(...)` | |||
| // | |||
| // Swaps the contents of two inlined vectors. | |||
| template<typename T, size_t N, typename A> | |||
| void swap(absl::InlinedVector<T, N, A>& a, absl::InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) | |||
| { | |||
| a.swap(b); | |||
| } | |||
| // `operator==(...)` | |||
| // | |||
| // Tests for value-equality of two inlined vectors. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator==(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| auto a_data = a.data(); | |||
| auto b_data = b.data(); | |||
| return absl::equal(a_data, a_data + a.size(), b_data, b_data + b.size()); | |||
| } | |||
| // `operator!=(...)` | |||
| // | |||
| // Tests for value-inequality of two inlined vectors. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator!=(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| return !(a == b); | |||
| } | |||
| // `operator<(...)` | |||
| // | |||
| // Tests whether the value of an inlined vector is less than the value of | |||
| // another inlined vector using a lexicographical comparison algorithm. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator<(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| auto a_data = a.data(); | |||
| auto b_data = b.data(); | |||
| return std::lexicographical_compare(a_data, a_data + a.size(), b_data, b_data + b.size()); | |||
| } | |||
| // `operator>(...)` | |||
| // | |||
| // Tests whether the value of an inlined vector is greater than the value of | |||
| // another inlined vector using a lexicographical comparison algorithm. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator>(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| return b < a; | |||
| } | |||
| // `operator<=(...)` | |||
| // | |||
| // Tests whether the value of an inlined vector is less than or equal to the | |||
| // value of another inlined vector using a lexicographical comparison algorithm. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator<=(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| return !(b < a); | |||
| } | |||
| // `operator>=(...)` | |||
| // | |||
| // Tests whether the value of an inlined vector is greater than or equal to the | |||
| // value of another inlined vector using a lexicographical comparison algorithm. | |||
| template<typename T, size_t N, typename A> | |||
| bool operator>=(const absl::InlinedVector<T, N, A>& a, const absl::InlinedVector<T, N, A>& b) | |||
| { | |||
| return !(a < b); | |||
| } | |||
| // `AbslHashValue(...)` | |||
| // | |||
| // Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to | |||
| // call this directly. | |||
| template<typename H, typename T, size_t N, typename A> | |||
| H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) | |||
| { | |||
| auto size = a.size(); | |||
| return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size); | |||
| } | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INLINED_VECTOR_H_ | |||
| @@ -0,0 +1,863 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ | |||
| #include <algorithm> | |||
| #include <initializer_list> | |||
| #include <iterator> | |||
| #include <utility> | |||
| #include "absl/base/attributes.h" | |||
| #include "absl/base/internal/throw_delegate.h" | |||
| #include "absl/container/internal/btree.h" // IWYU pragma: export | |||
| #include "absl/container/internal/common.h" | |||
| #include "absl/memory/memory.h" | |||
| #include "absl/meta/type_traits.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // A common base class for btree_set, btree_map, btree_multiset, and | |||
| // btree_multimap. | |||
| template<typename Tree> | |||
| class btree_container | |||
| { | |||
| using params_type = typename Tree::params_type; | |||
| protected: | |||
| // Alias used for heterogeneous lookup functions. | |||
| // `key_arg<K>` evaluates to `K` when the functors are transparent and to | |||
| // `key_type` otherwise. It permits template argument deduction on `K` for the | |||
| // transparent case. | |||
| template<class K> | |||
| using key_arg = | |||
| typename KeyArg<params_type::kIsKeyCompareTransparent>::template type< | |||
| K, | |||
| typename Tree::key_type>; | |||
| public: | |||
| using key_type = typename Tree::key_type; | |||
| using value_type = typename Tree::value_type; | |||
| using size_type = typename Tree::size_type; | |||
| using difference_type = typename Tree::difference_type; | |||
| using key_compare = typename Tree::original_key_compare; | |||
| using value_compare = typename Tree::value_compare; | |||
| using allocator_type = typename Tree::allocator_type; | |||
| using reference = typename Tree::reference; | |||
| using const_reference = typename Tree::const_reference; | |||
| using pointer = typename Tree::pointer; | |||
| using const_pointer = typename Tree::const_pointer; | |||
| using iterator = typename Tree::iterator; | |||
| using const_iterator = typename Tree::const_iterator; | |||
| using reverse_iterator = typename Tree::reverse_iterator; | |||
| using const_reverse_iterator = typename Tree::const_reverse_iterator; | |||
| using node_type = typename Tree::node_handle_type; | |||
| // Constructors/assignments. | |||
| btree_container() : | |||
| tree_(key_compare(), allocator_type()) | |||
| { | |||
| } | |||
| explicit btree_container(const key_compare& comp, const allocator_type& alloc = allocator_type()) : | |||
| tree_(comp, alloc) | |||
| { | |||
| } | |||
| explicit btree_container(const allocator_type& alloc) : | |||
| tree_(key_compare(), alloc) | |||
| { | |||
| } | |||
| btree_container(const btree_container& other) : | |||
| btree_container(other, absl::allocator_traits<allocator_type>::select_on_container_copy_construction(other.get_allocator())) | |||
| { | |||
| } | |||
| btree_container(const btree_container& other, const allocator_type& alloc) : | |||
| tree_(other.tree_, alloc) | |||
| { | |||
| } | |||
| btree_container(btree_container&& other) noexcept( | |||
| std::is_nothrow_move_constructible<Tree>::value | |||
| ) = default; | |||
| btree_container(btree_container&& other, const allocator_type& alloc) : | |||
| tree_(std::move(other.tree_), alloc) | |||
| { | |||
| } | |||
| btree_container& operator=(const btree_container& other) = default; | |||
| btree_container& operator=(btree_container&& other) noexcept( | |||
| std::is_nothrow_move_assignable<Tree>::value | |||
| ) = default; | |||
| // Iterator routines. | |||
| iterator begin() | |||
| { | |||
| return tree_.begin(); | |||
| } | |||
| const_iterator begin() const | |||
| { | |||
| return tree_.begin(); | |||
| } | |||
| const_iterator cbegin() const | |||
| { | |||
| return tree_.begin(); | |||
| } | |||
| iterator end() | |||
| { | |||
| return tree_.end(); | |||
| } | |||
| const_iterator end() const | |||
| { | |||
| return tree_.end(); | |||
| } | |||
| const_iterator cend() const | |||
| { | |||
| return tree_.end(); | |||
| } | |||
| reverse_iterator rbegin() | |||
| { | |||
| return tree_.rbegin(); | |||
| } | |||
| const_reverse_iterator rbegin() const | |||
| { | |||
| return tree_.rbegin(); | |||
| } | |||
| const_reverse_iterator crbegin() const | |||
| { | |||
| return tree_.rbegin(); | |||
| } | |||
| reverse_iterator rend() | |||
| { | |||
| return tree_.rend(); | |||
| } | |||
| const_reverse_iterator rend() const | |||
| { | |||
| return tree_.rend(); | |||
| } | |||
| const_reverse_iterator crend() const | |||
| { | |||
| return tree_.rend(); | |||
| } | |||
| // Lookup routines. | |||
| template<typename K = key_type> | |||
| size_type count(const key_arg<K>& key) const | |||
| { | |||
| auto equal_range = this->equal_range(key); | |||
| return std::distance(equal_range.first, equal_range.second); | |||
| } | |||
| template<typename K = key_type> | |||
| iterator find(const key_arg<K>& key) | |||
| { | |||
| return tree_.find(key); | |||
| } | |||
| template<typename K = key_type> | |||
| const_iterator find(const key_arg<K>& key) const | |||
| { | |||
| return tree_.find(key); | |||
| } | |||
| template<typename K = key_type> | |||
| bool contains(const key_arg<K>& key) const | |||
| { | |||
| return find(key) != end(); | |||
| } | |||
| template<typename K = key_type> | |||
| iterator lower_bound(const key_arg<K>& key) | |||
| { | |||
| return tree_.lower_bound(key); | |||
| } | |||
| template<typename K = key_type> | |||
| const_iterator lower_bound(const key_arg<K>& key) const | |||
| { | |||
| return tree_.lower_bound(key); | |||
| } | |||
| template<typename K = key_type> | |||
| iterator upper_bound(const key_arg<K>& key) | |||
| { | |||
| return tree_.upper_bound(key); | |||
| } | |||
| template<typename K = key_type> | |||
| const_iterator upper_bound(const key_arg<K>& key) const | |||
| { | |||
| return tree_.upper_bound(key); | |||
| } | |||
| template<typename K = key_type> | |||
| std::pair<iterator, iterator> equal_range(const key_arg<K>& key) | |||
| { | |||
| return tree_.equal_range(key); | |||
| } | |||
| template<typename K = key_type> | |||
| std::pair<const_iterator, const_iterator> equal_range( | |||
| const key_arg<K>& key | |||
| ) const | |||
| { | |||
| return tree_.equal_range(key); | |||
| } | |||
| // Deletion routines. Note that there is also a deletion routine that is | |||
| // specific to btree_set_container/btree_multiset_container. | |||
| // Erase the specified iterator from the btree. The iterator must be valid | |||
| // (i.e. not equal to end()). Return an iterator pointing to the node after | |||
| // the one that was erased (or end() if none exists). | |||
| iterator erase(const_iterator iter) | |||
| { | |||
| return tree_.erase(iterator(iter)); | |||
| } | |||
| iterator erase(iterator iter) | |||
| { | |||
| return tree_.erase(iter); | |||
| } | |||
| iterator erase(const_iterator first, const_iterator last) | |||
| { | |||
| return tree_.erase_range(iterator(first), iterator(last)).second; | |||
| } | |||
| template<typename K = key_type> | |||
| size_type erase(const key_arg<K>& key) | |||
| { | |||
| auto equal_range = this->equal_range(key); | |||
| return tree_.erase_range(equal_range.first, equal_range.second).first; | |||
| } | |||
| // Extract routines. | |||
| node_type extract(iterator position) | |||
| { | |||
| // Use Construct instead of Transfer because the rebalancing code will | |||
| // destroy the slot later. | |||
| auto node = | |||
| CommonAccess::Construct<node_type>(get_allocator(), position.slot()); | |||
| erase(position); | |||
| return node; | |||
| } | |||
| node_type extract(const_iterator position) | |||
| { | |||
| return extract(iterator(position)); | |||
| } | |||
| // Utility routines. | |||
| ABSL_ATTRIBUTE_REINITIALIZES void clear() | |||
| { | |||
| tree_.clear(); | |||
| } | |||
| void swap(btree_container& other) | |||
| { | |||
| tree_.swap(other.tree_); | |||
| } | |||
| void verify() const | |||
| { | |||
| tree_.verify(); | |||
| } | |||
| // Size routines. | |||
| size_type size() const | |||
| { | |||
| return tree_.size(); | |||
| } | |||
| size_type max_size() const | |||
| { | |||
| return tree_.max_size(); | |||
| } | |||
| bool empty() const | |||
| { | |||
| return tree_.empty(); | |||
| } | |||
| friend bool operator==(const btree_container& x, const btree_container& y) | |||
| { | |||
| if (x.size() != y.size()) | |||
| return false; | |||
| return std::equal(x.begin(), x.end(), y.begin()); | |||
| } | |||
| friend bool operator!=(const btree_container& x, const btree_container& y) | |||
| { | |||
| return !(x == y); | |||
| } | |||
| friend bool operator<(const btree_container& x, const btree_container& y) | |||
| { | |||
| return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); | |||
| } | |||
| friend bool operator>(const btree_container& x, const btree_container& y) | |||
| { | |||
| return y < x; | |||
| } | |||
| friend bool operator<=(const btree_container& x, const btree_container& y) | |||
| { | |||
| return !(y < x); | |||
| } | |||
| friend bool operator>=(const btree_container& x, const btree_container& y) | |||
| { | |||
| return !(x < y); | |||
| } | |||
| // The allocator used by the btree. | |||
| allocator_type get_allocator() const | |||
| { | |||
| return tree_.get_allocator(); | |||
| } | |||
| // The key comparator used by the btree. | |||
| key_compare key_comp() const | |||
| { | |||
| return key_compare(tree_.key_comp()); | |||
| } | |||
| value_compare value_comp() const | |||
| { | |||
| return tree_.value_comp(); | |||
| } | |||
| // Support absl::Hash. | |||
| template<typename State> | |||
| friend State AbslHashValue(State h, const btree_container& b) | |||
| { | |||
| for (const auto& v : b) | |||
| { | |||
| h = State::combine(std::move(h), v); | |||
| } | |||
| return State::combine(std::move(h), b.size()); | |||
| } | |||
| protected: | |||
| friend struct btree_access; | |||
| Tree tree_; | |||
| }; | |||
| // A common base class for btree_set and btree_map. | |||
| template<typename Tree> | |||
| class btree_set_container : public btree_container<Tree> | |||
| { | |||
| using super_type = btree_container<Tree>; | |||
| using params_type = typename Tree::params_type; | |||
| using init_type = typename params_type::init_type; | |||
| using is_key_compare_to = typename params_type::is_key_compare_to; | |||
| friend class BtreeNodePeer; | |||
| protected: | |||
| template<class K> | |||
| using key_arg = typename super_type::template key_arg<K>; | |||
| public: | |||
| using key_type = typename Tree::key_type; | |||
| using value_type = typename Tree::value_type; | |||
| using size_type = typename Tree::size_type; | |||
| using key_compare = typename Tree::original_key_compare; | |||
| using allocator_type = typename Tree::allocator_type; | |||
| using iterator = typename Tree::iterator; | |||
| using const_iterator = typename Tree::const_iterator; | |||
| using node_type = typename super_type::node_type; | |||
| using insert_return_type = InsertReturnType<iterator, node_type>; | |||
| // Inherit constructors. | |||
| using super_type::super_type; | |||
| btree_set_container() | |||
| { | |||
| } | |||
| // Range constructors. | |||
| template<class InputIterator> | |||
| btree_set_container(InputIterator b, InputIterator e, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) : | |||
| super_type(comp, alloc) | |||
| { | |||
| insert(b, e); | |||
| } | |||
| template<class InputIterator> | |||
| btree_set_container(InputIterator b, InputIterator e, const allocator_type& alloc) : | |||
| btree_set_container(b, e, key_compare(), alloc) | |||
| { | |||
| } | |||
| // Initializer list constructors. | |||
| btree_set_container(std::initializer_list<init_type> init, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) : | |||
| btree_set_container(init.begin(), init.end(), comp, alloc) | |||
| { | |||
| } | |||
| btree_set_container(std::initializer_list<init_type> init, const allocator_type& alloc) : | |||
| btree_set_container(init.begin(), init.end(), alloc) | |||
| { | |||
| } | |||
| // Insertion routines. | |||
| std::pair<iterator, bool> insert(const value_type& v) | |||
| { | |||
| return this->tree_.insert_unique(params_type::key(v), v); | |||
| } | |||
| std::pair<iterator, bool> insert(value_type&& v) | |||
| { | |||
| return this->tree_.insert_unique(params_type::key(v), std::move(v)); | |||
| } | |||
| template<typename... Args> | |||
| std::pair<iterator, bool> emplace(Args&&... args) | |||
| { | |||
| // Use a node handle to manage a temp slot. | |||
| auto node = CommonAccess::Construct<node_type>(this->get_allocator(), std::forward<Args>(args)...); | |||
| auto* slot = CommonAccess::GetSlot(node); | |||
| return this->tree_.insert_unique(params_type::key(slot), slot); | |||
| } | |||
| iterator insert(const_iterator hint, const value_type& v) | |||
| { | |||
| return this->tree_ | |||
| .insert_hint_unique(iterator(hint), params_type::key(v), v) | |||
| .first; | |||
| } | |||
| iterator insert(const_iterator hint, value_type&& v) | |||
| { | |||
| return this->tree_ | |||
| .insert_hint_unique(iterator(hint), params_type::key(v), std::move(v)) | |||
| .first; | |||
| } | |||
| template<typename... Args> | |||
| iterator emplace_hint(const_iterator hint, Args&&... args) | |||
| { | |||
| // Use a node handle to manage a temp slot. | |||
| auto node = CommonAccess::Construct<node_type>(this->get_allocator(), std::forward<Args>(args)...); | |||
| auto* slot = CommonAccess::GetSlot(node); | |||
| return this->tree_ | |||
| .insert_hint_unique(iterator(hint), params_type::key(slot), slot) | |||
| .first; | |||
| } | |||
| template<typename InputIterator> | |||
| void insert(InputIterator b, InputIterator e) | |||
| { | |||
| this->tree_.insert_iterator_unique(b, e, 0); | |||
| } | |||
| void insert(std::initializer_list<init_type> init) | |||
| { | |||
| this->tree_.insert_iterator_unique(init.begin(), init.end(), 0); | |||
| } | |||
| insert_return_type insert(node_type&& node) | |||
| { | |||
| if (!node) | |||
| return {this->end(), false, node_type()}; | |||
| std::pair<iterator, bool> res = | |||
| this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), CommonAccess::GetSlot(node)); | |||
| if (res.second) | |||
| { | |||
| CommonAccess::Destroy(&node); | |||
| return {res.first, true, node_type()}; | |||
| } | |||
| else | |||
| { | |||
| return {res.first, false, std::move(node)}; | |||
| } | |||
| } | |||
| iterator insert(const_iterator hint, node_type&& node) | |||
| { | |||
| if (!node) | |||
| return this->end(); | |||
| std::pair<iterator, bool> res = this->tree_.insert_hint_unique( | |||
| iterator(hint), params_type::key(CommonAccess::GetSlot(node)), CommonAccess::GetSlot(node) | |||
| ); | |||
| if (res.second) | |||
| CommonAccess::Destroy(&node); | |||
| return res.first; | |||
| } | |||
| // Node extraction routines. | |||
| template<typename K = key_type> | |||
| node_type extract(const key_arg<K>& key) | |||
| { | |||
| const std::pair<iterator, bool> lower_and_equal = | |||
| this->tree_.lower_bound_equal(key); | |||
| return lower_and_equal.second ? extract(lower_and_equal.first) : node_type(); | |||
| } | |||
| using super_type::extract; | |||
| // Merge routines. | |||
| // Moves elements from `src` into `this`. If the element already exists in | |||
| // `this`, it is left unmodified in `src`. | |||
| template< | |||
| typename T, | |||
| typename absl::enable_if_t< | |||
| absl::conjunction< | |||
| std::is_same<value_type, typename T::value_type>, | |||
| std::is_same<allocator_type, typename T::allocator_type>, | |||
| std::is_same<typename params_type::is_map_container, typename T::params_type::is_map_container>>::value, | |||
| int> = 0> | |||
| void merge(btree_container<T>& src) | |||
| { // NOLINT | |||
| for (auto src_it = src.begin(); src_it != src.end();) | |||
| { | |||
| if (insert(std::move(params_type::element(src_it.slot()))).second) | |||
| { | |||
| src_it = src.erase(src_it); | |||
| } | |||
| else | |||
| { | |||
| ++src_it; | |||
| } | |||
| } | |||
| } | |||
| template< | |||
| typename T, | |||
| typename absl::enable_if_t< | |||
| absl::conjunction< | |||
| std::is_same<value_type, typename T::value_type>, | |||
| std::is_same<allocator_type, typename T::allocator_type>, | |||
| std::is_same<typename params_type::is_map_container, typename T::params_type::is_map_container>>::value, | |||
| int> = 0> | |||
| void merge(btree_container<T>&& src) | |||
| { | |||
| merge(src); | |||
| } | |||
| }; | |||
| // Base class for btree_map. | |||
| template<typename Tree> | |||
| class btree_map_container : public btree_set_container<Tree> | |||
| { | |||
| using super_type = btree_set_container<Tree>; | |||
| using params_type = typename Tree::params_type; | |||
| friend class BtreeNodePeer; | |||
| private: | |||
| template<class K> | |||
| using key_arg = typename super_type::template key_arg<K>; | |||
| public: | |||
| using key_type = typename Tree::key_type; | |||
| using mapped_type = typename params_type::mapped_type; | |||
| using value_type = typename Tree::value_type; | |||
| using key_compare = typename Tree::original_key_compare; | |||
| using allocator_type = typename Tree::allocator_type; | |||
| using iterator = typename Tree::iterator; | |||
| using const_iterator = typename Tree::const_iterator; | |||
| // Inherit constructors. | |||
| using super_type::super_type; | |||
| btree_map_container() | |||
| { | |||
| } | |||
| // Insertion routines. | |||
| // Note: the nullptr template arguments and extra `const M&` overloads allow | |||
| // for supporting bitfield arguments. | |||
| template<typename K = key_type, class M> | |||
| std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const M& obj) | |||
| { | |||
| return insert_or_assign_impl(k, obj); | |||
| } | |||
| template<typename K = key_type, class M, K* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const M& obj) | |||
| { | |||
| return insert_or_assign_impl(std::forward<K>(k), obj); | |||
| } | |||
| template<typename K = key_type, class M, M* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, M&& obj) | |||
| { | |||
| return insert_or_assign_impl(k, std::forward<M>(obj)); | |||
| } | |||
| template<typename K = key_type, class M, K* = nullptr, M* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, M&& obj) | |||
| { | |||
| return insert_or_assign_impl(std::forward<K>(k), std::forward<M>(obj)); | |||
| } | |||
| template<typename K = key_type, class M> | |||
| iterator insert_or_assign(const_iterator hint, const key_arg<K>& k, const M& obj) | |||
| { | |||
| return insert_or_assign_hint_impl(hint, k, obj); | |||
| } | |||
| template<typename K = key_type, class M, K* = nullptr> | |||
| iterator insert_or_assign(const_iterator hint, key_arg<K>&& k, const M& obj) | |||
| { | |||
| return insert_or_assign_hint_impl(hint, std::forward<K>(k), obj); | |||
| } | |||
| template<typename K = key_type, class M, M* = nullptr> | |||
| iterator insert_or_assign(const_iterator hint, const key_arg<K>& k, M&& obj) | |||
| { | |||
| return insert_or_assign_hint_impl(hint, k, std::forward<M>(obj)); | |||
| } | |||
| template<typename K = key_type, class M, K* = nullptr, M* = nullptr> | |||
| iterator insert_or_assign(const_iterator hint, key_arg<K>&& k, M&& obj) | |||
| { | |||
| return insert_or_assign_hint_impl(hint, std::forward<K>(k), std::forward<M>(obj)); | |||
| } | |||
| template<typename K = key_type, typename... Args, typename absl::enable_if_t<!std::is_convertible<K, const_iterator>::value, int> = 0> | |||
| std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) | |||
| { | |||
| return try_emplace_impl(k, std::forward<Args>(args)...); | |||
| } | |||
| template<typename K = key_type, typename... Args, typename absl::enable_if_t<!std::is_convertible<K, const_iterator>::value, int> = 0> | |||
| std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) | |||
| { | |||
| return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...); | |||
| } | |||
| template<typename K = key_type, typename... Args> | |||
| iterator try_emplace(const_iterator hint, const key_arg<K>& k, Args&&... args) | |||
| { | |||
| return try_emplace_hint_impl(hint, k, std::forward<Args>(args)...); | |||
| } | |||
| template<typename K = key_type, typename... Args> | |||
| iterator try_emplace(const_iterator hint, key_arg<K>&& k, Args&&... args) | |||
| { | |||
| return try_emplace_hint_impl(hint, std::forward<K>(k), std::forward<Args>(args)...); | |||
| } | |||
| template<typename K = key_type> | |||
| mapped_type& operator[](const key_arg<K>& k) | |||
| { | |||
| return try_emplace(k).first->second; | |||
| } | |||
| template<typename K = key_type> | |||
| mapped_type& operator[](key_arg<K>&& k) | |||
| { | |||
| return try_emplace(std::forward<K>(k)).first->second; | |||
| } | |||
| template<typename K = key_type> | |||
| mapped_type& at(const key_arg<K>& key) | |||
| { | |||
| auto it = this->find(key); | |||
| if (it == this->end()) | |||
| base_internal::ThrowStdOutOfRange("absl::btree_map::at"); | |||
| return it->second; | |||
| } | |||
| template<typename K = key_type> | |||
| const mapped_type& at(const key_arg<K>& key) const | |||
| { | |||
| auto it = this->find(key); | |||
| if (it == this->end()) | |||
| base_internal::ThrowStdOutOfRange("absl::btree_map::at"); | |||
| return it->second; | |||
| } | |||
| private: | |||
| // Note: when we call `std::forward<M>(obj)` twice, it's safe because | |||
| // insert_unique/insert_hint_unique are guaranteed to not consume `obj` when | |||
| // `ret.second` is false. | |||
| template<class K, class M> | |||
| std::pair<iterator, bool> insert_or_assign_impl(K&& k, M&& obj) | |||
| { | |||
| const std::pair<iterator, bool> ret = | |||
| this->tree_.insert_unique(k, std::forward<K>(k), std::forward<M>(obj)); | |||
| if (!ret.second) | |||
| ret.first->second = std::forward<M>(obj); | |||
| return ret; | |||
| } | |||
| template<class K, class M> | |||
| iterator insert_or_assign_hint_impl(const_iterator hint, K&& k, M&& obj) | |||
| { | |||
| const std::pair<iterator, bool> ret = this->tree_.insert_hint_unique( | |||
| iterator(hint), k, std::forward<K>(k), std::forward<M>(obj) | |||
| ); | |||
| if (!ret.second) | |||
| ret.first->second = std::forward<M>(obj); | |||
| return ret.first; | |||
| } | |||
| template<class K, class... Args> | |||
| std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) | |||
| { | |||
| return this->tree_.insert_unique( | |||
| k, std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)), std::forward_as_tuple(std::forward<Args>(args)...) | |||
| ); | |||
| } | |||
| template<class K, class... Args> | |||
| iterator try_emplace_hint_impl(const_iterator hint, K&& k, Args&&... args) | |||
| { | |||
| return this->tree_ | |||
| .insert_hint_unique(iterator(hint), k, std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)), std::forward_as_tuple(std::forward<Args>(args)...)) | |||
| .first; | |||
| } | |||
| }; | |||
| // A common base class for btree_multiset and btree_multimap. | |||
| template<typename Tree> | |||
| class btree_multiset_container : public btree_container<Tree> | |||
| { | |||
| using super_type = btree_container<Tree>; | |||
| using params_type = typename Tree::params_type; | |||
| using init_type = typename params_type::init_type; | |||
| using is_key_compare_to = typename params_type::is_key_compare_to; | |||
| friend class BtreeNodePeer; | |||
| template<class K> | |||
| using key_arg = typename super_type::template key_arg<K>; | |||
| public: | |||
| using key_type = typename Tree::key_type; | |||
| using value_type = typename Tree::value_type; | |||
| using size_type = typename Tree::size_type; | |||
| using key_compare = typename Tree::original_key_compare; | |||
| using allocator_type = typename Tree::allocator_type; | |||
| using iterator = typename Tree::iterator; | |||
| using const_iterator = typename Tree::const_iterator; | |||
| using node_type = typename super_type::node_type; | |||
| // Inherit constructors. | |||
| using super_type::super_type; | |||
| btree_multiset_container() | |||
| { | |||
| } | |||
| // Range constructors. | |||
| template<class InputIterator> | |||
| btree_multiset_container(InputIterator b, InputIterator e, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) : | |||
| super_type(comp, alloc) | |||
| { | |||
| insert(b, e); | |||
| } | |||
| template<class InputIterator> | |||
| btree_multiset_container(InputIterator b, InputIterator e, const allocator_type& alloc) : | |||
| btree_multiset_container(b, e, key_compare(), alloc) | |||
| { | |||
| } | |||
| // Initializer list constructors. | |||
| btree_multiset_container(std::initializer_list<init_type> init, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()) : | |||
| btree_multiset_container(init.begin(), init.end(), comp, alloc) | |||
| { | |||
| } | |||
| btree_multiset_container(std::initializer_list<init_type> init, const allocator_type& alloc) : | |||
| btree_multiset_container(init.begin(), init.end(), alloc) | |||
| { | |||
| } | |||
| // Insertion routines. | |||
| iterator insert(const value_type& v) | |||
| { | |||
| return this->tree_.insert_multi(v); | |||
| } | |||
| iterator insert(value_type&& v) | |||
| { | |||
| return this->tree_.insert_multi(std::move(v)); | |||
| } | |||
| iterator insert(const_iterator hint, const value_type& v) | |||
| { | |||
| return this->tree_.insert_hint_multi(iterator(hint), v); | |||
| } | |||
| iterator insert(const_iterator hint, value_type&& v) | |||
| { | |||
| return this->tree_.insert_hint_multi(iterator(hint), std::move(v)); | |||
| } | |||
| template<typename InputIterator> | |||
| void insert(InputIterator b, InputIterator e) | |||
| { | |||
| this->tree_.insert_iterator_multi(b, e); | |||
| } | |||
| void insert(std::initializer_list<init_type> init) | |||
| { | |||
| this->tree_.insert_iterator_multi(init.begin(), init.end()); | |||
| } | |||
| template<typename... Args> | |||
| iterator emplace(Args&&... args) | |||
| { | |||
| // Use a node handle to manage a temp slot. | |||
| auto node = CommonAccess::Construct<node_type>(this->get_allocator(), std::forward<Args>(args)...); | |||
| return this->tree_.insert_multi(CommonAccess::GetSlot(node)); | |||
| } | |||
| template<typename... Args> | |||
| iterator emplace_hint(const_iterator hint, Args&&... args) | |||
| { | |||
| // Use a node handle to manage a temp slot. | |||
| auto node = CommonAccess::Construct<node_type>(this->get_allocator(), std::forward<Args>(args)...); | |||
| return this->tree_.insert_hint_multi(iterator(hint), CommonAccess::GetSlot(node)); | |||
| } | |||
| iterator insert(node_type&& node) | |||
| { | |||
| if (!node) | |||
| return this->end(); | |||
| iterator res = | |||
| this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), CommonAccess::GetSlot(node)); | |||
| CommonAccess::Destroy(&node); | |||
| return res; | |||
| } | |||
| iterator insert(const_iterator hint, node_type&& node) | |||
| { | |||
| if (!node) | |||
| return this->end(); | |||
| iterator res = this->tree_.insert_hint_multi( | |||
| iterator(hint), | |||
| std::move(params_type::element(CommonAccess::GetSlot(node))) | |||
| ); | |||
| CommonAccess::Destroy(&node); | |||
| return res; | |||
| } | |||
| // Node extraction routines. | |||
| template<typename K = key_type> | |||
| node_type extract(const key_arg<K>& key) | |||
| { | |||
| const std::pair<iterator, bool> lower_and_equal = | |||
| this->tree_.lower_bound_equal(key); | |||
| return lower_and_equal.second ? extract(lower_and_equal.first) : node_type(); | |||
| } | |||
| using super_type::extract; | |||
| // Merge routines. | |||
| // Moves all elements from `src` into `this`. | |||
| template< | |||
| typename T, | |||
| typename absl::enable_if_t< | |||
| absl::conjunction< | |||
| std::is_same<value_type, typename T::value_type>, | |||
| std::is_same<allocator_type, typename T::allocator_type>, | |||
| std::is_same<typename params_type::is_map_container, typename T::params_type::is_map_container>>::value, | |||
| int> = 0> | |||
| void merge(btree_container<T>& src) | |||
| { // NOLINT | |||
| for (auto src_it = src.begin(), end = src.end(); src_it != end; ++src_it) | |||
| { | |||
| insert(std::move(params_type::element(src_it.slot()))); | |||
| } | |||
| src.clear(); | |||
| } | |||
| template< | |||
| typename T, | |||
| typename absl::enable_if_t< | |||
| absl::conjunction< | |||
| std::is_same<value_type, typename T::value_type>, | |||
| std::is_same<allocator_type, typename T::allocator_type>, | |||
| std::is_same<typename params_type::is_map_container, typename T::params_type::is_map_container>>::value, | |||
| int> = 0> | |||
| void merge(btree_container<T>&& src) | |||
| { | |||
| merge(src); | |||
| } | |||
| }; | |||
| // A base class for btree_multimap. | |||
| template<typename Tree> | |||
| class btree_multimap_container : public btree_multiset_container<Tree> | |||
| { | |||
| using super_type = btree_multiset_container<Tree>; | |||
| using params_type = typename Tree::params_type; | |||
| friend class BtreeNodePeer; | |||
| public: | |||
| using mapped_type = typename params_type::mapped_type; | |||
| // Inherit constructors. | |||
| using super_type::super_type; | |||
| btree_multimap_container() | |||
| { | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_BTREE_CONTAINER_H_ | |||
| @@ -0,0 +1,258 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_CONTAINER_H_ | |||
| #include <cassert> | |||
| #include <type_traits> | |||
| #include "absl/meta/type_traits.h" | |||
| #include "absl/types/optional.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class, class = void> | |||
| struct IsTransparent : std::false_type | |||
| { | |||
| }; | |||
| template<class T> | |||
| struct IsTransparent<T, absl::void_t<typename T::is_transparent>> : std::true_type | |||
| { | |||
| }; | |||
| template<bool is_transparent> | |||
| struct KeyArg | |||
| { | |||
| // Transparent. Forward `K`. | |||
| template<typename K, typename key_type> | |||
| using type = K; | |||
| }; | |||
| template<> | |||
| struct KeyArg<false> | |||
| { | |||
| // Not transparent. Always use `key_type`. | |||
| template<typename K, typename key_type> | |||
| using type = key_type; | |||
| }; | |||
| // The node_handle concept from C++17. | |||
| // We specialize node_handle for sets and maps. node_handle_base holds the | |||
| // common API of both. | |||
| template<typename PolicyTraits, typename Alloc> | |||
| class node_handle_base | |||
| { | |||
| protected: | |||
| using slot_type = typename PolicyTraits::slot_type; | |||
| public: | |||
| using allocator_type = Alloc; | |||
| constexpr node_handle_base() = default; | |||
| node_handle_base(node_handle_base&& other) noexcept | |||
| { | |||
| *this = std::move(other); | |||
| } | |||
| ~node_handle_base() | |||
| { | |||
| destroy(); | |||
| } | |||
| node_handle_base& operator=(node_handle_base&& other) noexcept | |||
| { | |||
| destroy(); | |||
| if (!other.empty()) | |||
| { | |||
| alloc_ = other.alloc_; | |||
| PolicyTraits::transfer(alloc(), slot(), other.slot()); | |||
| other.reset(); | |||
| } | |||
| return *this; | |||
| } | |||
| bool empty() const noexcept | |||
| { | |||
| return !alloc_; | |||
| } | |||
| explicit operator bool() const noexcept | |||
| { | |||
| return !empty(); | |||
| } | |||
| allocator_type get_allocator() const | |||
| { | |||
| return *alloc_; | |||
| } | |||
| protected: | |||
| friend struct CommonAccess; | |||
| struct transfer_tag_t | |||
| { | |||
| }; | |||
| node_handle_base(transfer_tag_t, const allocator_type& a, slot_type* s) : | |||
| alloc_(a) | |||
| { | |||
| PolicyTraits::transfer(alloc(), slot(), s); | |||
| } | |||
| struct construct_tag_t | |||
| { | |||
| }; | |||
| template<typename... Args> | |||
| node_handle_base(construct_tag_t, const allocator_type& a, Args&&... args) : | |||
| alloc_(a) | |||
| { | |||
| PolicyTraits::construct(alloc(), slot(), std::forward<Args>(args)...); | |||
| } | |||
| void destroy() | |||
| { | |||
| if (!empty()) | |||
| { | |||
| PolicyTraits::destroy(alloc(), slot()); | |||
| reset(); | |||
| } | |||
| } | |||
| void reset() | |||
| { | |||
| assert(alloc_.has_value()); | |||
| alloc_ = absl::nullopt; | |||
| } | |||
| slot_type* slot() const | |||
| { | |||
| assert(!empty()); | |||
| return reinterpret_cast<slot_type*>(std::addressof(slot_space_)); | |||
| } | |||
| allocator_type* alloc() | |||
| { | |||
| return std::addressof(*alloc_); | |||
| } | |||
| private: | |||
| absl::optional<allocator_type> alloc_ = {}; | |||
| alignas(slot_type) mutable unsigned char slot_space_[sizeof(slot_type)] = {}; | |||
| }; | |||
| // For sets. | |||
| template<typename Policy, typename PolicyTraits, typename Alloc, typename = void> | |||
| class node_handle : public node_handle_base<PolicyTraits, Alloc> | |||
| { | |||
| using Base = node_handle_base<PolicyTraits, Alloc>; | |||
| public: | |||
| using value_type = typename PolicyTraits::value_type; | |||
| constexpr node_handle() | |||
| { | |||
| } | |||
| value_type& value() const | |||
| { | |||
| return PolicyTraits::element(this->slot()); | |||
| } | |||
| private: | |||
| friend struct CommonAccess; | |||
| using Base::Base; | |||
| }; | |||
| // For maps. | |||
| template<typename Policy, typename PolicyTraits, typename Alloc> | |||
| class node_handle<Policy, PolicyTraits, Alloc, absl::void_t<typename Policy::mapped_type>> : public node_handle_base<PolicyTraits, Alloc> | |||
| { | |||
| using Base = node_handle_base<PolicyTraits, Alloc>; | |||
| using slot_type = typename PolicyTraits::slot_type; | |||
| public: | |||
| using key_type = typename Policy::key_type; | |||
| using mapped_type = typename Policy::mapped_type; | |||
| constexpr node_handle() | |||
| { | |||
| } | |||
| // When C++17 is available, we can use std::launder to provide mutable | |||
| // access to the key. Otherwise, we provide const access. | |||
| auto key() const | |||
| -> decltype(PolicyTraits::mutable_key(std::declval<slot_type*>())) | |||
| { | |||
| return PolicyTraits::mutable_key(this->slot()); | |||
| } | |||
| mapped_type& mapped() const | |||
| { | |||
| return PolicyTraits::value(&PolicyTraits::element(this->slot())); | |||
| } | |||
| private: | |||
| friend struct CommonAccess; | |||
| using Base::Base; | |||
| }; | |||
| // Provide access to non-public node-handle functions. | |||
| struct CommonAccess | |||
| { | |||
| template<typename Node> | |||
| static auto GetSlot(const Node& node) -> decltype(node.slot()) | |||
| { | |||
| return node.slot(); | |||
| } | |||
| template<typename Node> | |||
| static void Destroy(Node* node) | |||
| { | |||
| node->destroy(); | |||
| } | |||
| template<typename Node> | |||
| static void Reset(Node* node) | |||
| { | |||
| node->reset(); | |||
| } | |||
| template<typename T, typename... Args> | |||
| static T Transfer(Args&&... args) | |||
| { | |||
| return T(typename T::transfer_tag_t{}, std::forward<Args>(args)...); | |||
| } | |||
| template<typename T, typename... Args> | |||
| static T Construct(Args&&... args) | |||
| { | |||
| return T(typename T::construct_tag_t{}, std::forward<Args>(args)...); | |||
| } | |||
| }; | |||
| // Implement the insert_return_type<> concept of C++17. | |||
| template<class Iterator, class NodeType> | |||
| struct InsertReturnType | |||
| { | |||
| Iterator position; | |||
| bool inserted; | |||
| NodeType node; | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_CONTAINER_H_ | |||
| @@ -0,0 +1,339 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Helper class to perform the Empty Base Optimization. | |||
| // Ts can contain classes and non-classes, empty or not. For the ones that | |||
| // are empty classes, we perform the optimization. If all types in Ts are empty | |||
| // classes, then CompressedTuple<Ts...> is itself an empty class. | |||
| // | |||
| // To access the members, use member get<N>() function. | |||
| // | |||
| // Eg: | |||
| // absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2, | |||
| // t3); | |||
| // assert(value.get<0>() == 7); | |||
| // T1& t1 = value.get<1>(); | |||
| // const T2& t2 = value.get<2>(); | |||
| // ... | |||
| // | |||
| // https://en.cppreference.com/w/cpp/language/ebo | |||
| #ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ | |||
| #include <initializer_list> | |||
| #include <tuple> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/utility/utility.h" | |||
| #if defined(_MSC_VER) && !defined(__NVCC__) | |||
| // We need to mark these classes with this declspec to ensure that | |||
| // CompressedTuple happens. | |||
| #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases) | |||
| #else | |||
| #define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<typename... Ts> | |||
| class CompressedTuple; | |||
| namespace internal_compressed_tuple | |||
| { | |||
| template<typename D, size_t I> | |||
| struct Elem; | |||
| template<typename... B, size_t I> | |||
| struct Elem<CompressedTuple<B...>, I> : std::tuple_element<I, std::tuple<B...>> | |||
| { | |||
| }; | |||
| template<typename D, size_t I> | |||
| using ElemT = typename Elem<D, I>::type; | |||
| // Use the __is_final intrinsic if available. Where it's not available, classes | |||
| // declared with the 'final' specifier cannot be used as CompressedTuple | |||
| // elements. | |||
| // TODO(sbenza): Replace this with std::is_final in C++14. | |||
| template<typename T> | |||
| constexpr bool IsFinal() | |||
| { | |||
| #if defined(__clang__) || defined(__GNUC__) | |||
| return __is_final(T); | |||
| #else | |||
| return false; | |||
| #endif | |||
| } | |||
| // We can't use EBCO on other CompressedTuples because that would mean that we | |||
| // derive from multiple Storage<> instantiations with the same I parameter, | |||
| // and potentially from multiple identical Storage<> instantiations. So anytime | |||
| // we use type inheritance rather than encapsulation, we mark | |||
| // CompressedTupleImpl, to make this easy to detect. | |||
| struct uses_inheritance | |||
| { | |||
| }; | |||
| template<typename T> | |||
| constexpr bool ShouldUseBase() | |||
| { | |||
| return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>() && | |||
| !std::is_base_of<uses_inheritance, T>::value; | |||
| } | |||
| // The storage class provides two specializations: | |||
| // - For empty classes, it stores T as a base class. | |||
| // - For everything else, it stores T as a member. | |||
| template<typename T, size_t I, | |||
| #if defined(_MSC_VER) | |||
| bool UseBase = ShouldUseBase<typename std::enable_if<true, T>::type>()> | |||
| #else | |||
| bool UseBase = ShouldUseBase<T>()> | |||
| #endif | |||
| struct Storage | |||
| { | |||
| T value; | |||
| constexpr Storage() = default; | |||
| template<typename V> | |||
| explicit constexpr Storage(absl::in_place_t, V&& v) : | |||
| value(absl::forward<V>(v)) | |||
| { | |||
| } | |||
| constexpr const T& get() const& | |||
| { | |||
| return value; | |||
| } | |||
| T& get() & | |||
| { | |||
| return value; | |||
| } | |||
| constexpr const T&& get() const&& | |||
| { | |||
| return absl::move(*this).value; | |||
| } | |||
| T&& get() && | |||
| { | |||
| return std::move(*this).value; | |||
| } | |||
| }; | |||
| template<typename T, size_t I> | |||
| struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T | |||
| { | |||
| constexpr Storage() = default; | |||
| template<typename V> | |||
| explicit constexpr Storage(absl::in_place_t, V&& v) : | |||
| T(absl::forward<V>(v)) | |||
| { | |||
| } | |||
| constexpr const T& get() const& | |||
| { | |||
| return *this; | |||
| } | |||
| T& get() & | |||
| { | |||
| return *this; | |||
| } | |||
| constexpr const T&& get() const&& | |||
| { | |||
| return absl::move(*this); | |||
| } | |||
| T&& get() && | |||
| { | |||
| return std::move(*this); | |||
| } | |||
| }; | |||
| template<typename D, typename I, bool ShouldAnyUseBase> | |||
| struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl; | |||
| template<typename... Ts, size_t... I, bool ShouldAnyUseBase> | |||
| struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< | |||
| CompressedTuple<Ts...>, | |||
| absl::index_sequence<I...>, | |||
| ShouldAnyUseBase> | |||
| // We use the dummy identity function through std::integral_constant to | |||
| // convince MSVC of accepting and expanding I in that context. Without it | |||
| // you would get: | |||
| // error C3548: 'I': parameter pack cannot be used in this context | |||
| : uses_inheritance, Storage<Ts, std::integral_constant<size_t, I>::value>... | |||
| { | |||
| constexpr CompressedTupleImpl() = default; | |||
| template<typename... Vs> | |||
| explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) : | |||
| Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... | |||
| { | |||
| } | |||
| friend CompressedTuple<Ts...>; | |||
| }; | |||
| template<typename... Ts, size_t... I> | |||
| struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl< | |||
| CompressedTuple<Ts...>, | |||
| absl::index_sequence<I...>, | |||
| false> | |||
| // We use the dummy identity function as above... | |||
| : Storage<Ts, std::integral_constant<size_t, I>::value, false>... | |||
| { | |||
| constexpr CompressedTupleImpl() = default; | |||
| template<typename... Vs> | |||
| explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args) : | |||
| Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... | |||
| { | |||
| } | |||
| friend CompressedTuple<Ts...>; | |||
| }; | |||
| std::false_type Or(std::initializer_list<std::false_type>); | |||
| std::true_type Or(std::initializer_list<bool>); | |||
| // MSVC requires this to be done separately rather than within the declaration | |||
| // of CompressedTuple below. | |||
| template<typename... Ts> | |||
| constexpr bool ShouldAnyUseBase() | |||
| { | |||
| return decltype(Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){}; | |||
| } | |||
| template<typename T, typename V> | |||
| using TupleElementMoveConstructible = | |||
| typename std::conditional<std::is_reference<T>::value, std::is_convertible<V, T>, std::is_constructible<T, V&&>>::type; | |||
| template<bool SizeMatches, class T, class... Vs> | |||
| struct TupleMoveConstructible : std::false_type | |||
| { | |||
| }; | |||
| template<class... Ts, class... Vs> | |||
| struct TupleMoveConstructible<true, CompressedTuple<Ts...>, Vs...> : std::integral_constant<bool, absl::conjunction<TupleElementMoveConstructible<Ts, Vs&&>...>::value> | |||
| { | |||
| }; | |||
| template<typename T> | |||
| struct compressed_tuple_size; | |||
| template<typename... Es> | |||
| struct compressed_tuple_size<CompressedTuple<Es...>> : public std::integral_constant<std::size_t, sizeof...(Es)> | |||
| { | |||
| }; | |||
| template<class T, class... Vs> | |||
| struct TupleItemsMoveConstructible : std::integral_constant<bool, TupleMoveConstructible<compressed_tuple_size<T>::value == sizeof...(Vs), T, Vs...>::value> | |||
| { | |||
| }; | |||
| } // namespace internal_compressed_tuple | |||
| // Helper class to perform the Empty Base Class Optimization. | |||
| // Ts can contain classes and non-classes, empty or not. For the ones that | |||
| // are empty classes, we perform the CompressedTuple. If all types in Ts are | |||
| // empty classes, then CompressedTuple<Ts...> is itself an empty class. (This | |||
| // does not apply when one or more of those empty classes is itself an empty | |||
| // CompressedTuple.) | |||
| // | |||
| // To access the members, use member .get<N>() function. | |||
| // | |||
| // Eg: | |||
| // absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2, | |||
| // t3); | |||
| // assert(value.get<0>() == 7); | |||
| // T1& t1 = value.get<1>(); | |||
| // const T2& t2 = value.get<2>(); | |||
| // ... | |||
| // | |||
| // https://en.cppreference.com/w/cpp/language/ebo | |||
| template<typename... Ts> | |||
| class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple : private internal_compressed_tuple::CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>, internal_compressed_tuple::ShouldAnyUseBase<Ts...>()> | |||
| { | |||
| private: | |||
| template<int I> | |||
| using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>; | |||
| template<int I> | |||
| using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>; | |||
| public: | |||
| // There seems to be a bug in MSVC dealing in which using '=default' here will | |||
| // cause the compiler to ignore the body of other constructors. The work- | |||
| // around is to explicitly implement the default constructor. | |||
| #if defined(_MSC_VER) | |||
| constexpr CompressedTuple() : | |||
| CompressedTuple::CompressedTupleImpl() | |||
| { | |||
| } | |||
| #else | |||
| constexpr CompressedTuple() = default; | |||
| #endif | |||
| explicit constexpr CompressedTuple(const Ts&... base) : | |||
| CompressedTuple::CompressedTupleImpl(absl::in_place, base...) | |||
| { | |||
| } | |||
| template<typename First, typename... Vs, absl::enable_if_t<absl::conjunction< | |||
| // Ensure we are not hiding default copy/move constructors. | |||
| absl::negation<std::is_same<void(CompressedTuple), void(absl::decay_t<First>)>>, | |||
| internal_compressed_tuple::TupleItemsMoveConstructible<CompressedTuple<Ts...>, First, Vs...>>::value, | |||
| bool> = true> | |||
| explicit constexpr CompressedTuple(First&& first, Vs&&... base) : | |||
| CompressedTuple::CompressedTupleImpl(absl::in_place, absl::forward<First>(first), absl::forward<Vs>(base)...) | |||
| { | |||
| } | |||
| template<int I> | |||
| ElemT<I>& get() & | |||
| { | |||
| return StorageT<I>::get(); | |||
| } | |||
| template<int I> | |||
| constexpr const ElemT<I>& get() const& | |||
| { | |||
| return StorageT<I>::get(); | |||
| } | |||
| template<int I> | |||
| ElemT<I>&& get() && | |||
| { | |||
| return std::move(*this).StorageT<I>::get(); | |||
| } | |||
| template<int I> | |||
| constexpr const ElemT<I>&& get() const&& | |||
| { | |||
| return absl::move(*this).StorageT<I>::get(); | |||
| } | |||
| }; | |||
| // Explicit specialization for a zero-element tuple | |||
| // (needed to avoid ambiguous overloads for the default constructor). | |||
| template<> | |||
| class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> | |||
| { | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC | |||
| #endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_ | |||
| @@ -0,0 +1,499 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ | |||
| #include <cassert> | |||
| #include <cstddef> | |||
| #include <memory> | |||
| #include <new> | |||
| #include <tuple> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/base/config.h" | |||
| #include "absl/memory/memory.h" | |||
| #include "absl/meta/type_traits.h" | |||
| #include "absl/utility/utility.h" | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| #include <sanitizer/asan_interface.h> | |||
| #endif | |||
| #ifdef ABSL_HAVE_MEMORY_SANITIZER | |||
| #include <sanitizer/msan_interface.h> | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<size_t Alignment> | |||
| struct alignas(Alignment) AlignedType | |||
| { | |||
| }; | |||
| // Allocates at least n bytes aligned to the specified alignment. | |||
| // Alignment must be a power of 2. It must be positive. | |||
| // | |||
| // Note that many allocators don't honor alignment requirements above certain | |||
| // threshold (usually either alignof(std::max_align_t) or alignof(void*)). | |||
| // Allocate() doesn't apply alignment corrections. If the underlying allocator | |||
| // returns insufficiently alignment pointer, that's what you are going to get. | |||
| template<size_t Alignment, class Alloc> | |||
| void* Allocate(Alloc* alloc, size_t n) | |||
| { | |||
| static_assert(Alignment > 0, ""); | |||
| assert(n && "n must be positive"); | |||
| using M = AlignedType<Alignment>; | |||
| using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | |||
| using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | |||
| // On macOS, "mem_alloc" is a #define with one argument defined in | |||
| // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it | |||
| // with the "foo(bar)" syntax. | |||
| A my_mem_alloc(*alloc); | |||
| void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); | |||
| assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 && "allocator does not respect alignment"); | |||
| return p; | |||
| } | |||
| // The pointer must have been previously obtained by calling | |||
| // Allocate<Alignment>(alloc, n). | |||
| template<size_t Alignment, class Alloc> | |||
| void Deallocate(Alloc* alloc, void* p, size_t n) | |||
| { | |||
| static_assert(Alignment > 0, ""); | |||
| assert(n && "n must be positive"); | |||
| using M = AlignedType<Alignment>; | |||
| using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; | |||
| using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; | |||
| // On macOS, "mem_alloc" is a #define with one argument defined in | |||
| // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it | |||
| // with the "foo(bar)" syntax. | |||
| A my_mem_alloc(*alloc); | |||
| AT::deallocate(my_mem_alloc, static_cast<M*>(p), (n + sizeof(M) - 1) / sizeof(M)); | |||
| } | |||
| namespace memory_internal | |||
| { | |||
| // Constructs T into uninitialized storage pointed by `ptr` using the args | |||
| // specified in the tuple. | |||
| template<class Alloc, class T, class Tuple, size_t... I> | |||
| void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, absl::index_sequence<I...>) | |||
| { | |||
| absl::allocator_traits<Alloc>::construct( | |||
| *alloc, ptr, std::get<I>(std::forward<Tuple>(t))... | |||
| ); | |||
| } | |||
| template<class T, class F> | |||
| struct WithConstructedImplF | |||
| { | |||
| template<class... Args> | |||
| decltype(std::declval<F>()(std::declval<T>())) operator()( | |||
| Args&&... args | |||
| ) const | |||
| { | |||
| return std::forward<F>(f)(T(std::forward<Args>(args)...)); | |||
| } | |||
| F&& f; | |||
| }; | |||
| template<class T, class Tuple, size_t... Is, class F> | |||
| decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl( | |||
| Tuple&& t, absl::index_sequence<Is...>, F&& f | |||
| ) | |||
| { | |||
| return WithConstructedImplF<T, F>{std::forward<F>(f)}( | |||
| std::get<Is>(std::forward<Tuple>(t))... | |||
| ); | |||
| } | |||
| template<class T, size_t... Is> | |||
| auto TupleRefImpl(T&& t, absl::index_sequence<Is...>) | |||
| -> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) | |||
| { | |||
| return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...); | |||
| } | |||
| // Returns a tuple of references to the elements of the input tuple. T must be a | |||
| // tuple. | |||
| template<class T> | |||
| auto TupleRef(T&& t) -> decltype(TupleRefImpl(std::forward<T>(t), absl::make_index_sequence<std::tuple_size<typename std::decay<T>::type>::value>())) | |||
| { | |||
| return TupleRefImpl( | |||
| std::forward<T>(t), | |||
| absl::make_index_sequence< | |||
| std::tuple_size<typename std::decay<T>::type>::value>() | |||
| ); | |||
| } | |||
| template<class F, class K, class V> | |||
| decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct, std::declval<std::tuple<K>>(), std::declval<V>())) | |||
| DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) | |||
| { | |||
| const auto& key = std::get<0>(p.first); | |||
| return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first), std::move(p.second)); | |||
| } | |||
| } // namespace memory_internal | |||
| // Constructs T into uninitialized storage pointed by `ptr` using the args | |||
| // specified in the tuple. | |||
| template<class Alloc, class T, class Tuple> | |||
| void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) | |||
| { | |||
| memory_internal::ConstructFromTupleImpl( | |||
| alloc, ptr, std::forward<Tuple>(t), absl::make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>() | |||
| ); | |||
| } | |||
| // Constructs T using the args specified in the tuple and calls F with the | |||
| // constructed value. | |||
| template<class T, class Tuple, class F> | |||
| decltype(std::declval<F>()(std::declval<T>())) WithConstructed( | |||
| Tuple&& t, F&& f | |||
| ) | |||
| { | |||
| return memory_internal::WithConstructedImpl<T>( | |||
| std::forward<Tuple>(t), | |||
| absl::make_index_sequence< | |||
| std::tuple_size<typename std::decay<Tuple>::type>::value>(), | |||
| std::forward<F>(f) | |||
| ); | |||
| } | |||
| // Given arguments of an std::pair's consructor, PairArgs() returns a pair of | |||
| // tuples with references to the passed arguments. The tuples contain | |||
| // constructor arguments for the first and the second elements of the pair. | |||
| // | |||
| // The following two snippets are equivalent. | |||
| // | |||
| // 1. std::pair<F, S> p(args...); | |||
| // | |||
| // 2. auto a = PairArgs(args...); | |||
| // std::pair<F, S> p(std::piecewise_construct, | |||
| // std::move(a.first), std::move(a.second)); | |||
| inline std::pair<std::tuple<>, std::tuple<>> PairArgs() | |||
| { | |||
| return {}; | |||
| } | |||
| template<class F, class S> | |||
| std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) | |||
| { | |||
| return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)), std::forward_as_tuple(std::forward<S>(s))}; | |||
| } | |||
| template<class F, class S> | |||
| std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs( | |||
| const std::pair<F, S>& p | |||
| ) | |||
| { | |||
| return PairArgs(p.first, p.second); | |||
| } | |||
| template<class F, class S> | |||
| std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) | |||
| { | |||
| return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second)); | |||
| } | |||
| template<class F, class S> | |||
| auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) | |||
| -> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), memory_internal::TupleRef(std::forward<S>(s)))) | |||
| { | |||
| return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), memory_internal::TupleRef(std::forward<S>(s))); | |||
| } | |||
| // A helper function for implementing apply() in map policies. | |||
| template<class F, class... Args> | |||
| auto DecomposePair(F&& f, Args&&... args) | |||
| -> decltype(memory_internal::DecomposePairImpl( | |||
| std::forward<F>(f), PairArgs(std::forward<Args>(args)...) | |||
| )) | |||
| { | |||
| return memory_internal::DecomposePairImpl( | |||
| std::forward<F>(f), PairArgs(std::forward<Args>(args)...) | |||
| ); | |||
| } | |||
| // A helper function for implementing apply() in set policies. | |||
| template<class F, class Arg> | |||
| decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>())) | |||
| DecomposeValue(F&& f, Arg&& arg) | |||
| { | |||
| const auto& key = arg; | |||
| return std::forward<F>(f)(key, std::forward<Arg>(arg)); | |||
| } | |||
| // Helper functions for asan and msan. | |||
| inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) | |||
| { | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| ASAN_POISON_MEMORY_REGION(m, s); | |||
| #endif | |||
| #ifdef ABSL_HAVE_MEMORY_SANITIZER | |||
| __msan_poison(m, s); | |||
| #endif | |||
| (void)m; | |||
| (void)s; | |||
| } | |||
| inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) | |||
| { | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| ASAN_UNPOISON_MEMORY_REGION(m, s); | |||
| #endif | |||
| #ifdef ABSL_HAVE_MEMORY_SANITIZER | |||
| __msan_unpoison(m, s); | |||
| #endif | |||
| (void)m; | |||
| (void)s; | |||
| } | |||
| template<typename T> | |||
| inline void SanitizerPoisonObject(const T* object) | |||
| { | |||
| SanitizerPoisonMemoryRegion(object, sizeof(T)); | |||
| } | |||
| template<typename T> | |||
| inline void SanitizerUnpoisonObject(const T* object) | |||
| { | |||
| SanitizerUnpoisonMemoryRegion(object, sizeof(T)); | |||
| } | |||
| namespace memory_internal | |||
| { | |||
| // If Pair is a standard-layout type, OffsetOf<Pair>::kFirst and | |||
| // OffsetOf<Pair>::kSecond are equivalent to offsetof(Pair, first) and | |||
| // offsetof(Pair, second) respectively. Otherwise they are -1. | |||
| // | |||
| // The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout | |||
| // type, which is non-portable. | |||
| template<class Pair, class = std::true_type> | |||
| struct OffsetOf | |||
| { | |||
| static constexpr size_t kFirst = static_cast<size_t>(-1); | |||
| static constexpr size_t kSecond = static_cast<size_t>(-1); | |||
| }; | |||
| template<class Pair> | |||
| struct OffsetOf<Pair, typename std::is_standard_layout<Pair>::type> | |||
| { | |||
| static constexpr size_t kFirst = offsetof(Pair, first); | |||
| static constexpr size_t kSecond = offsetof(Pair, second); | |||
| }; | |||
| template<class K, class V> | |||
| struct IsLayoutCompatible | |||
| { | |||
| private: | |||
| struct Pair | |||
| { | |||
| K first; | |||
| V second; | |||
| }; | |||
| // Is P layout-compatible with Pair? | |||
| template<class P> | |||
| static constexpr bool LayoutCompatible() | |||
| { | |||
| return std::is_standard_layout<P>() && sizeof(P) == sizeof(Pair) && | |||
| alignof(P) == alignof(Pair) && | |||
| memory_internal::OffsetOf<P>::kFirst == | |||
| memory_internal::OffsetOf<Pair>::kFirst && | |||
| memory_internal::OffsetOf<P>::kSecond == | |||
| memory_internal::OffsetOf<Pair>::kSecond; | |||
| } | |||
| public: | |||
| // Whether pair<const K, V> and pair<K, V> are layout-compatible. If they are, | |||
| // then it is safe to store them in a union and read from either. | |||
| static constexpr bool value = std::is_standard_layout<K>() && | |||
| std::is_standard_layout<Pair>() && | |||
| memory_internal::OffsetOf<Pair>::kFirst == 0 && | |||
| LayoutCompatible<std::pair<K, V>>() && | |||
| LayoutCompatible<std::pair<const K, V>>(); | |||
| }; | |||
| } // namespace memory_internal | |||
| // The internal storage type for key-value containers like flat_hash_map. | |||
| // | |||
| // It is convenient for the value_type of a flat_hash_map<K, V> to be | |||
| // pair<const K, V>; the "const K" prevents accidental modification of the key | |||
| // when dealing with the reference returned from find() and similar methods. | |||
| // However, this creates other problems; we want to be able to emplace(K, V) | |||
| // efficiently with move operations, and similarly be able to move a | |||
| // pair<K, V> in insert(). | |||
| // | |||
| // The solution is this union, which aliases the const and non-const versions | |||
| // of the pair. This also allows flat_hash_map<const K, V> to work, even though | |||
| // that has the same efficiency issues with move in emplace() and insert() - | |||
| // but people do it anyway. | |||
| // | |||
| // If kMutableKeys is false, only the value member can be accessed. | |||
| // | |||
| // If kMutableKeys is true, key can be accessed through all slots while value | |||
| // and mutable_value must be accessed only via INITIALIZED slots. Slots are | |||
| // created and destroyed via mutable_value so that the key can be moved later. | |||
| // | |||
| // Accessing one of the union fields while the other is active is safe as | |||
| // long as they are layout-compatible, which is guaranteed by the definition of | |||
| // kMutableKeys. For C++11, the relevant section of the standard is | |||
| // https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) | |||
| template<class K, class V> | |||
| union map_slot_type | |||
| { | |||
| map_slot_type() | |||
| { | |||
| } | |||
| ~map_slot_type() = delete; | |||
| using value_type = std::pair<const K, V>; | |||
| using mutable_value_type = | |||
| std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; | |||
| value_type value; | |||
| mutable_value_type mutable_value; | |||
| absl::remove_const_t<K> key; | |||
| }; | |||
| template<class K, class V> | |||
| struct map_slot_policy | |||
| { | |||
| using slot_type = map_slot_type<K, V>; | |||
| using value_type = std::pair<const K, V>; | |||
| using mutable_value_type = std::pair<K, V>; | |||
| private: | |||
| static void emplace(slot_type* slot) | |||
| { | |||
| // The construction of union doesn't do anything at runtime but it allows us | |||
| // to access its members without violating aliasing rules. | |||
| new (slot) slot_type; | |||
| } | |||
| // If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one | |||
| // or the other via slot_type. We are also free to access the key via | |||
| // slot_type::key in this case. | |||
| using kMutableKeys = memory_internal::IsLayoutCompatible<K, V>; | |||
| public: | |||
| static value_type& element(slot_type* slot) | |||
| { | |||
| return slot->value; | |||
| } | |||
| static const value_type& element(const slot_type* slot) | |||
| { | |||
| return slot->value; | |||
| } | |||
| // When C++17 is available, we can use std::launder to provide mutable | |||
| // access to the key for use in node handle. | |||
| #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 | |||
| static K& mutable_key(slot_type* slot) | |||
| { | |||
| // Still check for kMutableKeys so that we can avoid calling std::launder | |||
| // unless necessary because it can interfere with optimizations. | |||
| return kMutableKeys::value ? slot->key : *std::launder(const_cast<K*>(std::addressof(slot->value.first))); | |||
| } | |||
| #else // !(defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606) | |||
| static const K& mutable_key(slot_type* slot) | |||
| { | |||
| return key(slot); | |||
| } | |||
| #endif | |||
| static const K& key(const slot_type* slot) | |||
| { | |||
| return kMutableKeys::value ? slot->key : slot->value.first; | |||
| } | |||
| template<class Allocator, class... Args> | |||
| static void construct(Allocator* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| emplace(slot); | |||
| if (kMutableKeys::value) | |||
| { | |||
| absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value, std::forward<Args>(args)...); | |||
| } | |||
| else | |||
| { | |||
| absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, std::forward<Args>(args)...); | |||
| } | |||
| } | |||
| // Construct this slot by moving from another slot. | |||
| template<class Allocator> | |||
| static void construct(Allocator* alloc, slot_type* slot, slot_type* other) | |||
| { | |||
| emplace(slot); | |||
| if (kMutableKeys::value) | |||
| { | |||
| absl::allocator_traits<Allocator>::construct( | |||
| *alloc, &slot->mutable_value, std::move(other->mutable_value) | |||
| ); | |||
| } | |||
| else | |||
| { | |||
| absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, std::move(other->value)); | |||
| } | |||
| } | |||
| // Construct this slot by copying from another slot. | |||
| template<class Allocator> | |||
| static void construct(Allocator* alloc, slot_type* slot, const slot_type* other) | |||
| { | |||
| emplace(slot); | |||
| absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, other->value); | |||
| } | |||
| template<class Allocator> | |||
| static void destroy(Allocator* alloc, slot_type* slot) | |||
| { | |||
| if (kMutableKeys::value) | |||
| { | |||
| absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value); | |||
| } | |||
| else | |||
| { | |||
| absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value); | |||
| } | |||
| } | |||
| template<class Allocator> | |||
| static void transfer(Allocator* alloc, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| emplace(new_slot); | |||
| if (kMutableKeys::value) | |||
| { | |||
| absl::allocator_traits<Allocator>::construct( | |||
| *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value) | |||
| ); | |||
| } | |||
| else | |||
| { | |||
| absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value, std::move(old_slot->value)); | |||
| } | |||
| destroy(alloc, old_slot); | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ | |||
| @@ -0,0 +1,144 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ | |||
| #include <cstdint> | |||
| #include <memory> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // This is a stateful allocator, but the state lives outside of the | |||
| // allocator (in whatever test is using the allocator). This is odd | |||
| // but helps in tests where the allocator is propagated into nested | |||
| // containers - that chain of allocators uses the same state and is | |||
| // thus easier to query for aggregate allocation information. | |||
| template<typename T> | |||
| class CountingAllocator | |||
| { | |||
| public: | |||
| using Allocator = std::allocator<T>; | |||
| using AllocatorTraits = std::allocator_traits<Allocator>; | |||
| using value_type = typename AllocatorTraits::value_type; | |||
| using pointer = typename AllocatorTraits::pointer; | |||
| using const_pointer = typename AllocatorTraits::const_pointer; | |||
| using size_type = typename AllocatorTraits::size_type; | |||
| using difference_type = typename AllocatorTraits::difference_type; | |||
| CountingAllocator() = default; | |||
| explicit CountingAllocator(int64_t* bytes_used) : | |||
| bytes_used_(bytes_used) | |||
| { | |||
| } | |||
| CountingAllocator(int64_t* bytes_used, int64_t* instance_count) : | |||
| bytes_used_(bytes_used), | |||
| instance_count_(instance_count) | |||
| { | |||
| } | |||
| template<typename U> | |||
| CountingAllocator(const CountingAllocator<U>& x) : | |||
| bytes_used_(x.bytes_used_), | |||
| instance_count_(x.instance_count_) | |||
| { | |||
| } | |||
| pointer allocate( | |||
| size_type n, | |||
| typename AllocatorTraits::const_void_pointer hint = nullptr | |||
| ) | |||
| { | |||
| Allocator allocator; | |||
| pointer ptr = AllocatorTraits::allocate(allocator, n, hint); | |||
| if (bytes_used_ != nullptr) | |||
| { | |||
| *bytes_used_ += n * sizeof(T); | |||
| } | |||
| return ptr; | |||
| } | |||
| void deallocate(pointer p, size_type n) | |||
| { | |||
| Allocator allocator; | |||
| AllocatorTraits::deallocate(allocator, p, n); | |||
| if (bytes_used_ != nullptr) | |||
| { | |||
| *bytes_used_ -= n * sizeof(T); | |||
| } | |||
| } | |||
| template<typename U, typename... Args> | |||
| void construct(U* p, Args&&... args) | |||
| { | |||
| Allocator allocator; | |||
| AllocatorTraits::construct(allocator, p, std::forward<Args>(args)...); | |||
| if (instance_count_ != nullptr) | |||
| { | |||
| *instance_count_ += 1; | |||
| } | |||
| } | |||
| template<typename U> | |||
| void destroy(U* p) | |||
| { | |||
| Allocator allocator; | |||
| // Ignore GCC warning bug. | |||
| #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wuse-after-free" | |||
| #endif | |||
| AllocatorTraits::destroy(allocator, p); | |||
| #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| if (instance_count_ != nullptr) | |||
| { | |||
| *instance_count_ -= 1; | |||
| } | |||
| } | |||
| template<typename U> | |||
| class rebind | |||
| { | |||
| public: | |||
| using other = CountingAllocator<U>; | |||
| }; | |||
| friend bool operator==(const CountingAllocator& a, const CountingAllocator& b) | |||
| { | |||
| return a.bytes_used_ == b.bytes_used_ && | |||
| a.instance_count_ == b.instance_count_; | |||
| } | |||
| friend bool operator!=(const CountingAllocator& a, const CountingAllocator& b) | |||
| { | |||
| return !(a == b); | |||
| } | |||
| int64_t* bytes_used_ = nullptr; | |||
| int64_t* instance_count_ = nullptr; | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_COUNTING_ALLOCATOR_H_ | |||
| @@ -0,0 +1,195 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Define the default Hash and Eq functions for SwissTable containers. | |||
| // | |||
| // std::hash<T> and std::equal_to<T> are not appropriate hash and equal | |||
| // functions for SwissTable containers. There are two reasons for this. | |||
| // | |||
| // SwissTable containers are power of 2 sized containers: | |||
| // | |||
| // This means they use the lower bits of the hash value to find the slot for | |||
| // each entry. The typical hash function for integral types is the identity. | |||
| // This is a very weak hash function for SwissTable and any power of 2 sized | |||
| // hashtable implementation which will lead to excessive collisions. For | |||
| // SwissTable we use murmur3 style mixing to reduce collisions to a minimum. | |||
| // | |||
| // SwissTable containers support heterogeneous lookup: | |||
| // | |||
| // In order to make heterogeneous lookup work, hash and equal functions must be | |||
| // polymorphic. At the same time they have to satisfy the same requirements the | |||
| // C++ standard imposes on hash functions and equality operators. That is: | |||
| // | |||
| // if hash_default_eq<T>(a, b) returns true for any a and b of type T, then | |||
| // hash_default_hash<T>(a) must equal hash_default_hash<T>(b) | |||
| // | |||
| // For SwissTable containers this requirement is relaxed to allow a and b of | |||
| // any and possibly different types. Note that like the standard the hash and | |||
| // equal functions are still bound to T. This is important because some type U | |||
| // can be hashed by/tested for equality differently depending on T. A notable | |||
| // example is `const char*`. `const char*` is treated as a c-style string when | |||
| // the hash function is hash<std::string> but as a pointer when the hash | |||
| // function is hash<void*>. | |||
| // | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ | |||
| #include <stdint.h> | |||
| #include <cstddef> | |||
| #include <memory> | |||
| #include <string> | |||
| #include <type_traits> | |||
| #include "absl/base/config.h" | |||
| #include "absl/hash/hash.h" | |||
| #include "absl/strings/cord.h" | |||
| #include "absl/strings/string_view.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // The hash of an object of type T is computed by using absl::Hash. | |||
| template<class T, class E = void> | |||
| struct HashEq | |||
| { | |||
| using Hash = absl::Hash<T>; | |||
| using Eq = std::equal_to<T>; | |||
| }; | |||
| struct StringHash | |||
| { | |||
| using is_transparent = void; | |||
| size_t operator()(absl::string_view v) const | |||
| { | |||
| return absl::Hash<absl::string_view>{}(v); | |||
| } | |||
| size_t operator()(const absl::Cord& v) const | |||
| { | |||
| return absl::Hash<absl::Cord>{}(v); | |||
| } | |||
| }; | |||
| struct StringEq | |||
| { | |||
| using is_transparent = void; | |||
| bool operator()(absl::string_view lhs, absl::string_view rhs) const | |||
| { | |||
| return lhs == rhs; | |||
| } | |||
| bool operator()(const absl::Cord& lhs, const absl::Cord& rhs) const | |||
| { | |||
| return lhs == rhs; | |||
| } | |||
| bool operator()(const absl::Cord& lhs, absl::string_view rhs) const | |||
| { | |||
| return lhs == rhs; | |||
| } | |||
| bool operator()(absl::string_view lhs, const absl::Cord& rhs) const | |||
| { | |||
| return lhs == rhs; | |||
| } | |||
| }; | |||
| // Supports heterogeneous lookup for string-like elements. | |||
| struct StringHashEq | |||
| { | |||
| using Hash = StringHash; | |||
| using Eq = StringEq; | |||
| }; | |||
| template<> | |||
| struct HashEq<std::string> : StringHashEq | |||
| { | |||
| }; | |||
| template<> | |||
| struct HashEq<absl::string_view> : StringHashEq | |||
| { | |||
| }; | |||
| template<> | |||
| struct HashEq<absl::Cord> : StringHashEq | |||
| { | |||
| }; | |||
| // Supports heterogeneous lookup for pointers and smart pointers. | |||
| template<class T> | |||
| struct HashEq<T*> | |||
| { | |||
| struct Hash | |||
| { | |||
| using is_transparent = void; | |||
| template<class U> | |||
| size_t operator()(const U& ptr) const | |||
| { | |||
| return absl::Hash<const T*>{}(HashEq::ToPtr(ptr)); | |||
| } | |||
| }; | |||
| struct Eq | |||
| { | |||
| using is_transparent = void; | |||
| template<class A, class B> | |||
| bool operator()(const A& a, const B& b) const | |||
| { | |||
| return HashEq::ToPtr(a) == HashEq::ToPtr(b); | |||
| } | |||
| }; | |||
| private: | |||
| static const T* ToPtr(const T* ptr) | |||
| { | |||
| return ptr; | |||
| } | |||
| template<class U, class D> | |||
| static const T* ToPtr(const std::unique_ptr<U, D>& ptr) | |||
| { | |||
| return ptr.get(); | |||
| } | |||
| template<class U> | |||
| static const T* ToPtr(const std::shared_ptr<U>& ptr) | |||
| { | |||
| return ptr.get(); | |||
| } | |||
| }; | |||
| template<class T, class D> | |||
| struct HashEq<std::unique_ptr<T, D>> : HashEq<T*> | |||
| { | |||
| }; | |||
| template<class T> | |||
| struct HashEq<std::shared_ptr<T>> : HashEq<T*> | |||
| { | |||
| }; | |||
| // This header's visibility is restricted. If you need to access the default | |||
| // hasher please use the container's ::hasher alias instead. | |||
| // | |||
| // Example: typename Hash = typename absl::flat_hash_map<K, V>::hasher | |||
| template<class T> | |||
| using hash_default_hash = typename container_internal::HashEq<T>::Hash; | |||
| // This header's visibility is restricted. If you need to access the default | |||
| // key equal please use the container's ::key_equal alias instead. | |||
| // | |||
| // Example: typename Eq = typename absl::flat_hash_map<K, V, Hash>::key_equal | |||
| template<class T> | |||
| using hash_default_eq = typename container_internal::HashEq<T>::Eq; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASH_FUNCTION_DEFAULTS_H_ | |||
| @@ -0,0 +1,209 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Generates random values for testing. Specialized only for the few types we | |||
| // care about. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ | |||
| #include <stdint.h> | |||
| #include <algorithm> | |||
| #include <cassert> | |||
| #include <iosfwd> | |||
| #include <random> | |||
| #include <tuple> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include <vector> | |||
| #include "absl/container/internal/hash_policy_testing.h" | |||
| #include "absl/memory/memory.h" | |||
| #include "absl/meta/type_traits.h" | |||
| #include "absl/strings/string_view.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| namespace hash_internal | |||
| { | |||
| namespace generator_internal | |||
| { | |||
| template<class Container, class = void> | |||
| struct IsMap : std::false_type | |||
| { | |||
| }; | |||
| template<class Map> | |||
| struct IsMap<Map, absl::void_t<typename Map::mapped_type>> : std::true_type | |||
| { | |||
| }; | |||
| } // namespace generator_internal | |||
| std::mt19937_64* GetSharedRng(); | |||
| enum Enum | |||
| { | |||
| kEnumEmpty, | |||
| kEnumDeleted, | |||
| }; | |||
| enum class EnumClass : uint64_t | |||
| { | |||
| kEmpty, | |||
| kDeleted, | |||
| }; | |||
| inline std::ostream& operator<<(std::ostream& o, const EnumClass& ec) | |||
| { | |||
| return o << static_cast<uint64_t>(ec); | |||
| } | |||
| template<class T, class E = void> | |||
| struct Generator; | |||
| template<class T> | |||
| struct Generator<T, typename std::enable_if<std::is_integral<T>::value>::type> | |||
| { | |||
| T operator()() const | |||
| { | |||
| std::uniform_int_distribution<T> dist; | |||
| return dist(*GetSharedRng()); | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<Enum> | |||
| { | |||
| Enum operator()() const | |||
| { | |||
| std::uniform_int_distribution<typename std::underlying_type<Enum>::type> | |||
| dist; | |||
| while (true) | |||
| { | |||
| auto variate = dist(*GetSharedRng()); | |||
| if (variate != kEnumEmpty && variate != kEnumDeleted) | |||
| return static_cast<Enum>(variate); | |||
| } | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<EnumClass> | |||
| { | |||
| EnumClass operator()() const | |||
| { | |||
| std::uniform_int_distribution< | |||
| typename std::underlying_type<EnumClass>::type> | |||
| dist; | |||
| while (true) | |||
| { | |||
| EnumClass variate = static_cast<EnumClass>(dist(*GetSharedRng())); | |||
| if (variate != EnumClass::kEmpty && variate != EnumClass::kDeleted) | |||
| return static_cast<EnumClass>(variate); | |||
| } | |||
| } | |||
| }; | |||
| template<> | |||
| struct Generator<std::string> | |||
| { | |||
| std::string operator()() const; | |||
| }; | |||
| template<> | |||
| struct Generator<absl::string_view> | |||
| { | |||
| absl::string_view operator()() const; | |||
| }; | |||
| template<> | |||
| struct Generator<NonStandardLayout> | |||
| { | |||
| NonStandardLayout operator()() const | |||
| { | |||
| return NonStandardLayout(Generator<std::string>()()); | |||
| } | |||
| }; | |||
| template<class K, class V> | |||
| struct Generator<std::pair<K, V>> | |||
| { | |||
| std::pair<K, V> operator()() const | |||
| { | |||
| return std::pair<K, V>(Generator<typename std::decay<K>::type>()(), Generator<typename std::decay<V>::type>()()); | |||
| } | |||
| }; | |||
| template<class... Ts> | |||
| struct Generator<std::tuple<Ts...>> | |||
| { | |||
| std::tuple<Ts...> operator()() const | |||
| { | |||
| return std::tuple<Ts...>(Generator<typename std::decay<Ts>::type>()()...); | |||
| } | |||
| }; | |||
| template<class T> | |||
| struct Generator<std::unique_ptr<T>> | |||
| { | |||
| std::unique_ptr<T> operator()() const | |||
| { | |||
| return absl::make_unique<T>(Generator<T>()()); | |||
| } | |||
| }; | |||
| template<class U> | |||
| struct Generator<U, absl::void_t<decltype(std::declval<U&>().key()), decltype(std::declval<U&>().value())>> : Generator<std::pair<typename std::decay<decltype(std::declval<U&>().key())>::type, typename std::decay<decltype(std::declval<U&>().value())>::type>> | |||
| { | |||
| }; | |||
| template<class Container> | |||
| using GeneratedType = decltype(std::declval<const Generator< | |||
| typename std::conditional<generator_internal::IsMap<Container>::value, typename Container::value_type, typename Container::key_type>::type>&>()()); | |||
| // Naive wrapper that performs a linear search of previous values. | |||
| // Beware this is O(SQR), which is reasonable for smaller kMaxValues. | |||
| template<class T, size_t kMaxValues = 64, class E = void> | |||
| struct UniqueGenerator | |||
| { | |||
| Generator<T, E> gen; | |||
| std::vector<T> values; | |||
| T operator()() | |||
| { | |||
| assert(values.size() < kMaxValues); | |||
| for (;;) | |||
| { | |||
| T value = gen(); | |||
| if (std::find(values.begin(), values.end(), value) == values.end()) | |||
| { | |||
| values.push_back(value); | |||
| return value; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| } // namespace hash_internal | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASH_GENERATOR_TESTING_H_ | |||
| @@ -0,0 +1,240 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Utilities to help tests verify that hash tables properly handle stateful | |||
| // allocators and hash functions. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ | |||
| #include <cstdlib> | |||
| #include <limits> | |||
| #include <memory> | |||
| #include <ostream> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include <vector> | |||
| #include "absl/hash/hash.h" | |||
| #include "absl/strings/string_view.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| namespace hash_testing_internal | |||
| { | |||
| template<class Derived> | |||
| struct WithId | |||
| { | |||
| WithId() : | |||
| id_(next_id<Derived>()) | |||
| { | |||
| } | |||
| WithId(const WithId& that) : | |||
| id_(that.id_) | |||
| { | |||
| } | |||
| WithId(WithId&& that) : | |||
| id_(that.id_) | |||
| { | |||
| that.id_ = 0; | |||
| } | |||
| WithId& operator=(const WithId& that) | |||
| { | |||
| id_ = that.id_; | |||
| return *this; | |||
| } | |||
| WithId& operator=(WithId&& that) | |||
| { | |||
| id_ = that.id_; | |||
| that.id_ = 0; | |||
| return *this; | |||
| } | |||
| size_t id() const | |||
| { | |||
| return id_; | |||
| } | |||
| friend bool operator==(const WithId& a, const WithId& b) | |||
| { | |||
| return a.id_ == b.id_; | |||
| } | |||
| friend bool operator!=(const WithId& a, const WithId& b) | |||
| { | |||
| return !(a == b); | |||
| } | |||
| protected: | |||
| explicit WithId(size_t id) : | |||
| id_(id) | |||
| { | |||
| } | |||
| private: | |||
| size_t id_; | |||
| template<class T> | |||
| static size_t next_id() | |||
| { | |||
| // 0 is reserved for moved from state. | |||
| static size_t gId = 1; | |||
| return gId++; | |||
| } | |||
| }; | |||
| } // namespace hash_testing_internal | |||
| struct NonStandardLayout | |||
| { | |||
| NonStandardLayout() | |||
| { | |||
| } | |||
| explicit NonStandardLayout(std::string s) : | |||
| value(std::move(s)) | |||
| { | |||
| } | |||
| virtual ~NonStandardLayout() | |||
| { | |||
| } | |||
| friend bool operator==(const NonStandardLayout& a, const NonStandardLayout& b) | |||
| { | |||
| return a.value == b.value; | |||
| } | |||
| friend bool operator!=(const NonStandardLayout& a, const NonStandardLayout& b) | |||
| { | |||
| return a.value != b.value; | |||
| } | |||
| template<typename H> | |||
| friend H AbslHashValue(H h, const NonStandardLayout& v) | |||
| { | |||
| return H::combine(std::move(h), v.value); | |||
| } | |||
| std::string value; | |||
| }; | |||
| struct StatefulTestingHash : absl::container_internal::hash_testing_internal::WithId<StatefulTestingHash> | |||
| { | |||
| template<class T> | |||
| size_t operator()(const T& t) const | |||
| { | |||
| return absl::Hash<T>{}(t); | |||
| } | |||
| }; | |||
| struct StatefulTestingEqual : absl::container_internal::hash_testing_internal::WithId<StatefulTestingEqual> | |||
| { | |||
| template<class T, class U> | |||
| bool operator()(const T& t, const U& u) const | |||
| { | |||
| return t == u; | |||
| } | |||
| }; | |||
| // It is expected that Alloc() == Alloc() for all allocators so we cannot use | |||
| // WithId base. We need to explicitly assign ids. | |||
| template<class T = int> | |||
| struct Alloc : std::allocator<T> | |||
| { | |||
| using propagate_on_container_swap = std::true_type; | |||
| // Using old paradigm for this to ensure compatibility. | |||
| explicit Alloc(size_t id = 0) : | |||
| id_(id) | |||
| { | |||
| } | |||
| Alloc(const Alloc&) = default; | |||
| Alloc& operator=(const Alloc&) = default; | |||
| template<class U> | |||
| Alloc(const Alloc<U>& that) : | |||
| std::allocator<T>(that), | |||
| id_(that.id()) | |||
| { | |||
| } | |||
| template<class U> | |||
| struct rebind | |||
| { | |||
| using other = Alloc<U>; | |||
| }; | |||
| size_t id() const | |||
| { | |||
| return id_; | |||
| } | |||
| friend bool operator==(const Alloc& a, const Alloc& b) | |||
| { | |||
| return a.id_ == b.id_; | |||
| } | |||
| friend bool operator!=(const Alloc& a, const Alloc& b) | |||
| { | |||
| return !(a == b); | |||
| } | |||
| private: | |||
| size_t id_ = (std::numeric_limits<size_t>::max)(); | |||
| }; | |||
| template<class Map> | |||
| auto items(const Map& m) -> std::vector< | |||
| std::pair<typename Map::key_type, typename Map::mapped_type>> | |||
| { | |||
| using std::get; | |||
| std::vector<std::pair<typename Map::key_type, typename Map::mapped_type>> res; | |||
| res.reserve(m.size()); | |||
| for (const auto& v : m) | |||
| res.emplace_back(get<0>(v), get<1>(v)); | |||
| return res; | |||
| } | |||
| template<class Set> | |||
| auto keys(const Set& s) | |||
| -> std::vector<typename std::decay<typename Set::key_type>::type> | |||
| { | |||
| std::vector<typename std::decay<typename Set::key_type>::type> res; | |||
| res.reserve(s.size()); | |||
| for (const auto& v : s) | |||
| res.emplace_back(v); | |||
| return res; | |||
| } | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| // ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions | |||
| // where the unordered containers are missing certain constructors that | |||
| // take allocator arguments. This test is defined ad-hoc for the platforms | |||
| // we care about (notably Crosstool 17) because libstdcxx's useless | |||
| // versioning scheme precludes a more principled solution. | |||
| // From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html) | |||
| // "the unordered associative containers in <unordered_map> and <unordered_set> | |||
| // meet the allocator-aware container requirements;" | |||
| #if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425) || \ | |||
| (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)) | |||
| #define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 0 | |||
| #else | |||
| #define ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS 1 | |||
| #endif | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TESTING_H_ | |||
| @@ -0,0 +1,226 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ | |||
| #include <cstddef> | |||
| #include <memory> | |||
| #include <new> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/meta/type_traits.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // Defines how slots are initialized/destroyed/moved. | |||
| template<class Policy, class = void> | |||
| struct hash_policy_traits | |||
| { | |||
| // The type of the keys stored in the hashtable. | |||
| using key_type = typename Policy::key_type; | |||
| private: | |||
| struct ReturnKey | |||
| { | |||
| // When C++17 is available, we can use std::launder to provide mutable | |||
| // access to the key for use in node handle. | |||
| #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 | |||
| template<class Key, absl::enable_if_t<std::is_lvalue_reference<Key>::value, int> = 0> | |||
| static key_type& Impl(Key&& k, int) | |||
| { | |||
| return *std::launder( | |||
| const_cast<key_type*>(std::addressof(std::forward<Key>(k))) | |||
| ); | |||
| } | |||
| #endif | |||
| template<class Key> | |||
| static Key Impl(Key&& k, char) | |||
| { | |||
| return std::forward<Key>(k); | |||
| } | |||
| // When Key=T&, we forward the lvalue reference. | |||
| // When Key=T, we return by value to avoid a dangling reference. | |||
| // eg, for string_hash_map. | |||
| template<class Key, class... Args> | |||
| auto operator()(Key&& k, const Args&...) const | |||
| -> decltype(Impl(std::forward<Key>(k), 0)) | |||
| { | |||
| return Impl(std::forward<Key>(k), 0); | |||
| } | |||
| }; | |||
| template<class P = Policy, class = void> | |||
| struct ConstantIteratorsImpl : std::false_type | |||
| { | |||
| }; | |||
| template<class P> | |||
| struct ConstantIteratorsImpl<P, absl::void_t<typename P::constant_iterators>> : P::constant_iterators | |||
| { | |||
| }; | |||
| public: | |||
| // The actual object stored in the hash table. | |||
| using slot_type = typename Policy::slot_type; | |||
| // The argument type for insertions into the hashtable. This is different | |||
| // from value_type for increased performance. See initializer_list constructor | |||
| // and insert() member functions for more details. | |||
| using init_type = typename Policy::init_type; | |||
| using reference = decltype(Policy::element(std::declval<slot_type*>())); | |||
| using pointer = typename std::remove_reference<reference>::type*; | |||
| using value_type = typename std::remove_reference<reference>::type; | |||
| // Policies can set this variable to tell raw_hash_set that all iterators | |||
| // should be constant, even `iterator`. This is useful for set-like | |||
| // containers. | |||
| // Defaults to false if not provided by the policy. | |||
| using constant_iterators = ConstantIteratorsImpl<>; | |||
| // PRECONDITION: `slot` is UNINITIALIZED | |||
| // POSTCONDITION: `slot` is INITIALIZED | |||
| template<class Alloc, class... Args> | |||
| static void construct(Alloc* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| Policy::construct(alloc, slot, std::forward<Args>(args)...); | |||
| } | |||
| // PRECONDITION: `slot` is INITIALIZED | |||
| // POSTCONDITION: `slot` is UNINITIALIZED | |||
| template<class Alloc> | |||
| static void destroy(Alloc* alloc, slot_type* slot) | |||
| { | |||
| Policy::destroy(alloc, slot); | |||
| } | |||
| // Transfers the `old_slot` to `new_slot`. Any memory allocated by the | |||
| // allocator inside `old_slot` to `new_slot` can be transferred. | |||
| // | |||
| // OPTIONAL: defaults to: | |||
| // | |||
| // clone(new_slot, std::move(*old_slot)); | |||
| // destroy(old_slot); | |||
| // | |||
| // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED | |||
| // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is | |||
| // UNINITIALIZED | |||
| template<class Alloc> | |||
| static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| transfer_impl(alloc, new_slot, old_slot, 0); | |||
| } | |||
| // PRECONDITION: `slot` is INITIALIZED | |||
| // POSTCONDITION: `slot` is INITIALIZED | |||
| template<class P = Policy> | |||
| static auto element(slot_type* slot) -> decltype(P::element(slot)) | |||
| { | |||
| return P::element(slot); | |||
| } | |||
| // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. | |||
| // | |||
| // If `slot` is nullptr, returns the constant amount of memory owned by any | |||
| // full slot or -1 if slots own variable amounts of memory. | |||
| // | |||
| // PRECONDITION: `slot` is INITIALIZED or nullptr | |||
| template<class P = Policy> | |||
| static size_t space_used(const slot_type* slot) | |||
| { | |||
| return P::space_used(slot); | |||
| } | |||
| // Provides generalized access to the key for elements, both for elements in | |||
| // the table and for elements that have not yet been inserted (or even | |||
| // constructed). We would like an API that allows us to say: `key(args...)` | |||
| // but we cannot do that for all cases, so we use this more general API that | |||
| // can be used for many things, including the following: | |||
| // | |||
| // - Given an element in a table, get its key. | |||
| // - Given an element initializer, get its key. | |||
| // - Given `emplace()` arguments, get the element key. | |||
| // | |||
| // Implementations of this must adhere to a very strict technical | |||
| // specification around aliasing and consuming arguments: | |||
| // | |||
| // Let `value_type` be the result type of `element()` without ref- and | |||
| // cv-qualifiers. The first argument is a functor, the rest are constructor | |||
| // arguments for `value_type`. Returns `std::forward<F>(f)(k, xs...)`, where | |||
| // `k` is the element key, and `xs...` are the new constructor arguments for | |||
| // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias | |||
| // `ts...`. The key won't be touched once `xs...` are used to construct an | |||
| // element; `ts...` won't be touched at all, which allows `apply()` to consume | |||
| // any rvalues among them. | |||
| // | |||
| // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not | |||
| // trigger a hard compile error unless it originates from `f`. In other words, | |||
| // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not | |||
| // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. | |||
| // | |||
| // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, | |||
| // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. | |||
| template<class F, class... Ts, class P = Policy> | |||
| static auto apply(F&& f, Ts&&... ts) | |||
| -> decltype(P::apply(std::forward<F>(f), std::forward<Ts>(ts)...)) | |||
| { | |||
| return P::apply(std::forward<F>(f), std::forward<Ts>(ts)...); | |||
| } | |||
| // Returns the "key" portion of the slot. | |||
| // Used for node handle manipulation. | |||
| template<class P = Policy> | |||
| static auto mutable_key(slot_type* slot) | |||
| -> decltype(P::apply(ReturnKey(), element(slot))) | |||
| { | |||
| return P::apply(ReturnKey(), element(slot)); | |||
| } | |||
| // Returns the "value" (as opposed to the "key") portion of the element. Used | |||
| // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. | |||
| template<class T, class P = Policy> | |||
| static auto value(T* elem) -> decltype(P::value(elem)) | |||
| { | |||
| return P::value(elem); | |||
| } | |||
| private: | |||
| // Use auto -> decltype as an enabler. | |||
| template<class Alloc, class P = Policy> | |||
| static auto transfer_impl(Alloc* alloc, slot_type* new_slot, slot_type* old_slot, int) | |||
| -> decltype((void)P::transfer(alloc, new_slot, old_slot)) | |||
| { | |||
| P::transfer(alloc, new_slot, old_slot); | |||
| } | |||
| template<class Alloc> | |||
| static void transfer_impl(Alloc* alloc, slot_type* new_slot, slot_type* old_slot, char) | |||
| { | |||
| construct(alloc, new_slot, std::move(element(old_slot))); | |||
| destroy(alloc, old_slot); | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASH_POLICY_TRAITS_H_ | |||
| @@ -0,0 +1,122 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // This library provides APIs to debug the probing behavior of hash tables. | |||
| // | |||
| // In general, the probing behavior is a black box for users and only the | |||
| // side effects can be measured in the form of performance differences. | |||
| // These APIs give a glimpse on the actual behavior of the probing algorithms in | |||
| // these hashtables given a specified hash function and a set of elements. | |||
| // | |||
| // The probe count distribution can be used to assess the quality of the hash | |||
| // function for that particular hash table. Note that a hash function that | |||
| // performs well in one hash table implementation does not necessarily performs | |||
| // well in a different one. | |||
| // | |||
| // This library supports std::unordered_{set,map}, dense_hash_{set,map} and | |||
| // absl::{flat,node,string}_hash_{set,map}. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ | |||
| #include <cstddef> | |||
| #include <algorithm> | |||
| #include <type_traits> | |||
| #include <vector> | |||
| #include "absl/container/internal/hashtable_debug_hooks.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // Returns the number of probes required to lookup `key`. Returns 0 for a | |||
| // search with no collisions. Higher values mean more hash collisions occurred; | |||
| // however, the exact meaning of this number varies according to the container | |||
| // type. | |||
| template<typename C> | |||
| size_t GetHashtableDebugNumProbes( | |||
| const C& c, const typename C::key_type& key | |||
| ) | |||
| { | |||
| return absl::container_internal::hashtable_debug_internal:: | |||
| HashtableDebugAccess<C>::GetNumProbes(c, key); | |||
| } | |||
| // Gets a histogram of the number of probes for each elements in the container. | |||
| // The sum of all the values in the vector is equal to container.size(). | |||
| template<typename C> | |||
| std::vector<size_t> GetHashtableDebugNumProbesHistogram(const C& container) | |||
| { | |||
| std::vector<size_t> v; | |||
| for (auto it = container.begin(); it != container.end(); ++it) | |||
| { | |||
| size_t num_probes = GetHashtableDebugNumProbes( | |||
| container, | |||
| absl::container_internal::hashtable_debug_internal::GetKey<C>(*it, 0) | |||
| ); | |||
| v.resize((std::max)(v.size(), num_probes + 1)); | |||
| v[num_probes]++; | |||
| } | |||
| return v; | |||
| } | |||
| struct HashtableDebugProbeSummary | |||
| { | |||
| size_t total_elements; | |||
| size_t total_num_probes; | |||
| double mean; | |||
| }; | |||
| // Gets a summary of the probe count distribution for the elements in the | |||
| // container. | |||
| template<typename C> | |||
| HashtableDebugProbeSummary GetHashtableDebugProbeSummary(const C& container) | |||
| { | |||
| auto probes = GetHashtableDebugNumProbesHistogram(container); | |||
| HashtableDebugProbeSummary summary = {}; | |||
| for (size_t i = 0; i < probes.size(); ++i) | |||
| { | |||
| summary.total_elements += probes[i]; | |||
| summary.total_num_probes += probes[i] * i; | |||
| } | |||
| summary.mean = 1.0 * summary.total_num_probes / summary.total_elements; | |||
| return summary; | |||
| } | |||
| // Returns the number of bytes requested from the allocator by the container | |||
| // and not freed. | |||
| template<typename C> | |||
| size_t AllocatedByteSize(const C& c) | |||
| { | |||
| return absl::container_internal::hashtable_debug_internal:: | |||
| HashtableDebugAccess<C>::AllocatedByteSize(c); | |||
| } | |||
| // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C` | |||
| // and `c.size()` is equal to `num_elements`. | |||
| template<typename C> | |||
| size_t LowerBoundAllocatedByteSize(size_t num_elements) | |||
| { | |||
| return absl::container_internal::hashtable_debug_internal:: | |||
| HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements); | |||
| } | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_ | |||
| @@ -0,0 +1,95 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Provides the internal API for hashtable_debug.h. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ | |||
| #include <cstddef> | |||
| #include <algorithm> | |||
| #include <type_traits> | |||
| #include <vector> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| namespace hashtable_debug_internal | |||
| { | |||
| // If it is a map, call get<0>(). | |||
| using std::get; | |||
| template<typename T, typename = typename T::mapped_type> | |||
| auto GetKey(const typename T::value_type& pair, int) -> decltype(get<0>(pair)) | |||
| { | |||
| return get<0>(pair); | |||
| } | |||
| // If it is not a map, return the value directly. | |||
| template<typename T> | |||
| const typename T::key_type& GetKey(const typename T::key_type& key, char) | |||
| { | |||
| return key; | |||
| } | |||
| // Containers should specialize this to provide debug information for that | |||
| // container. | |||
| template<class Container, typename Enabler = void> | |||
| struct HashtableDebugAccess | |||
| { | |||
| // Returns the number of probes required to find `key` in `c`. The "number of | |||
| // probes" is a concept that can vary by container. Implementations should | |||
| // return 0 when `key` was found in the minimum number of operations and | |||
| // should increment the result for each non-trivial operation required to find | |||
| // `key`. | |||
| // | |||
| // The default implementation uses the bucket api from the standard and thus | |||
| // works for `std::unordered_*` containers. | |||
| static size_t GetNumProbes(const Container& c, const typename Container::key_type& key) | |||
| { | |||
| if (!c.bucket_count()) | |||
| return {}; | |||
| size_t num_probes = 0; | |||
| size_t bucket = c.bucket(key); | |||
| for (auto it = c.begin(bucket), e = c.end(bucket);; ++it, ++num_probes) | |||
| { | |||
| if (it == e) | |||
| return num_probes; | |||
| if (c.key_eq()(key, GetKey<Container>(*it, 0))) | |||
| return num_probes; | |||
| } | |||
| } | |||
| // Returns the number of bytes requested from the allocator by the container | |||
| // and not freed. | |||
| // | |||
| // static size_t AllocatedByteSize(const Container& c); | |||
| // Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type | |||
| // `Container` and `c.size()` is equal to `num_elements`. | |||
| // | |||
| // static size_t LowerBoundAllocatedByteSize(size_t num_elements); | |||
| }; | |||
| } // namespace hashtable_debug_internal | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASHTABLE_DEBUG_HOOKS_H_ | |||
| @@ -0,0 +1,353 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // ----------------------------------------------------------------------------- | |||
| // File: hashtablez_sampler.h | |||
| // ----------------------------------------------------------------------------- | |||
| // | |||
| // This header file defines the API for a low level library to sample hashtables | |||
| // and collect runtime statistics about them. | |||
| // | |||
| // `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which | |||
| // store information about a single sample. | |||
| // | |||
| // `Record*` methods store information into samples. | |||
| // `Sample()` and `Unsample()` make use of a single global sampler with | |||
| // properties controlled by the flags hashtablez_enabled, | |||
| // hashtablez_sample_rate, and hashtablez_max_samples. | |||
| // | |||
| // WARNING | |||
| // | |||
| // Using this sampling API may cause sampled Swiss tables to use the global | |||
| // allocator (operator `new`) in addition to any custom allocator. If you | |||
| // are using a table in an unusual circumstance where allocation or calling a | |||
| // linux syscall is unacceptable, this could interfere. | |||
| // | |||
| // This utility is internal-only. Use at your own risk. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ | |||
| #include <atomic> | |||
| #include <functional> | |||
| #include <memory> | |||
| #include <vector> | |||
| #include "absl/base/config.h" | |||
| #include "absl/base/internal/per_thread_tls.h" | |||
| #include "absl/base/optimization.h" | |||
| #include "absl/profiling/internal/sample_recorder.h" | |||
| #include "absl/synchronization/mutex.h" | |||
| #include "absl/utility/utility.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // Stores information about a sampled hashtable. All mutations to this *must* | |||
| // be made through `Record*` functions below. All reads from this *must* only | |||
| // occur in the callback to `HashtablezSampler::Iterate`. | |||
| struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo> | |||
| { | |||
| // Constructs the object but does not fill in any fields. | |||
| HashtablezInfo(); | |||
| ~HashtablezInfo(); | |||
| HashtablezInfo(const HashtablezInfo&) = delete; | |||
| HashtablezInfo& operator=(const HashtablezInfo&) = delete; | |||
| // Puts the object into a clean state, fills in the logically `const` members, | |||
| // blocking for any readers that are currently sampling the object. | |||
| void PrepareForSampling(int64_t stride, size_t inline_element_size_value) | |||
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu); | |||
| // These fields are mutated by the various Record* APIs and need to be | |||
| // thread-safe. | |||
| std::atomic<size_t> capacity; | |||
| std::atomic<size_t> size; | |||
| std::atomic<size_t> num_erases; | |||
| std::atomic<size_t> num_rehashes; | |||
| std::atomic<size_t> max_probe_length; | |||
| std::atomic<size_t> total_probe_length; | |||
| std::atomic<size_t> hashes_bitwise_or; | |||
| std::atomic<size_t> hashes_bitwise_and; | |||
| std::atomic<size_t> hashes_bitwise_xor; | |||
| std::atomic<size_t> max_reserve; | |||
| // All of the fields below are set by `PrepareForSampling`, they must not be | |||
| // mutated in `Record*` functions. They are logically `const` in that sense. | |||
| // These are guarded by init_mu, but that is not externalized to clients, | |||
| // which can read them only during `SampleRecorder::Iterate` which will hold | |||
| // the lock. | |||
| static constexpr int kMaxStackDepth = 64; | |||
| absl::Time create_time; | |||
| int32_t depth; | |||
| void* stack[kMaxStackDepth]; | |||
| size_t inline_element_size; // How big is the slot? | |||
| }; | |||
| inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length) | |||
| { | |||
| #ifdef ABSL_INTERNAL_HAVE_SSE2 | |||
| total_probe_length /= 16; | |||
| #else | |||
| total_probe_length /= 8; | |||
| #endif | |||
| info->total_probe_length.store(total_probe_length, std::memory_order_relaxed); | |||
| info->num_erases.store(0, std::memory_order_relaxed); | |||
| // There is only one concurrent writer, so `load` then `store` is sufficient | |||
| // instead of using `fetch_add`. | |||
| info->num_rehashes.store( | |||
| 1 + info->num_rehashes.load(std::memory_order_relaxed), | |||
| std::memory_order_relaxed | |||
| ); | |||
| } | |||
| inline void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity) | |||
| { | |||
| info->max_reserve.store( | |||
| (std::max)(info->max_reserve.load(std::memory_order_relaxed), target_capacity), | |||
| std::memory_order_relaxed | |||
| ); | |||
| } | |||
| inline void RecordClearedReservationSlow(HashtablezInfo* info) | |||
| { | |||
| info->max_reserve.store(0, std::memory_order_relaxed); | |||
| } | |||
| inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, size_t capacity) | |||
| { | |||
| info->size.store(size, std::memory_order_relaxed); | |||
| info->capacity.store(capacity, std::memory_order_relaxed); | |||
| if (size == 0) | |||
| { | |||
| // This is a clear, reset the total/num_erases too. | |||
| info->total_probe_length.store(0, std::memory_order_relaxed); | |||
| info->num_erases.store(0, std::memory_order_relaxed); | |||
| } | |||
| } | |||
| void RecordInsertSlow(HashtablezInfo* info, size_t hash, size_t distance_from_desired); | |||
| inline void RecordEraseSlow(HashtablezInfo* info) | |||
| { | |||
| info->size.fetch_sub(1, std::memory_order_relaxed); | |||
| // There is only one concurrent writer, so `load` then `store` is sufficient | |||
| // instead of using `fetch_add`. | |||
| info->num_erases.store( | |||
| 1 + info->num_erases.load(std::memory_order_relaxed), | |||
| std::memory_order_relaxed | |||
| ); | |||
| } | |||
| struct SamplingState | |||
| { | |||
| int64_t next_sample; | |||
| // When we make a sampling decision, we record that distance so we can weight | |||
| // each sample. | |||
| int64_t sample_stride; | |||
| }; | |||
| HashtablezInfo* SampleSlow(SamplingState& next_sample, size_t inline_element_size); | |||
| void UnsampleSlow(HashtablezInfo* info); | |||
| #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| #error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set | |||
| #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| class HashtablezInfoHandle | |||
| { | |||
| public: | |||
| explicit HashtablezInfoHandle() : | |||
| info_(nullptr) | |||
| { | |||
| } | |||
| explicit HashtablezInfoHandle(HashtablezInfo* info) : | |||
| info_(info) | |||
| { | |||
| } | |||
| ~HashtablezInfoHandle() | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| UnsampleSlow(info_); | |||
| } | |||
| HashtablezInfoHandle(const HashtablezInfoHandle&) = delete; | |||
| HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete; | |||
| HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept | |||
| : | |||
| info_(absl::exchange(o.info_, nullptr)) | |||
| { | |||
| } | |||
| HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept | |||
| { | |||
| if (ABSL_PREDICT_FALSE(info_ != nullptr)) | |||
| { | |||
| UnsampleSlow(info_); | |||
| } | |||
| info_ = absl::exchange(o.info_, nullptr); | |||
| return *this; | |||
| } | |||
| inline void RecordStorageChanged(size_t size, size_t capacity) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordStorageChangedSlow(info_, size, capacity); | |||
| } | |||
| inline void RecordRehash(size_t total_probe_length) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordRehashSlow(info_, total_probe_length); | |||
| } | |||
| inline void RecordReservation(size_t target_capacity) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordReservationSlow(info_, target_capacity); | |||
| } | |||
| inline void RecordClearedReservation() | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordClearedReservationSlow(info_); | |||
| } | |||
| inline void RecordInsert(size_t hash, size_t distance_from_desired) | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordInsertSlow(info_, hash, distance_from_desired); | |||
| } | |||
| inline void RecordErase() | |||
| { | |||
| if (ABSL_PREDICT_TRUE(info_ == nullptr)) | |||
| return; | |||
| RecordEraseSlow(info_); | |||
| } | |||
| friend inline void swap(HashtablezInfoHandle& lhs, HashtablezInfoHandle& rhs) | |||
| { | |||
| std::swap(lhs.info_, rhs.info_); | |||
| } | |||
| private: | |||
| friend class HashtablezInfoHandlePeer; | |||
| HashtablezInfo* info_; | |||
| }; | |||
| #else | |||
| // Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can | |||
| // be removed by the linker, in order to reduce the binary size. | |||
| class HashtablezInfoHandle | |||
| { | |||
| public: | |||
| explicit HashtablezInfoHandle() = default; | |||
| explicit HashtablezInfoHandle(std::nullptr_t) | |||
| { | |||
| } | |||
| inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/) | |||
| { | |||
| } | |||
| inline void RecordRehash(size_t /*total_probe_length*/) | |||
| { | |||
| } | |||
| inline void RecordReservation(size_t /*target_capacity*/) | |||
| { | |||
| } | |||
| inline void RecordClearedReservation() | |||
| { | |||
| } | |||
| inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/) | |||
| { | |||
| } | |||
| inline void RecordErase() | |||
| { | |||
| } | |||
| friend inline void swap(HashtablezInfoHandle& /*lhs*/, HashtablezInfoHandle& /*rhs*/) | |||
| { | |||
| } | |||
| }; | |||
| #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample; | |||
| #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| // Returns an RAII sampling handle that manages registration and unregistation | |||
| // with the global sampler. | |||
| inline HashtablezInfoHandle Sample( | |||
| size_t inline_element_size ABSL_ATTRIBUTE_UNUSED | |||
| ) | |||
| { | |||
| #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE) | |||
| if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0)) | |||
| { | |||
| return HashtablezInfoHandle(nullptr); | |||
| } | |||
| return HashtablezInfoHandle( | |||
| SampleSlow(global_next_sample, inline_element_size) | |||
| ); | |||
| #else | |||
| return HashtablezInfoHandle(nullptr); | |||
| #endif // !ABSL_PER_THREAD_TLS | |||
| } | |||
| using HashtablezSampler = | |||
| ::absl::profiling_internal::SampleRecorder<HashtablezInfo>; | |||
| // Returns a global Sampler. | |||
| HashtablezSampler& GlobalHashtablezSampler(); | |||
| using HashtablezConfigListener = void (*)(); | |||
| void SetHashtablezConfigListener(HashtablezConfigListener l); | |||
| // Enables or disables sampling for Swiss tables. | |||
| bool IsHashtablezEnabled(); | |||
| void SetHashtablezEnabled(bool enabled); | |||
| void SetHashtablezEnabledInternal(bool enabled); | |||
| // Sets the rate at which Swiss tables will be sampled. | |||
| int32_t GetHashtablezSampleParameter(); | |||
| void SetHashtablezSampleParameter(int32_t rate); | |||
| void SetHashtablezSampleParameterInternal(int32_t rate); | |||
| // Sets a soft max for the number of samples that will be kept. | |||
| int32_t GetHashtablezMaxSamples(); | |||
| void SetHashtablezMaxSamples(int32_t max); | |||
| void SetHashtablezMaxSamplesInternal(int32_t max); | |||
| // Configuration override. | |||
| // This allows process-wide sampling without depending on order of | |||
| // initialization of static storage duration objects. | |||
| // The definition of this constant is weak, which allows us to inject a | |||
| // different value for it at link time. | |||
| extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)(); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_ | |||
| @@ -0,0 +1,809 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // MOTIVATION AND TUTORIAL | |||
| // | |||
| // If you want to put in a single heap allocation N doubles followed by M ints, | |||
| // it's easy if N and M are known at compile time. | |||
| // | |||
| // struct S { | |||
| // double a[N]; | |||
| // int b[M]; | |||
| // }; | |||
| // | |||
| // S* p = new S; | |||
| // | |||
| // But what if N and M are known only in run time? Class template Layout to the | |||
| // rescue! It's a portable generalization of the technique known as struct hack. | |||
| // | |||
| // // This object will tell us everything we need to know about the memory | |||
| // // layout of double[N] followed by int[M]. It's structurally identical to | |||
| // // size_t[2] that stores N and M. It's very cheap to create. | |||
| // const Layout<double, int> layout(N, M); | |||
| // | |||
| // // Allocate enough memory for both arrays. `AllocSize()` tells us how much | |||
| // // memory is needed. We are free to use any allocation function we want as | |||
| // // long as it returns aligned memory. | |||
| // std::unique_ptr<unsigned char[]> p(new unsigned char[layout.AllocSize()]); | |||
| // | |||
| // // Obtain the pointer to the array of doubles. | |||
| // // Equivalent to `reinterpret_cast<double*>(p.get())`. | |||
| // // | |||
| // // We could have written layout.Pointer<0>(p) instead. If all the types are | |||
| // // unique you can use either form, but if some types are repeated you must | |||
| // // use the index form. | |||
| // double* a = layout.Pointer<double>(p.get()); | |||
| // | |||
| // // Obtain the pointer to the array of ints. | |||
| // // Equivalent to `reinterpret_cast<int*>(p.get() + N * 8)`. | |||
| // int* b = layout.Pointer<int>(p); | |||
| // | |||
| // If we are unable to specify sizes of all fields, we can pass as many sizes as | |||
| // we can to `Partial()`. In return, it'll allow us to access the fields whose | |||
| // locations and sizes can be computed from the provided information. | |||
| // `Partial()` comes in handy when the array sizes are embedded into the | |||
| // allocation. | |||
| // | |||
| // // size_t[1] containing N, size_t[1] containing M, double[N], int[M]. | |||
| // using L = Layout<size_t, size_t, double, int>; | |||
| // | |||
| // unsigned char* Allocate(size_t n, size_t m) { | |||
| // const L layout(1, 1, n, m); | |||
| // unsigned char* p = new unsigned char[layout.AllocSize()]; | |||
| // *layout.Pointer<0>(p) = n; | |||
| // *layout.Pointer<1>(p) = m; | |||
| // return p; | |||
| // } | |||
| // | |||
| // void Use(unsigned char* p) { | |||
| // // First, extract N and M. | |||
| // // Specify that the first array has only one element. Using `prefix` we | |||
| // // can access the first two arrays but not more. | |||
| // constexpr auto prefix = L::Partial(1); | |||
| // size_t n = *prefix.Pointer<0>(p); | |||
| // size_t m = *prefix.Pointer<1>(p); | |||
| // | |||
| // // Now we can get pointers to the payload. | |||
| // const L layout(1, 1, n, m); | |||
| // double* a = layout.Pointer<double>(p); | |||
| // int* b = layout.Pointer<int>(p); | |||
| // } | |||
| // | |||
| // The layout we used above combines fixed-size with dynamically-sized fields. | |||
| // This is quite common. Layout is optimized for this use case and generates | |||
| // optimal code. All computations that can be performed at compile time are | |||
| // indeed performed at compile time. | |||
| // | |||
| // Efficiency tip: The order of fields matters. In `Layout<T1, ..., TN>` try to | |||
| // ensure that `alignof(T1) >= ... >= alignof(TN)`. This way you'll have no | |||
| // padding in between arrays. | |||
| // | |||
| // You can manually override the alignment of an array by wrapping the type in | |||
| // `Aligned<T, N>`. `Layout<..., Aligned<T, N>, ...>` has exactly the same API | |||
| // and behavior as `Layout<..., T, ...>` except that the first element of the | |||
| // array of `T` is aligned to `N` (the rest of the elements follow without | |||
| // padding). `N` cannot be less than `alignof(T)`. | |||
| // | |||
| // `AllocSize()` and `Pointer()` are the most basic methods for dealing with | |||
| // memory layouts. Check out the reference or code below to discover more. | |||
| // | |||
| // EXAMPLE | |||
| // | |||
| // // Immutable move-only string with sizeof equal to sizeof(void*). The | |||
| // // string size and the characters are kept in the same heap allocation. | |||
| // class CompactString { | |||
| // public: | |||
| // CompactString(const char* s = "") { | |||
| // const size_t size = strlen(s); | |||
| // // size_t[1] followed by char[size + 1]. | |||
| // const L layout(1, size + 1); | |||
| // p_.reset(new unsigned char[layout.AllocSize()]); | |||
| // // If running under ASAN, mark the padding bytes, if any, to catch | |||
| // // memory errors. | |||
| // layout.PoisonPadding(p_.get()); | |||
| // // Store the size in the allocation. | |||
| // *layout.Pointer<size_t>(p_.get()) = size; | |||
| // // Store the characters in the allocation. | |||
| // memcpy(layout.Pointer<char>(p_.get()), s, size + 1); | |||
| // } | |||
| // | |||
| // size_t size() const { | |||
| // // Equivalent to reinterpret_cast<size_t&>(*p). | |||
| // return *L::Partial().Pointer<size_t>(p_.get()); | |||
| // } | |||
| // | |||
| // const char* c_str() const { | |||
| // // Equivalent to reinterpret_cast<char*>(p.get() + sizeof(size_t)). | |||
| // // The argument in Partial(1) specifies that we have size_t[1] in front | |||
| // // of the characters. | |||
| // return L::Partial(1).Pointer<char>(p_.get()); | |||
| // } | |||
| // | |||
| // private: | |||
| // // Our heap allocation contains a size_t followed by an array of chars. | |||
| // using L = Layout<size_t, char>; | |||
| // std::unique_ptr<unsigned char[]> p_; | |||
| // }; | |||
| // | |||
| // int main() { | |||
| // CompactString s = "hello"; | |||
| // assert(s.size() == 5); | |||
| // assert(strcmp(s.c_str(), "hello") == 0); | |||
| // } | |||
| // | |||
| // DOCUMENTATION | |||
| // | |||
| // The interface exported by this file consists of: | |||
| // - class `Layout<>` and its public members. | |||
| // - The public members of class `internal_layout::LayoutImpl<>`. That class | |||
| // isn't intended to be used directly, and its name and template parameter | |||
| // list are internal implementation details, but the class itself provides | |||
| // most of the functionality in this file. See comments on its members for | |||
| // detailed documentation. | |||
| // | |||
| // `Layout<T1,... Tn>::Partial(count1,..., countm)` (where `m` <= `n`) returns a | |||
| // `LayoutImpl<>` object. `Layout<T1,..., Tn> layout(count1,..., countn)` | |||
| // creates a `Layout` object, which exposes the same functionality by inheriting | |||
| // from `LayoutImpl<>`. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_LAYOUT_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_LAYOUT_H_ | |||
| #include <assert.h> | |||
| #include <stddef.h> | |||
| #include <stdint.h> | |||
| #include <ostream> | |||
| #include <string> | |||
| #include <tuple> | |||
| #include <type_traits> | |||
| #include <typeinfo> | |||
| #include <utility> | |||
| #include "absl/base/config.h" | |||
| #include "absl/meta/type_traits.h" | |||
| #include "absl/strings/str_cat.h" | |||
| #include "absl/types/span.h" | |||
| #include "absl/utility/utility.h" | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| #include <sanitizer/asan_interface.h> | |||
| #endif | |||
| #if defined(__GXX_RTTI) | |||
| #define ABSL_INTERNAL_HAS_CXA_DEMANGLE | |||
| #endif | |||
| #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE | |||
| #include <cxxabi.h> | |||
| #endif | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // A type wrapper that instructs `Layout` to use the specific alignment for the | |||
| // array. `Layout<..., Aligned<T, N>, ...>` has exactly the same API | |||
| // and behavior as `Layout<..., T, ...>` except that the first element of the | |||
| // array of `T` is aligned to `N` (the rest of the elements follow without | |||
| // padding). | |||
| // | |||
| // Requires: `N >= alignof(T)` and `N` is a power of 2. | |||
| template<class T, size_t N> | |||
| struct Aligned; | |||
| namespace internal_layout | |||
| { | |||
| template<class T> | |||
| struct NotAligned | |||
| { | |||
| }; | |||
| template<class T, size_t N> | |||
| struct NotAligned<const Aligned<T, N>> | |||
| { | |||
| static_assert(sizeof(T) == 0, "Aligned<T, N> cannot be const-qualified"); | |||
| }; | |||
| template<size_t> | |||
| using IntToSize = size_t; | |||
| template<class> | |||
| using TypeToSize = size_t; | |||
| template<class T> | |||
| struct Type : NotAligned<T> | |||
| { | |||
| using type = T; | |||
| }; | |||
| template<class T, size_t N> | |||
| struct Type<Aligned<T, N>> | |||
| { | |||
| using type = T; | |||
| }; | |||
| template<class T> | |||
| struct SizeOf : NotAligned<T>, std::integral_constant<size_t, sizeof(T)> | |||
| { | |||
| }; | |||
| template<class T, size_t N> | |||
| struct SizeOf<Aligned<T, N>> : std::integral_constant<size_t, sizeof(T)> | |||
| { | |||
| }; | |||
| // Note: workaround for https://gcc.gnu.org/PR88115 | |||
| template<class T> | |||
| struct AlignOf : NotAligned<T> | |||
| { | |||
| static constexpr size_t value = alignof(T); | |||
| }; | |||
| template<class T, size_t N> | |||
| struct AlignOf<Aligned<T, N>> | |||
| { | |||
| static_assert(N % alignof(T) == 0, "Custom alignment can't be lower than the type's alignment"); | |||
| static constexpr size_t value = N; | |||
| }; | |||
| // Does `Ts...` contain `T`? | |||
| template<class T, class... Ts> | |||
| using Contains = absl::disjunction<std::is_same<T, Ts>...>; | |||
| template<class From, class To> | |||
| using CopyConst = | |||
| typename std::conditional<std::is_const<From>::value, const To, To>::type; | |||
| // Note: We're not qualifying this with absl:: because it doesn't compile under | |||
| // MSVC. | |||
| template<class T> | |||
| using SliceType = Span<T>; | |||
| // This namespace contains no types. It prevents functions defined in it from | |||
| // being found by ADL. | |||
| namespace adl_barrier | |||
| { | |||
| template<class Needle, class... Ts> | |||
| constexpr size_t Find(Needle, Needle, Ts...) | |||
| { | |||
| static_assert(!Contains<Needle, Ts...>(), "Duplicate element type"); | |||
| return 0; | |||
| } | |||
| template<class Needle, class T, class... Ts> | |||
| constexpr size_t Find(Needle, T, Ts...) | |||
| { | |||
| return adl_barrier::Find(Needle(), Ts()...) + 1; | |||
| } | |||
| constexpr bool IsPow2(size_t n) | |||
| { | |||
| return !(n & (n - 1)); | |||
| } | |||
| // Returns `q * m` for the smallest `q` such that `q * m >= n`. | |||
| // Requires: `m` is a power of two. It's enforced by IsLegalElementType below. | |||
| constexpr size_t Align(size_t n, size_t m) | |||
| { | |||
| return (n + m - 1) & ~(m - 1); | |||
| } | |||
| constexpr size_t Min(size_t a, size_t b) | |||
| { | |||
| return b < a ? b : a; | |||
| } | |||
| constexpr size_t Max(size_t a) | |||
| { | |||
| return a; | |||
| } | |||
| template<class... Ts> | |||
| constexpr size_t Max(size_t a, size_t b, Ts... rest) | |||
| { | |||
| return adl_barrier::Max(b < a ? a : b, rest...); | |||
| } | |||
| template<class T> | |||
| std::string TypeName() | |||
| { | |||
| std::string out; | |||
| int status = 0; | |||
| char* demangled = nullptr; | |||
| #ifdef ABSL_INTERNAL_HAS_CXA_DEMANGLE | |||
| demangled = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); | |||
| #endif | |||
| if (status == 0 && demangled != nullptr) | |||
| { // Demangling succeeded. | |||
| absl::StrAppend(&out, "<", demangled, ">"); | |||
| free(demangled); | |||
| } | |||
| else | |||
| { | |||
| #if defined(__GXX_RTTI) || defined(_CPPRTTI) | |||
| absl::StrAppend(&out, "<", typeid(T).name(), ">"); | |||
| #endif | |||
| } | |||
| return out; | |||
| } | |||
| } // namespace adl_barrier | |||
| template<bool C> | |||
| using EnableIf = typename std::enable_if<C, int>::type; | |||
| // Can `T` be a template argument of `Layout`? | |||
| template<class T> | |||
| using IsLegalElementType = std::integral_constant< | |||
| bool, | |||
| !std::is_reference<T>::value && !std::is_volatile<T>::value && | |||
| !std::is_reference<typename Type<T>::type>::value && | |||
| !std::is_volatile<typename Type<T>::type>::value && | |||
| adl_barrier::IsPow2(AlignOf<T>::value)>; | |||
| template<class Elements, class SizeSeq, class OffsetSeq> | |||
| class LayoutImpl; | |||
| // Public base class of `Layout` and the result type of `Layout::Partial()`. | |||
| // | |||
| // `Elements...` contains all template arguments of `Layout` that created this | |||
| // instance. | |||
| // | |||
| // `SizeSeq...` is `[0, NumSizes)` where `NumSizes` is the number of arguments | |||
| // passed to `Layout::Partial()` or `Layout::Layout()`. | |||
| // | |||
| // `OffsetSeq...` is `[0, NumOffsets)` where `NumOffsets` is | |||
| // `Min(sizeof...(Elements), NumSizes + 1)` (the number of arrays for which we | |||
| // can compute offsets). | |||
| template<class... Elements, size_t... SizeSeq, size_t... OffsetSeq> | |||
| class LayoutImpl<std::tuple<Elements...>, absl::index_sequence<SizeSeq...>, absl::index_sequence<OffsetSeq...>> | |||
| { | |||
| private: | |||
| static_assert(sizeof...(Elements) > 0, "At least one field is required"); | |||
| static_assert(absl::conjunction<IsLegalElementType<Elements>...>::value, "Invalid element type (see IsLegalElementType)"); | |||
| enum | |||
| { | |||
| NumTypes = sizeof...(Elements), | |||
| NumSizes = sizeof...(SizeSeq), | |||
| NumOffsets = sizeof...(OffsetSeq), | |||
| }; | |||
| // These are guaranteed by `Layout`. | |||
| static_assert(NumOffsets == adl_barrier::Min(NumTypes, NumSizes + 1), "Internal error"); | |||
| static_assert(NumTypes > 0, "Internal error"); | |||
| // Returns the index of `T` in `Elements...`. Results in a compilation error | |||
| // if `Elements...` doesn't contain exactly one instance of `T`. | |||
| template<class T> | |||
| static constexpr size_t ElementIndex() | |||
| { | |||
| static_assert(Contains<Type<T>, Type<typename Type<Elements>::type>...>(), "Type not found"); | |||
| return adl_barrier::Find(Type<T>(), Type<typename Type<Elements>::type>()...); | |||
| } | |||
| template<size_t N> | |||
| using ElementAlignment = | |||
| AlignOf<typename std::tuple_element<N, std::tuple<Elements...>>::type>; | |||
| public: | |||
| // Element types of all arrays packed in a tuple. | |||
| using ElementTypes = std::tuple<typename Type<Elements>::type...>; | |||
| // Element type of the Nth array. | |||
| template<size_t N> | |||
| using ElementType = typename std::tuple_element<N, ElementTypes>::type; | |||
| constexpr explicit LayoutImpl(IntToSize<SizeSeq>... sizes) : | |||
| size_{sizes...} | |||
| { | |||
| } | |||
| // Alignment of the layout, equal to the strictest alignment of all elements. | |||
| // All pointers passed to the methods of layout must be aligned to this value. | |||
| static constexpr size_t Alignment() | |||
| { | |||
| return adl_barrier::Max(AlignOf<Elements>::value...); | |||
| } | |||
| // Offset in bytes of the Nth array. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // assert(x.Offset<0>() == 0); // The ints starts from 0. | |||
| // assert(x.Offset<1>() == 16); // The doubles starts from 16. | |||
| // | |||
| // Requires: `N <= NumSizes && N < sizeof...(Ts)`. | |||
| template<size_t N, EnableIf<N == 0> = 0> | |||
| constexpr size_t Offset() const | |||
| { | |||
| return 0; | |||
| } | |||
| template<size_t N, EnableIf<N != 0> = 0> | |||
| constexpr size_t Offset() const | |||
| { | |||
| static_assert(N < NumOffsets, "Index out of bounds"); | |||
| return adl_barrier::Align( | |||
| Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1], | |||
| ElementAlignment<N>::value | |||
| ); | |||
| } | |||
| // Offset in bytes of the array with the specified element type. There must | |||
| // be exactly one such array and its zero-based index must be at most | |||
| // `NumSizes`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // assert(x.Offset<int>() == 0); // The ints starts from 0. | |||
| // assert(x.Offset<double>() == 16); // The doubles starts from 16. | |||
| template<class T> | |||
| constexpr size_t Offset() const | |||
| { | |||
| return Offset<ElementIndex<T>()>(); | |||
| } | |||
| // Offsets in bytes of all arrays for which the offsets are known. | |||
| constexpr std::array<size_t, NumOffsets> Offsets() const | |||
| { | |||
| return {{Offset<OffsetSeq>()...}}; | |||
| } | |||
| // The number of elements in the Nth array. This is the Nth argument of | |||
| // `Layout::Partial()` or `Layout::Layout()` (zero-based). | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // assert(x.Size<0>() == 3); | |||
| // assert(x.Size<1>() == 4); | |||
| // | |||
| // Requires: `N < NumSizes`. | |||
| template<size_t N> | |||
| constexpr size_t Size() const | |||
| { | |||
| static_assert(N < NumSizes, "Index out of bounds"); | |||
| return size_[N]; | |||
| } | |||
| // The number of elements in the array with the specified element type. | |||
| // There must be exactly one such array and its zero-based index must be | |||
| // at most `NumSizes`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // assert(x.Size<int>() == 3); | |||
| // assert(x.Size<double>() == 4); | |||
| template<class T> | |||
| constexpr size_t Size() const | |||
| { | |||
| return Size<ElementIndex<T>()>(); | |||
| } | |||
| // The number of elements of all arrays for which they are known. | |||
| constexpr std::array<size_t, NumSizes> Sizes() const | |||
| { | |||
| return {{Size<SizeSeq>()...}}; | |||
| } | |||
| // Pointer to the beginning of the Nth array. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // int* ints = x.Pointer<0>(p); | |||
| // double* doubles = x.Pointer<1>(p); | |||
| // | |||
| // Requires: `N <= NumSizes && N < sizeof...(Ts)`. | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| template<size_t N, class Char> | |||
| CopyConst<Char, ElementType<N>>* Pointer(Char* p) const | |||
| { | |||
| using C = typename std::remove_const<Char>::type; | |||
| static_assert( | |||
| std::is_same<C, char>() || std::is_same<C, unsigned char>() || | |||
| std::is_same<C, signed char>(), | |||
| "The argument must be a pointer to [const] [signed|unsigned] char" | |||
| ); | |||
| constexpr size_t alignment = Alignment(); | |||
| (void)alignment; | |||
| assert(reinterpret_cast<uintptr_t>(p) % alignment == 0); | |||
| return reinterpret_cast<CopyConst<Char, ElementType<N>>*>(p + Offset<N>()); | |||
| } | |||
| // Pointer to the beginning of the array with the specified element type. | |||
| // There must be exactly one such array and its zero-based index must be at | |||
| // most `NumSizes`. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // int* ints = x.Pointer<int>(p); | |||
| // double* doubles = x.Pointer<double>(p); | |||
| // | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| template<class T, class Char> | |||
| CopyConst<Char, T>* Pointer(Char* p) const | |||
| { | |||
| return Pointer<ElementIndex<T>()>(p); | |||
| } | |||
| // Pointers to all arrays for which pointers are known. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // | |||
| // int* ints; | |||
| // double* doubles; | |||
| // std::tie(ints, doubles) = x.Pointers(p); | |||
| // | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| // | |||
| // Note: We're not using ElementType alias here because it does not compile | |||
| // under MSVC. | |||
| template<class Char> | |||
| std::tuple<CopyConst< | |||
| Char, | |||
| typename std::tuple_element<OffsetSeq, ElementTypes>::type>*...> | |||
| Pointers(Char* p) const | |||
| { | |||
| return std::tuple<CopyConst<Char, ElementType<OffsetSeq>>*...>( | |||
| Pointer<OffsetSeq>(p)... | |||
| ); | |||
| } | |||
| // The Nth array. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // Span<int> ints = x.Slice<0>(p); | |||
| // Span<double> doubles = x.Slice<1>(p); | |||
| // | |||
| // Requires: `N < NumSizes`. | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| template<size_t N, class Char> | |||
| SliceType<CopyConst<Char, ElementType<N>>> Slice(Char* p) const | |||
| { | |||
| return SliceType<CopyConst<Char, ElementType<N>>>(Pointer<N>(p), Size<N>()); | |||
| } | |||
| // The array with the specified element type. There must be exactly one | |||
| // such array and its zero-based index must be less than `NumSizes`. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // Span<int> ints = x.Slice<int>(p); | |||
| // Span<double> doubles = x.Slice<double>(p); | |||
| // | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| template<class T, class Char> | |||
| SliceType<CopyConst<Char, T>> Slice(Char* p) const | |||
| { | |||
| return Slice<ElementIndex<T>()>(p); | |||
| } | |||
| // All arrays with known sizes. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; | |||
| // | |||
| // Span<int> ints; | |||
| // Span<double> doubles; | |||
| // std::tie(ints, doubles) = x.Slices(p); | |||
| // | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| // | |||
| // Note: We're not using ElementType alias here because it does not compile | |||
| // under MSVC. | |||
| template<class Char> | |||
| std::tuple<SliceType<CopyConst< | |||
| Char, | |||
| typename std::tuple_element<SizeSeq, ElementTypes>::type>>...> | |||
| Slices(Char* p) const | |||
| { | |||
| // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63875 (fixed | |||
| // in 6.1). | |||
| (void)p; | |||
| return std::tuple<SliceType<CopyConst<Char, ElementType<SizeSeq>>>...>( | |||
| Slice<SizeSeq>(p)... | |||
| ); | |||
| } | |||
| // The size of the allocation that fits all arrays. | |||
| // | |||
| // // int[3], 4 bytes of padding, double[4]. | |||
| // Layout<int, double> x(3, 4); | |||
| // unsigned char* p = new unsigned char[x.AllocSize()]; // 48 bytes | |||
| // | |||
| // Requires: `NumSizes == sizeof...(Ts)`. | |||
| constexpr size_t AllocSize() const | |||
| { | |||
| static_assert(NumTypes == NumSizes, "You must specify sizes of all fields"); | |||
| return Offset<NumTypes - 1>() + | |||
| SizeOf<ElementType<NumTypes - 1>>::value * size_[NumTypes - 1]; | |||
| } | |||
| // If built with --config=asan, poisons padding bytes (if any) in the | |||
| // allocation. The pointer must point to a memory block at least | |||
| // `AllocSize()` bytes in length. | |||
| // | |||
| // `Char` must be `[const] [signed|unsigned] char`. | |||
| // | |||
| // Requires: `p` is aligned to `Alignment()`. | |||
| template<class Char, size_t N = NumOffsets - 1, EnableIf<N == 0> = 0> | |||
| void PoisonPadding(const Char* p) const | |||
| { | |||
| Pointer<0>(p); // verify the requirements on `Char` and `p` | |||
| } | |||
| template<class Char, size_t N = NumOffsets - 1, EnableIf<N != 0> = 0> | |||
| void PoisonPadding(const Char* p) const | |||
| { | |||
| static_assert(N < NumOffsets, "Index out of bounds"); | |||
| (void)p; | |||
| #ifdef ABSL_HAVE_ADDRESS_SANITIZER | |||
| PoisonPadding<Char, N - 1>(p); | |||
| // The `if` is an optimization. It doesn't affect the observable behaviour. | |||
| if (ElementAlignment<N - 1>::value % ElementAlignment<N>::value) | |||
| { | |||
| size_t start = | |||
| Offset<N - 1>() + SizeOf<ElementType<N - 1>>::value * size_[N - 1]; | |||
| ASAN_POISON_MEMORY_REGION(p + start, Offset<N>() - start); | |||
| } | |||
| #endif | |||
| } | |||
| // Human-readable description of the memory layout. Useful for debugging. | |||
| // Slow. | |||
| // | |||
| // // char[5], 3 bytes of padding, int[3], 4 bytes of padding, followed | |||
| // // by an unknown number of doubles. | |||
| // auto x = Layout<char, int, double>::Partial(5, 3); | |||
| // assert(x.DebugString() == | |||
| // "@0<char>(1)[5]; @8<int>(4)[3]; @24<double>(8)"); | |||
| // | |||
| // Each field is in the following format: @offset<type>(sizeof)[size] (<type> | |||
| // may be missing depending on the target platform). For example, | |||
| // @8<int>(4)[3] means that at offset 8 we have an array of ints, where each | |||
| // int is 4 bytes, and we have 3 of those ints. The size of the last field may | |||
| // be missing (as in the example above). Only fields with known offsets are | |||
| // described. Type names may differ across platforms: one compiler might | |||
| // produce "unsigned*" where another produces "unsigned int *". | |||
| std::string DebugString() const | |||
| { | |||
| const auto offsets = Offsets(); | |||
| const size_t sizes[] = {SizeOf<ElementType<OffsetSeq>>::value...}; | |||
| const std::string types[] = { | |||
| adl_barrier::TypeName<ElementType<OffsetSeq>>()...}; | |||
| std::string res = absl::StrCat("@0", types[0], "(", sizes[0], ")"); | |||
| for (size_t i = 0; i != NumOffsets - 1; ++i) | |||
| { | |||
| absl::StrAppend(&res, "[", size_[i], "]; @", offsets[i + 1], types[i + 1], "(", sizes[i + 1], ")"); | |||
| } | |||
| // NumSizes is a constant that may be zero. Some compilers cannot see that | |||
| // inside the if statement "size_[NumSizes - 1]" must be valid. | |||
| int last = static_cast<int>(NumSizes) - 1; | |||
| if (NumTypes == NumSizes && last >= 0) | |||
| { | |||
| absl::StrAppend(&res, "[", size_[last], "]"); | |||
| } | |||
| return res; | |||
| } | |||
| private: | |||
| // Arguments of `Layout::Partial()` or `Layout::Layout()`. | |||
| size_t size_[NumSizes > 0 ? NumSizes : 1]; | |||
| }; | |||
| template<size_t NumSizes, class... Ts> | |||
| using LayoutType = LayoutImpl< | |||
| std::tuple<Ts...>, | |||
| absl::make_index_sequence<NumSizes>, | |||
| absl::make_index_sequence<adl_barrier::Min(sizeof...(Ts), NumSizes + 1)>>; | |||
| } // namespace internal_layout | |||
| // Descriptor of arrays of various types and sizes laid out in memory one after | |||
| // another. See the top of the file for documentation. | |||
| // | |||
| // Check out the public API of internal_layout::LayoutImpl above. The type is | |||
| // internal to the library but its methods are public, and they are inherited | |||
| // by `Layout`. | |||
| template<class... Ts> | |||
| class Layout : public internal_layout::LayoutType<sizeof...(Ts), Ts...> | |||
| { | |||
| public: | |||
| static_assert(sizeof...(Ts) > 0, "At least one field is required"); | |||
| static_assert( | |||
| absl::conjunction<internal_layout::IsLegalElementType<Ts>...>::value, | |||
| "Invalid element type (see IsLegalElementType)" | |||
| ); | |||
| // The result type of `Partial()` with `NumSizes` arguments. | |||
| template<size_t NumSizes> | |||
| using PartialType = internal_layout::LayoutType<NumSizes, Ts...>; | |||
| // `Layout` knows the element types of the arrays we want to lay out in | |||
| // memory but not the number of elements in each array. | |||
| // `Partial(size1, ..., sizeN)` allows us to specify the latter. The | |||
| // resulting immutable object can be used to obtain pointers to the | |||
| // individual arrays. | |||
| // | |||
| // It's allowed to pass fewer array sizes than the number of arrays. E.g., | |||
| // if all you need is to the offset of the second array, you only need to | |||
| // pass one argument -- the number of elements in the first array. | |||
| // | |||
| // // int[3] followed by 4 bytes of padding and an unknown number of | |||
| // // doubles. | |||
| // auto x = Layout<int, double>::Partial(3); | |||
| // // doubles start at byte 16. | |||
| // assert(x.Offset<1>() == 16); | |||
| // | |||
| // If you know the number of elements in all arrays, you can still call | |||
| // `Partial()` but it's more convenient to use the constructor of `Layout`. | |||
| // | |||
| // Layout<int, double> x(3, 5); | |||
| // | |||
| // Note: The sizes of the arrays must be specified in number of elements, | |||
| // not in bytes. | |||
| // | |||
| // Requires: `sizeof...(Sizes) <= sizeof...(Ts)`. | |||
| // Requires: all arguments are convertible to `size_t`. | |||
| template<class... Sizes> | |||
| static constexpr PartialType<sizeof...(Sizes)> Partial(Sizes&&... sizes) | |||
| { | |||
| static_assert(sizeof...(Sizes) <= sizeof...(Ts), ""); | |||
| return PartialType<sizeof...(Sizes)>(absl::forward<Sizes>(sizes)...); | |||
| } | |||
| // Creates a layout with the sizes of all arrays specified. If you know | |||
| // only the sizes of the first N arrays (where N can be zero), you can use | |||
| // `Partial()` defined above. The constructor is essentially equivalent to | |||
| // calling `Partial()` and passing in all array sizes; the constructor is | |||
| // provided as a convenient abbreviation. | |||
| // | |||
| // Note: The sizes of the arrays must be specified in number of elements, | |||
| // not in bytes. | |||
| constexpr explicit Layout(internal_layout::TypeToSize<Ts>... sizes) : | |||
| internal_layout::LayoutType<sizeof...(Ts), Ts...>(sizes...) | |||
| { | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_LAYOUT_H_ | |||
| @@ -0,0 +1,105 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // | |||
| // Adapts a policy for nodes. | |||
| // | |||
| // The node policy should model: | |||
| // | |||
| // struct Policy { | |||
| // // Returns a new node allocated and constructed using the allocator, using | |||
| // // the specified arguments. | |||
| // template <class Alloc, class... Args> | |||
| // value_type* new_element(Alloc* alloc, Args&&... args) const; | |||
| // | |||
| // // Destroys and deallocates node using the allocator. | |||
| // template <class Alloc> | |||
| // void delete_element(Alloc* alloc, value_type* node) const; | |||
| // }; | |||
| // | |||
| // It may also optionally define `value()` and `apply()`. For documentation on | |||
| // these, see hash_policy_traits.h. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ | |||
| #include <cassert> | |||
| #include <cstddef> | |||
| #include <memory> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class Reference, class Policy> | |||
| struct node_slot_policy | |||
| { | |||
| static_assert(std::is_lvalue_reference<Reference>::value, ""); | |||
| using slot_type = typename std::remove_cv< | |||
| typename std::remove_reference<Reference>::type>::type*; | |||
| template<class Alloc, class... Args> | |||
| static void construct(Alloc* alloc, slot_type* slot, Args&&... args) | |||
| { | |||
| *slot = Policy::new_element(alloc, std::forward<Args>(args)...); | |||
| } | |||
| template<class Alloc> | |||
| static void destroy(Alloc* alloc, slot_type* slot) | |||
| { | |||
| Policy::delete_element(alloc, *slot); | |||
| } | |||
| template<class Alloc> | |||
| static void transfer(Alloc*, slot_type* new_slot, slot_type* old_slot) | |||
| { | |||
| *new_slot = *old_slot; | |||
| } | |||
| static size_t space_used(const slot_type* slot) | |||
| { | |||
| if (slot == nullptr) | |||
| return Policy::element_space_used(nullptr); | |||
| return Policy::element_space_used(*slot); | |||
| } | |||
| static Reference element(slot_type* slot) | |||
| { | |||
| return **slot; | |||
| } | |||
| template<class T, class P = Policy> | |||
| static auto value(T* elem) -> decltype(P::value(elem)) | |||
| { | |||
| return P::value(elem); | |||
| } | |||
| template<class... Ts, class P = Policy> | |||
| static auto apply(Ts&&... ts) -> decltype(P::apply(std::forward<Ts>(ts)...)) | |||
| { | |||
| return P::apply(std::forward<Ts>(ts)...); | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_NODE_SLOT_POLICY_H_ | |||
| @@ -0,0 +1,218 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ | |||
| #include <tuple> | |||
| #include <type_traits> | |||
| #include <utility> | |||
| #include "absl/base/internal/throw_delegate.h" | |||
| #include "absl/container/internal/container_memory.h" | |||
| #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class Policy, class Hash, class Eq, class Alloc> | |||
| class raw_hash_map : public raw_hash_set<Policy, Hash, Eq, Alloc> | |||
| { | |||
| // P is Policy. It's passed as a template argument to support maps that have | |||
| // incomplete types as values, as in unordered_map<K, IncompleteType>. | |||
| // MappedReference<> may be a non-reference type. | |||
| template<class P> | |||
| using MappedReference = decltype(P::value( | |||
| std::addressof(std::declval<typename raw_hash_map::reference>()) | |||
| )); | |||
| // MappedConstReference<> may be a non-reference type. | |||
| template<class P> | |||
| using MappedConstReference = decltype(P::value( | |||
| std::addressof(std::declval<typename raw_hash_map::const_reference>()) | |||
| )); | |||
| using KeyArgImpl = | |||
| KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>; | |||
| public: | |||
| using key_type = typename Policy::key_type; | |||
| using mapped_type = typename Policy::mapped_type; | |||
| template<class K> | |||
| using key_arg = typename KeyArgImpl::template type<K, key_type>; | |||
| static_assert(!std::is_reference<key_type>::value, ""); | |||
| // TODO(b/187807849): Evaluate whether to support reference mapped_type and | |||
| // remove this assertion if/when it is supported. | |||
| static_assert(!std::is_reference<mapped_type>::value, ""); | |||
| using iterator = typename raw_hash_map::raw_hash_set::iterator; | |||
| using const_iterator = typename raw_hash_map::raw_hash_set::const_iterator; | |||
| raw_hash_map() | |||
| { | |||
| } | |||
| using raw_hash_map::raw_hash_set::raw_hash_set; | |||
| // The last two template parameters ensure that both arguments are rvalues | |||
| // (lvalue arguments are handled by the overloads below). This is necessary | |||
| // for supporting bitfield arguments. | |||
| // | |||
| // union { int n : 1; }; | |||
| // flat_hash_map<int, int> m; | |||
| // m.insert_or_assign(n, n); | |||
| template<class K = key_type, class V = mapped_type, K* = nullptr, V* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) | |||
| { | |||
| return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v)); | |||
| } | |||
| template<class K = key_type, class V = mapped_type, K* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) | |||
| { | |||
| return insert_or_assign_impl(std::forward<K>(k), v); | |||
| } | |||
| template<class K = key_type, class V = mapped_type, V* = nullptr> | |||
| std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) | |||
| { | |||
| return insert_or_assign_impl(k, std::forward<V>(v)); | |||
| } | |||
| template<class K = key_type, class V = mapped_type> | |||
| std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) | |||
| { | |||
| return insert_or_assign_impl(k, v); | |||
| } | |||
| template<class K = key_type, class V = mapped_type, K* = nullptr, V* = nullptr> | |||
| iterator insert_or_assign(const_iterator, key_arg<K>&& k, V&& v) | |||
| { | |||
| return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first; | |||
| } | |||
| template<class K = key_type, class V = mapped_type, K* = nullptr> | |||
| iterator insert_or_assign(const_iterator, key_arg<K>&& k, const V& v) | |||
| { | |||
| return insert_or_assign(std::forward<K>(k), v).first; | |||
| } | |||
| template<class K = key_type, class V = mapped_type, V* = nullptr> | |||
| iterator insert_or_assign(const_iterator, const key_arg<K>& k, V&& v) | |||
| { | |||
| return insert_or_assign(k, std::forward<V>(v)).first; | |||
| } | |||
| template<class K = key_type, class V = mapped_type> | |||
| iterator insert_or_assign(const_iterator, const key_arg<K>& k, const V& v) | |||
| { | |||
| return insert_or_assign(k, v).first; | |||
| } | |||
| // All `try_emplace()` overloads make the same guarantees regarding rvalue | |||
| // arguments as `std::unordered_map::try_emplace()`, namely that these | |||
| // functions will not move from rvalue arguments if insertions do not happen. | |||
| template<class K = key_type, class... Args, typename std::enable_if<!std::is_convertible<K, const_iterator>::value, int>::type = 0, K* = nullptr> | |||
| std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) | |||
| { | |||
| return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...); | |||
| } | |||
| template<class K = key_type, class... Args, typename std::enable_if<!std::is_convertible<K, const_iterator>::value, int>::type = 0> | |||
| std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) | |||
| { | |||
| return try_emplace_impl(k, std::forward<Args>(args)...); | |||
| } | |||
| template<class K = key_type, class... Args, K* = nullptr> | |||
| iterator try_emplace(const_iterator, key_arg<K>&& k, Args&&... args) | |||
| { | |||
| return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first; | |||
| } | |||
| template<class K = key_type, class... Args> | |||
| iterator try_emplace(const_iterator, const key_arg<K>& k, Args&&... args) | |||
| { | |||
| return try_emplace(k, std::forward<Args>(args)...).first; | |||
| } | |||
| template<class K = key_type, class P = Policy> | |||
| MappedReference<P> at(const key_arg<K>& key) | |||
| { | |||
| auto it = this->find(key); | |||
| if (it == this->end()) | |||
| { | |||
| base_internal::ThrowStdOutOfRange( | |||
| "absl::container_internal::raw_hash_map<>::at" | |||
| ); | |||
| } | |||
| return Policy::value(&*it); | |||
| } | |||
| template<class K = key_type, class P = Policy> | |||
| MappedConstReference<P> at(const key_arg<K>& key) const | |||
| { | |||
| auto it = this->find(key); | |||
| if (it == this->end()) | |||
| { | |||
| base_internal::ThrowStdOutOfRange( | |||
| "absl::container_internal::raw_hash_map<>::at" | |||
| ); | |||
| } | |||
| return Policy::value(&*it); | |||
| } | |||
| template<class K = key_type, class P = Policy, K* = nullptr> | |||
| MappedReference<P> operator[](key_arg<K>&& key) | |||
| { | |||
| return Policy::value(&*try_emplace(std::forward<K>(key)).first); | |||
| } | |||
| template<class K = key_type, class P = Policy> | |||
| MappedReference<P> operator[](const key_arg<K>& key) | |||
| { | |||
| return Policy::value(&*try_emplace(key).first); | |||
| } | |||
| private: | |||
| template<class K, class V> | |||
| std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) | |||
| { | |||
| auto res = this->find_or_prepare_insert(k); | |||
| if (res.second) | |||
| this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v)); | |||
| else | |||
| Policy::value(&*this->iterator_at(res.first)) = std::forward<V>(v); | |||
| return {this->iterator_at(res.first), res.second}; | |||
| } | |||
| template<class K = key_type, class... Args> | |||
| std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) | |||
| { | |||
| auto res = this->find_or_prepare_insert(k); | |||
| if (res.second) | |||
| this->emplace_at(res.first, std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)), std::forward_as_tuple(std::forward<Args>(args)...)); | |||
| return {this->iterator_at(res.first), res.second}; | |||
| } | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_RAW_HASH_MAP_H_ | |||
| @@ -0,0 +1,340 @@ | |||
| // Copyright 2017 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ | |||
| #include <cstdlib> | |||
| #include <ostream> | |||
| #include "absl/types/compare.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace test_internal | |||
| { | |||
| // A type that counts number of occurrences of the type, the live occurrences of | |||
| // the type, as well as the number of copies, moves, swaps, and comparisons that | |||
| // have occurred on the type. This is used as a base class for the copyable, | |||
| // copyable+movable, and movable types below that are used in actual tests. Use | |||
| // InstanceTracker in tests to track the number of instances. | |||
| class BaseCountedInstance | |||
| { | |||
| public: | |||
| explicit BaseCountedInstance(int x) : | |||
| value_(x) | |||
| { | |||
| ++num_instances_; | |||
| ++num_live_instances_; | |||
| } | |||
| BaseCountedInstance(const BaseCountedInstance& x) : | |||
| value_(x.value_), | |||
| is_live_(x.is_live_) | |||
| { | |||
| ++num_instances_; | |||
| if (is_live_) | |||
| ++num_live_instances_; | |||
| ++num_copies_; | |||
| } | |||
| BaseCountedInstance(BaseCountedInstance&& x) : | |||
| value_(x.value_), | |||
| is_live_(x.is_live_) | |||
| { | |||
| x.is_live_ = false; | |||
| ++num_instances_; | |||
| ++num_moves_; | |||
| } | |||
| ~BaseCountedInstance() | |||
| { | |||
| --num_instances_; | |||
| if (is_live_) | |||
| --num_live_instances_; | |||
| } | |||
| BaseCountedInstance& operator=(const BaseCountedInstance& x) | |||
| { | |||
| value_ = x.value_; | |||
| if (is_live_) | |||
| --num_live_instances_; | |||
| is_live_ = x.is_live_; | |||
| if (is_live_) | |||
| ++num_live_instances_; | |||
| ++num_copies_; | |||
| return *this; | |||
| } | |||
| BaseCountedInstance& operator=(BaseCountedInstance&& x) | |||
| { | |||
| value_ = x.value_; | |||
| if (is_live_) | |||
| --num_live_instances_; | |||
| is_live_ = x.is_live_; | |||
| x.is_live_ = false; | |||
| ++num_moves_; | |||
| return *this; | |||
| } | |||
| bool operator==(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ == x.value_; | |||
| } | |||
| bool operator!=(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ != x.value_; | |||
| } | |||
| bool operator<(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ < x.value_; | |||
| } | |||
| bool operator>(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ > x.value_; | |||
| } | |||
| bool operator<=(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ <= x.value_; | |||
| } | |||
| bool operator>=(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ >= x.value_; | |||
| } | |||
| absl::weak_ordering compare(const BaseCountedInstance& x) const | |||
| { | |||
| ++num_comparisons_; | |||
| return value_ < x.value_ ? absl::weak_ordering::less : value_ == x.value_ ? absl::weak_ordering::equivalent : | |||
| absl::weak_ordering::greater; | |||
| } | |||
| int value() const | |||
| { | |||
| if (!is_live_) | |||
| std::abort(); | |||
| return value_; | |||
| } | |||
| friend std::ostream& operator<<(std::ostream& o, const BaseCountedInstance& v) | |||
| { | |||
| return o << "[value:" << v.value() << "]"; | |||
| } | |||
| // Implementation of efficient swap() that counts swaps. | |||
| static void SwapImpl( | |||
| BaseCountedInstance& lhs, // NOLINT(runtime/references) | |||
| BaseCountedInstance& rhs | |||
| ) | |||
| { // NOLINT(runtime/references) | |||
| using std::swap; | |||
| swap(lhs.value_, rhs.value_); | |||
| swap(lhs.is_live_, rhs.is_live_); | |||
| ++BaseCountedInstance::num_swaps_; | |||
| } | |||
| private: | |||
| friend class InstanceTracker; | |||
| int value_; | |||
| // Indicates if the value is live, ie it hasn't been moved away from. | |||
| bool is_live_ = true; | |||
| // Number of instances. | |||
| static int num_instances_; | |||
| // Number of live instances (those that have not been moved away from.) | |||
| static int num_live_instances_; | |||
| // Number of times that BaseCountedInstance objects were moved. | |||
| static int num_moves_; | |||
| // Number of times that BaseCountedInstance objects were copied. | |||
| static int num_copies_; | |||
| // Number of times that BaseCountedInstance objects were swapped. | |||
| static int num_swaps_; | |||
| // Number of times that BaseCountedInstance objects were compared. | |||
| static int num_comparisons_; | |||
| }; | |||
| // Helper to track the BaseCountedInstance instance counters. Expects that the | |||
| // number of instances and live_instances are the same when it is constructed | |||
| // and when it is destructed. | |||
| class InstanceTracker | |||
| { | |||
| public: | |||
| InstanceTracker() : | |||
| start_instances_(BaseCountedInstance::num_instances_), | |||
| start_live_instances_(BaseCountedInstance::num_live_instances_) | |||
| { | |||
| ResetCopiesMovesSwaps(); | |||
| } | |||
| ~InstanceTracker() | |||
| { | |||
| if (instances() != 0) | |||
| std::abort(); | |||
| if (live_instances() != 0) | |||
| std::abort(); | |||
| } | |||
| // Returns the number of BaseCountedInstance instances both containing valid | |||
| // values and those moved away from compared to when the InstanceTracker was | |||
| // constructed | |||
| int instances() const | |||
| { | |||
| return BaseCountedInstance::num_instances_ - start_instances_; | |||
| } | |||
| // Returns the number of live BaseCountedInstance instances compared to when | |||
| // the InstanceTracker was constructed | |||
| int live_instances() const | |||
| { | |||
| return BaseCountedInstance::num_live_instances_ - start_live_instances_; | |||
| } | |||
| // Returns the number of moves on BaseCountedInstance objects since | |||
| // construction or since the last call to ResetCopiesMovesSwaps(). | |||
| int moves() const | |||
| { | |||
| return BaseCountedInstance::num_moves_ - start_moves_; | |||
| } | |||
| // Returns the number of copies on BaseCountedInstance objects since | |||
| // construction or the last call to ResetCopiesMovesSwaps(). | |||
| int copies() const | |||
| { | |||
| return BaseCountedInstance::num_copies_ - start_copies_; | |||
| } | |||
| // Returns the number of swaps on BaseCountedInstance objects since | |||
| // construction or the last call to ResetCopiesMovesSwaps(). | |||
| int swaps() const | |||
| { | |||
| return BaseCountedInstance::num_swaps_ - start_swaps_; | |||
| } | |||
| // Returns the number of comparisons on BaseCountedInstance objects since | |||
| // construction or the last call to ResetCopiesMovesSwaps(). | |||
| int comparisons() const | |||
| { | |||
| return BaseCountedInstance::num_comparisons_ - start_comparisons_; | |||
| } | |||
| // Resets the base values for moves, copies, comparisons, and swaps to the | |||
| // current values, so that subsequent Get*() calls for moves, copies, | |||
| // comparisons, and swaps will compare to the situation at the point of this | |||
| // call. | |||
| void ResetCopiesMovesSwaps() | |||
| { | |||
| start_moves_ = BaseCountedInstance::num_moves_; | |||
| start_copies_ = BaseCountedInstance::num_copies_; | |||
| start_swaps_ = BaseCountedInstance::num_swaps_; | |||
| start_comparisons_ = BaseCountedInstance::num_comparisons_; | |||
| } | |||
| private: | |||
| int start_instances_; | |||
| int start_live_instances_; | |||
| int start_moves_; | |||
| int start_copies_; | |||
| int start_swaps_; | |||
| int start_comparisons_; | |||
| }; | |||
| // Copyable, not movable. | |||
| class CopyableOnlyInstance : public BaseCountedInstance | |||
| { | |||
| public: | |||
| explicit CopyableOnlyInstance(int x) : | |||
| BaseCountedInstance(x) | |||
| { | |||
| } | |||
| CopyableOnlyInstance(const CopyableOnlyInstance& rhs) = default; | |||
| CopyableOnlyInstance& operator=(const CopyableOnlyInstance& rhs) = default; | |||
| friend void swap(CopyableOnlyInstance& lhs, CopyableOnlyInstance& rhs) | |||
| { | |||
| BaseCountedInstance::SwapImpl(lhs, rhs); | |||
| } | |||
| static bool supports_move() | |||
| { | |||
| return false; | |||
| } | |||
| }; | |||
| // Copyable and movable. | |||
| class CopyableMovableInstance : public BaseCountedInstance | |||
| { | |||
| public: | |||
| explicit CopyableMovableInstance(int x) : | |||
| BaseCountedInstance(x) | |||
| { | |||
| } | |||
| CopyableMovableInstance(const CopyableMovableInstance& rhs) = default; | |||
| CopyableMovableInstance(CopyableMovableInstance&& rhs) = default; | |||
| CopyableMovableInstance& operator=(const CopyableMovableInstance& rhs) = | |||
| default; | |||
| CopyableMovableInstance& operator=(CopyableMovableInstance&& rhs) = default; | |||
| friend void swap(CopyableMovableInstance& lhs, CopyableMovableInstance& rhs) | |||
| { | |||
| BaseCountedInstance::SwapImpl(lhs, rhs); | |||
| } | |||
| static bool supports_move() | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| // Only movable, not default-constructible. | |||
| class MovableOnlyInstance : public BaseCountedInstance | |||
| { | |||
| public: | |||
| explicit MovableOnlyInstance(int x) : | |||
| BaseCountedInstance(x) | |||
| { | |||
| } | |||
| MovableOnlyInstance(MovableOnlyInstance&& other) = default; | |||
| MovableOnlyInstance& operator=(MovableOnlyInstance&& other) = default; | |||
| friend void swap(MovableOnlyInstance& lhs, MovableOnlyInstance& rhs) | |||
| { | |||
| BaseCountedInstance::SwapImpl(lhs, rhs); | |||
| } | |||
| static bool supports_move() | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| } // namespace test_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_ | |||
| @@ -0,0 +1,106 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_TRACKED_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_TRACKED_H_ | |||
| #include <stddef.h> | |||
| #include <memory> | |||
| #include <utility> | |||
| #include "absl/base/config.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| // A class that tracks its copies and moves so that it can be queried in tests. | |||
| template<class T> | |||
| class Tracked | |||
| { | |||
| public: | |||
| Tracked() | |||
| { | |||
| } | |||
| // NOLINTNEXTLINE(runtime/explicit) | |||
| Tracked(const T& val) : | |||
| val_(val) | |||
| { | |||
| } | |||
| Tracked(const Tracked& that) : | |||
| val_(that.val_), | |||
| num_moves_(that.num_moves_), | |||
| num_copies_(that.num_copies_) | |||
| { | |||
| ++(*num_copies_); | |||
| } | |||
| Tracked(Tracked&& that) : | |||
| val_(std::move(that.val_)), | |||
| num_moves_(std::move(that.num_moves_)), | |||
| num_copies_(std::move(that.num_copies_)) | |||
| { | |||
| ++(*num_moves_); | |||
| } | |||
| Tracked& operator=(const Tracked& that) | |||
| { | |||
| val_ = that.val_; | |||
| num_moves_ = that.num_moves_; | |||
| num_copies_ = that.num_copies_; | |||
| ++(*num_copies_); | |||
| } | |||
| Tracked& operator=(Tracked&& that) | |||
| { | |||
| val_ = std::move(that.val_); | |||
| num_moves_ = std::move(that.num_moves_); | |||
| num_copies_ = std::move(that.num_copies_); | |||
| ++(*num_moves_); | |||
| } | |||
| const T& val() const | |||
| { | |||
| return val_; | |||
| } | |||
| friend bool operator==(const Tracked& a, const Tracked& b) | |||
| { | |||
| return a.val_ == b.val_; | |||
| } | |||
| friend bool operator!=(const Tracked& a, const Tracked& b) | |||
| { | |||
| return !(a == b); | |||
| } | |||
| size_t num_copies() | |||
| { | |||
| return *num_copies_; | |||
| } | |||
| size_t num_moves() | |||
| { | |||
| return *num_moves_; | |||
| } | |||
| private: | |||
| T val_; | |||
| std::shared_ptr<size_t> num_moves_ = std::make_shared<size_t>(0); | |||
| std::shared_ptr<size_t> num_copies_ = std::make_shared<size_t>(0); | |||
| }; | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_TRACKED_H_ | |||
| @@ -0,0 +1,546 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ | |||
| #include <algorithm> | |||
| #include <unordered_map> | |||
| #include <vector> | |||
| #include "gmock/gmock.h" | |||
| #include "gtest/gtest.h" | |||
| #include "absl/container/internal/hash_generator_testing.h" | |||
| #include "absl/container/internal/hash_policy_testing.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class UnordMap> | |||
| class ConstructorTest : public ::testing::Test | |||
| { | |||
| }; | |||
| TYPED_TEST_SUITE_P(ConstructorTest); | |||
| TYPED_TEST_P(ConstructorTest, NoArgs) | |||
| { | |||
| TypeParam m; | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCount) | |||
| { | |||
| TypeParam m(123); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHash) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| H hasher; | |||
| TypeParam m(123, hasher); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| H hasher; | |||
| E equal; | |||
| TypeParam m(123, hasher, equal); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| template<typename T> | |||
| struct is_std_unordered_map : std::false_type | |||
| { | |||
| }; | |||
| template<typename... T> | |||
| struct is_std_unordered_map<std::unordered_map<T...>> : std::true_type | |||
| { | |||
| }; | |||
| #if defined(UNORDERED_MAP_CXX14) || defined(UNORDERED_MAP_CXX17) | |||
| using has_cxx14_std_apis = std::true_type; | |||
| #else | |||
| using has_cxx14_std_apis = std::false_type; | |||
| #endif | |||
| template<typename T> | |||
| using expect_cxx14_apis = | |||
| absl::disjunction<absl::negation<is_std_unordered_map<T>>, has_cxx14_std_apis>; | |||
| template<typename TypeParam> | |||
| void BucketCountAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountAllocTest(std::true_type) | |||
| { | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| TypeParam m(123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountAlloc) | |||
| { | |||
| BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountHashAllocTest(std::true_type) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) | |||
| { | |||
| BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS | |||
| using has_alloc_std_constructors = std::true_type; | |||
| #else | |||
| using has_alloc_std_constructors = std::false_type; | |||
| #endif | |||
| template<typename T> | |||
| using expect_alloc_constructors = | |||
| absl::disjunction<absl::negation<is_std_unordered_map<T>>, has_alloc_std_constructors>; | |||
| template<typename TypeParam> | |||
| void AllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void AllocTest(std::true_type) | |||
| { | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| TypeParam m(alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(m, ::testing::UnorderedElementsAre()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, Alloc) | |||
| { | |||
| AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::UniqueGenerator<T>()); | |||
| TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::UniqueGenerator<T>()); | |||
| TypeParam m(values.begin(), values.end(), 123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) | |||
| { | |||
| InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketHashAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::UniqueGenerator<T>()); | |||
| TypeParam m(values.begin(), values.end(), 123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) | |||
| { | |||
| InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, CopyConstructor) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(gen()); | |||
| TypeParam n(m); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| template<typename TypeParam> | |||
| void CopyConstructorAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void CopyConstructorAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(gen()); | |||
| TypeParam n(m, A(11)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_NE(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) | |||
| { | |||
| CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| // TODO(alkis): Test non-propagating allocators on copy constructors. | |||
| TYPED_TEST_P(ConstructorTest, MoveConstructor) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(gen()); | |||
| TypeParam t(m); | |||
| TypeParam n(std::move(t)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| template<typename TypeParam> | |||
| void MoveConstructorAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void MoveConstructorAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(gen()); | |||
| TypeParam t(m); | |||
| TypeParam n(std::move(t), A(1)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_NE(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) | |||
| { | |||
| MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| // TODO(alkis): Test non-propagating allocators on move constructors. | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(values, 123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using A = typename TypeParam::allocator_type; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| A alloc(0); | |||
| TypeParam m(values, 123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) | |||
| { | |||
| InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketHashAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m(values, 123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) | |||
| { | |||
| InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, Assignment) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); | |||
| TypeParam n; | |||
| n = m; | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| // TODO(alkis): Test [non-]propagating allocators on move/copy assignments | |||
| // (it depends on traits). | |||
| TYPED_TEST_P(ConstructorTest, MoveAssignment) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); | |||
| TypeParam t(m); | |||
| TypeParam n; | |||
| n = std::move(t); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m; | |||
| m = values; | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}); | |||
| TypeParam n({gen()}); | |||
| n = m; | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}); | |||
| TypeParam t(m); | |||
| TypeParam n({gen()}); | |||
| n = std::move(t); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m; | |||
| m = values; | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::UniqueGenerator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m(values); | |||
| m = *&m; // Avoid -Wself-assign | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| // We cannot test self move as standard states that it leaves standard | |||
| // containers in unspecified state (and in practice in causes memory-leak | |||
| // according to heap-checker!). | |||
| REGISTER_TYPED_TEST_SUITE_P( | |||
| ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, InitializerListBucketAlloc, InitializerListBucketHashAlloc, Assignment, MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf | |||
| ); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_CONSTRUCTOR_TEST_H_ | |||
| @@ -0,0 +1,125 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ | |||
| #include "gmock/gmock.h" | |||
| #include "gtest/gtest.h" | |||
| #include "absl/container/internal/hash_generator_testing.h" | |||
| #include "absl/container/internal/hash_policy_testing.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class UnordMap> | |||
| class LookupTest : public ::testing::Test | |||
| { | |||
| }; | |||
| TYPED_TEST_SUITE_P(LookupTest); | |||
| TYPED_TEST_P(LookupTest, At) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m(values.begin(), values.end()); | |||
| for (const auto& p : values) | |||
| { | |||
| const auto& val = m.at(p.first); | |||
| EXPECT_EQ(p.second, val) << ::testing::PrintToString(p.first); | |||
| } | |||
| } | |||
| TYPED_TEST_P(LookupTest, OperatorBracket) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m; | |||
| for (const auto& p : values) | |||
| { | |||
| auto& val = m[p.first]; | |||
| EXPECT_EQ(V(), val) << ::testing::PrintToString(p.first); | |||
| val = p.second; | |||
| } | |||
| for (const auto& p : values) | |||
| EXPECT_EQ(p.second, m[p.first]) << ::testing::PrintToString(p.first); | |||
| } | |||
| TYPED_TEST_P(LookupTest, Count) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m; | |||
| for (const auto& p : values) | |||
| EXPECT_EQ(0, m.count(p.first)) << ::testing::PrintToString(p.first); | |||
| m.insert(values.begin(), values.end()); | |||
| for (const auto& p : values) | |||
| EXPECT_EQ(1, m.count(p.first)) << ::testing::PrintToString(p.first); | |||
| } | |||
| TYPED_TEST_P(LookupTest, Find) | |||
| { | |||
| using std::get; | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m; | |||
| for (const auto& p : values) | |||
| EXPECT_TRUE(m.end() == m.find(p.first)) | |||
| << ::testing::PrintToString(p.first); | |||
| m.insert(values.begin(), values.end()); | |||
| for (const auto& p : values) | |||
| { | |||
| auto it = m.find(p.first); | |||
| EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(p.first); | |||
| EXPECT_EQ(p.second, get<1>(*it)) << ::testing::PrintToString(p.first); | |||
| } | |||
| } | |||
| TYPED_TEST_P(LookupTest, EqualRange) | |||
| { | |||
| using std::get; | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m; | |||
| for (const auto& p : values) | |||
| { | |||
| auto r = m.equal_range(p.first); | |||
| ASSERT_EQ(0, std::distance(r.first, r.second)); | |||
| } | |||
| m.insert(values.begin(), values.end()); | |||
| for (const auto& p : values) | |||
| { | |||
| auto r = m.equal_range(p.first); | |||
| ASSERT_EQ(1, std::distance(r.first, r.second)); | |||
| EXPECT_EQ(p.second, get<1>(*r.first)) << ::testing::PrintToString(p.first); | |||
| } | |||
| } | |||
| REGISTER_TYPED_TEST_SUITE_P(LookupTest, At, OperatorBracket, Count, Find, EqualRange); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_LOOKUP_TEST_H_ | |||
| @@ -0,0 +1,90 @@ | |||
| // Copyright 2019 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ | |||
| #include <type_traits> | |||
| #include "gmock/gmock.h" | |||
| #include "gtest/gtest.h" | |||
| #include "absl/meta/type_traits.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class UnordMap> | |||
| class MembersTest : public ::testing::Test | |||
| { | |||
| }; | |||
| TYPED_TEST_SUITE_P(MembersTest); | |||
| template<typename T> | |||
| void UseType() | |||
| { | |||
| } | |||
| TYPED_TEST_P(MembersTest, Typedefs) | |||
| { | |||
| EXPECT_TRUE((std::is_same<std::pair<const typename TypeParam::key_type, typename TypeParam::mapped_type>, typename TypeParam::value_type>())); | |||
| EXPECT_TRUE((absl::conjunction< | |||
| absl::negation<std::is_signed<typename TypeParam::size_type>>, | |||
| std::is_integral<typename TypeParam::size_type>>())); | |||
| EXPECT_TRUE((absl::conjunction< | |||
| std::is_signed<typename TypeParam::difference_type>, | |||
| std::is_integral<typename TypeParam::difference_type>>())); | |||
| EXPECT_TRUE((std::is_convertible< | |||
| decltype(std::declval<const typename TypeParam::hasher&>()( | |||
| std::declval<const typename TypeParam::key_type&>() | |||
| )), | |||
| size_t>())); | |||
| EXPECT_TRUE((std::is_convertible< | |||
| decltype(std::declval<const typename TypeParam::key_equal&>()( | |||
| std::declval<const typename TypeParam::key_type&>(), | |||
| std::declval<const typename TypeParam::key_type&>() | |||
| )), | |||
| bool>())); | |||
| EXPECT_TRUE((std::is_same<typename TypeParam::allocator_type::value_type, typename TypeParam::value_type>())); | |||
| EXPECT_TRUE((std::is_same<typename TypeParam::value_type&, typename TypeParam::reference>())); | |||
| EXPECT_TRUE((std::is_same<const typename TypeParam::value_type&, typename TypeParam::const_reference>())); | |||
| EXPECT_TRUE((std::is_same<typename std::allocator_traits<typename TypeParam::allocator_type>::pointer, typename TypeParam::pointer>())); | |||
| EXPECT_TRUE( | |||
| (std::is_same<typename std::allocator_traits<typename TypeParam::allocator_type>::const_pointer, typename TypeParam::const_pointer>()) | |||
| ); | |||
| } | |||
| TYPED_TEST_P(MembersTest, SimpleFunctions) | |||
| { | |||
| EXPECT_GT(TypeParam().max_size(), 0); | |||
| } | |||
| TYPED_TEST_P(MembersTest, BeginEnd) | |||
| { | |||
| TypeParam t = {typename TypeParam::value_type{}}; | |||
| EXPECT_EQ(t.begin(), t.cbegin()); | |||
| EXPECT_EQ(t.end(), t.cend()); | |||
| EXPECT_NE(t.begin(), t.end()); | |||
| EXPECT_NE(t.cbegin(), t.cend()); | |||
| } | |||
| REGISTER_TYPED_TEST_SUITE_P(MembersTest, Typedefs, SimpleFunctions, BeginEnd); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MEMBERS_TEST_H_ | |||
| @@ -0,0 +1,370 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ | |||
| #include <memory> | |||
| #include "gmock/gmock.h" | |||
| #include "gtest/gtest.h" | |||
| #include "absl/container/internal/hash_generator_testing.h" | |||
| #include "absl/container/internal/hash_policy_testing.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class UnordMap> | |||
| class ModifiersTest : public ::testing::Test | |||
| { | |||
| }; | |||
| TYPED_TEST_SUITE_P(ModifiersTest); | |||
| TYPED_TEST_P(ModifiersTest, Clear) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m(values.begin(), values.end()); | |||
| ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| m.clear(); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_TRUE(m.empty()); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, Insert) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| auto p = m.insert(val); | |||
| EXPECT_TRUE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| p = m.insert(val2); | |||
| EXPECT_FALSE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertHint) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| auto it = m.insert(m.end(), val); | |||
| EXPECT_TRUE(it != m.end()); | |||
| EXPECT_EQ(val, *it); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| it = m.insert(it, val2); | |||
| EXPECT_TRUE(it != m.end()); | |||
| EXPECT_EQ(val, *it); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertRange) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m; | |||
| m.insert(values.begin(), values.end()); | |||
| ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| m.reserve(10); | |||
| const size_t original_capacity = m.bucket_count(); | |||
| m.insert(val); | |||
| EXPECT_EQ(m.bucket_count(), original_capacity); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| m.insert(val2); | |||
| EXPECT_EQ(m.bucket_count(), original_capacity); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertRangeWithinCapacity) | |||
| { | |||
| #if !defined(__GLIBCXX__) | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> base_values; | |||
| std::generate_n(std::back_inserter(base_values), 10, hash_internal::Generator<T>()); | |||
| std::vector<T> values; | |||
| while (values.size() != 100) | |||
| { | |||
| std::copy_n(base_values.begin(), 10, std::back_inserter(values)); | |||
| } | |||
| TypeParam m; | |||
| m.reserve(10); | |||
| const size_t original_capacity = m.bucket_count(); | |||
| m.insert(values.begin(), values.end()); | |||
| EXPECT_EQ(m.bucket_count(), original_capacity); | |||
| #endif | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertOrAssign) | |||
| { | |||
| #ifdef UNORDERED_MAP_CXX17 | |||
| using std::get; | |||
| using K = typename TypeParam::key_type; | |||
| using V = typename TypeParam::mapped_type; | |||
| K k = hash_internal::Generator<K>()(); | |||
| V val = hash_internal::Generator<V>()(); | |||
| TypeParam m; | |||
| auto p = m.insert_or_assign(k, val); | |||
| EXPECT_TRUE(p.second); | |||
| EXPECT_EQ(k, get<0>(*p.first)); | |||
| EXPECT_EQ(val, get<1>(*p.first)); | |||
| V val2 = hash_internal::Generator<V>()(); | |||
| p = m.insert_or_assign(k, val2); | |||
| EXPECT_FALSE(p.second); | |||
| EXPECT_EQ(k, get<0>(*p.first)); | |||
| EXPECT_EQ(val2, get<1>(*p.first)); | |||
| #endif | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, InsertOrAssignHint) | |||
| { | |||
| #ifdef UNORDERED_MAP_CXX17 | |||
| using std::get; | |||
| using K = typename TypeParam::key_type; | |||
| using V = typename TypeParam::mapped_type; | |||
| K k = hash_internal::Generator<K>()(); | |||
| V val = hash_internal::Generator<V>()(); | |||
| TypeParam m; | |||
| auto it = m.insert_or_assign(m.end(), k, val); | |||
| EXPECT_TRUE(it != m.end()); | |||
| EXPECT_EQ(k, get<0>(*it)); | |||
| EXPECT_EQ(val, get<1>(*it)); | |||
| V val2 = hash_internal::Generator<V>()(); | |||
| it = m.insert_or_assign(it, k, val2); | |||
| EXPECT_EQ(k, get<0>(*it)); | |||
| EXPECT_EQ(val2, get<1>(*it)); | |||
| #endif | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, Emplace) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps | |||
| // with test traits/policy. | |||
| auto p = m.emplace(val); | |||
| EXPECT_TRUE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| p = m.emplace(val2); | |||
| EXPECT_FALSE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, EmplaceHint) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps | |||
| // with test traits/policy. | |||
| auto it = m.emplace_hint(m.end(), val); | |||
| EXPECT_EQ(val, *it); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| it = m.emplace_hint(it, val2); | |||
| EXPECT_EQ(val, *it); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, TryEmplace) | |||
| { | |||
| #ifdef UNORDERED_MAP_CXX17 | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps | |||
| // with test traits/policy. | |||
| auto p = m.try_emplace(val.first, val.second); | |||
| EXPECT_TRUE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| p = m.try_emplace(val2.first, val2.second); | |||
| EXPECT_FALSE(p.second); | |||
| EXPECT_EQ(val, *p.first); | |||
| #endif | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, TryEmplaceHint) | |||
| { | |||
| #ifdef UNORDERED_MAP_CXX17 | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| // TODO(alkis): We need a way to run emplace in a more meaningful way. Perhaps | |||
| // with test traits/policy. | |||
| auto it = m.try_emplace(m.end(), val.first, val.second); | |||
| EXPECT_EQ(val, *it); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| it = m.try_emplace(it, val2.first, val2.second); | |||
| EXPECT_EQ(val, *it); | |||
| #endif | |||
| } | |||
| template<class V> | |||
| using IfNotVoid = typename std::enable_if<!std::is_void<V>::value, V>::type; | |||
| // In openmap we chose not to return the iterator from erase because that's | |||
| // more expensive. As such we adapt erase to return an iterator here. | |||
| struct EraseFirst | |||
| { | |||
| template<class Map> | |||
| auto operator()(Map* m, int) const | |||
| -> IfNotVoid<decltype(m->erase(m->begin()))> | |||
| { | |||
| return m->erase(m->begin()); | |||
| } | |||
| template<class Map> | |||
| typename Map::iterator operator()(Map* m, ...) const | |||
| { | |||
| auto it = m->begin(); | |||
| m->erase(it++); | |||
| return it; | |||
| } | |||
| }; | |||
| TYPED_TEST_P(ModifiersTest, Erase) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using std::get; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m(values.begin(), values.end()); | |||
| ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| auto& first = *m.begin(); | |||
| std::vector<T> values2; | |||
| for (const auto& val : values) | |||
| if (get<0>(val) != get<0>(first)) | |||
| values2.push_back(val); | |||
| auto it = EraseFirst()(&m, 0); | |||
| ASSERT_TRUE(it != m.end()); | |||
| EXPECT_EQ(1, std::count(values2.begin(), values2.end(), *it)); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values2.begin(), values2.end())); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, EraseRange) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m(values.begin(), values.end()); | |||
| ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| auto it = m.erase(m.begin(), m.end()); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_TRUE(it == m.end()); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, EraseKey) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> values; | |||
| std::generate_n(std::back_inserter(values), 10, hash_internal::Generator<T>()); | |||
| TypeParam m(values.begin(), values.end()); | |||
| ASSERT_THAT(items(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_EQ(1, m.erase(values[0].first)); | |||
| EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0])); | |||
| EXPECT_THAT(items(m), ::testing::UnorderedElementsAreArray(values.begin() + 1, values.end())); | |||
| } | |||
| TYPED_TEST_P(ModifiersTest, Swap) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| std::vector<T> v1; | |||
| std::vector<T> v2; | |||
| std::generate_n(std::back_inserter(v1), 5, hash_internal::Generator<T>()); | |||
| std::generate_n(std::back_inserter(v2), 5, hash_internal::Generator<T>()); | |||
| TypeParam m1(v1.begin(), v1.end()); | |||
| TypeParam m2(v2.begin(), v2.end()); | |||
| EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v1)); | |||
| EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v2)); | |||
| m1.swap(m2); | |||
| EXPECT_THAT(items(m1), ::testing::UnorderedElementsAreArray(v2)); | |||
| EXPECT_THAT(items(m2), ::testing::UnorderedElementsAreArray(v1)); | |||
| } | |||
| // TODO(alkis): Write tests for extract. | |||
| // TODO(alkis): Write tests for merge. | |||
| REGISTER_TYPED_TEST_SUITE_P(ModifiersTest, Clear, Insert, InsertHint, InsertRange, InsertWithinCapacity, InsertRangeWithinCapacity, InsertOrAssign, InsertOrAssignHint, Emplace, EmplaceHint, TryEmplace, TryEmplaceHint, Erase, EraseRange, EraseKey, Swap); | |||
| template<typename Type> | |||
| struct is_unique_ptr : std::false_type | |||
| { | |||
| }; | |||
| template<typename Type> | |||
| struct is_unique_ptr<std::unique_ptr<Type>> : std::true_type | |||
| { | |||
| }; | |||
| template<class UnordMap> | |||
| class UniquePtrModifiersTest : public ::testing::Test | |||
| { | |||
| protected: | |||
| UniquePtrModifiersTest() | |||
| { | |||
| static_assert(is_unique_ptr<typename UnordMap::mapped_type>::value, "UniquePtrModifiersTyest may only be called with a " | |||
| "std::unique_ptr value type."); | |||
| } | |||
| }; | |||
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UniquePtrModifiersTest); | |||
| TYPED_TEST_SUITE_P(UniquePtrModifiersTest); | |||
| // Test that we do not move from rvalue arguments if an insertion does not | |||
| // happen. | |||
| TYPED_TEST_P(UniquePtrModifiersTest, TryEmplace) | |||
| { | |||
| #ifdef UNORDERED_MAP_CXX17 | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using V = typename TypeParam::mapped_type; | |||
| T val = hash_internal::Generator<T>()(); | |||
| TypeParam m; | |||
| auto p = m.try_emplace(val.first, std::move(val.second)); | |||
| EXPECT_TRUE(p.second); | |||
| // A moved from std::unique_ptr is guaranteed to be nullptr. | |||
| EXPECT_EQ(val.second, nullptr); | |||
| T val2 = {val.first, hash_internal::Generator<V>()()}; | |||
| p = m.try_emplace(val2.first, std::move(val2.second)); | |||
| EXPECT_FALSE(p.second); | |||
| EXPECT_NE(val2.second, nullptr); | |||
| #endif | |||
| } | |||
| REGISTER_TYPED_TEST_SUITE_P(UniquePtrModifiersTest, TryEmplace); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_MAP_MODIFIERS_TEST_H_ | |||
| @@ -0,0 +1,551 @@ | |||
| // Copyright 2018 The Abseil Authors. | |||
| // | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // https://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| #ifndef ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ | |||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ | |||
| #include <algorithm> | |||
| #include <unordered_set> | |||
| #include <vector> | |||
| #include "gmock/gmock.h" | |||
| #include "gtest/gtest.h" | |||
| #include "absl/container/internal/hash_generator_testing.h" | |||
| #include "absl/container/internal/hash_policy_testing.h" | |||
| #include "absl/meta/type_traits.h" | |||
| namespace absl | |||
| { | |||
| ABSL_NAMESPACE_BEGIN | |||
| namespace container_internal | |||
| { | |||
| template<class UnordMap> | |||
| class ConstructorTest : public ::testing::Test | |||
| { | |||
| }; | |||
| TYPED_TEST_SUITE_P(ConstructorTest); | |||
| TYPED_TEST_P(ConstructorTest, NoArgs) | |||
| { | |||
| TypeParam m; | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCount) | |||
| { | |||
| TypeParam m(123); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHash) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| H hasher; | |||
| TypeParam m(123, hasher); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashEqual) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| H hasher; | |||
| E equal; | |||
| TypeParam m(123, hasher, equal); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashEqualAlloc) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| const auto& cm = m; | |||
| EXPECT_EQ(cm.hash_function(), hasher); | |||
| EXPECT_EQ(cm.key_eq(), equal); | |||
| EXPECT_EQ(cm.get_allocator(), alloc); | |||
| EXPECT_TRUE(cm.empty()); | |||
| EXPECT_THAT(keys(cm), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(cm.bucket_count(), 123); | |||
| } | |||
| template<typename T> | |||
| struct is_std_unordered_set : std::false_type | |||
| { | |||
| }; | |||
| template<typename... T> | |||
| struct is_std_unordered_set<std::unordered_set<T...>> : std::true_type | |||
| { | |||
| }; | |||
| #if defined(UNORDERED_SET_CXX14) || defined(UNORDERED_SET_CXX17) | |||
| using has_cxx14_std_apis = std::true_type; | |||
| #else | |||
| using has_cxx14_std_apis = std::false_type; | |||
| #endif | |||
| template<typename T> | |||
| using expect_cxx14_apis = | |||
| absl::disjunction<absl::negation<is_std_unordered_set<T>>, has_cxx14_std_apis>; | |||
| template<typename TypeParam> | |||
| void BucketCountAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountAllocTest(std::true_type) | |||
| { | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| TypeParam m(123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountAlloc) | |||
| { | |||
| BucketCountAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void BucketCountHashAllocTest(std::true_type) | |||
| { | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, BucketCountHashAlloc) | |||
| { | |||
| BucketCountHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| #if ABSL_UNORDERED_SUPPORTS_ALLOC_CTORS | |||
| using has_alloc_std_constructors = std::true_type; | |||
| #else | |||
| using has_alloc_std_constructors = std::false_type; | |||
| #endif | |||
| template<typename T> | |||
| using expect_alloc_constructors = | |||
| absl::disjunction<absl::negation<is_std_unordered_set<T>>, has_alloc_std_constructors>; | |||
| template<typename TypeParam> | |||
| void AllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void AllocTest(std::true_type) | |||
| { | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| TypeParam m(alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_TRUE(m.empty()); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, Alloc) | |||
| { | |||
| AllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashEqualAlloc) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| for (size_t i = 0; i != 10; ++i) | |||
| values.push_back(hash_internal::Generator<T>()()); | |||
| TypeParam m(values.begin(), values.end(), 123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using A = typename TypeParam::allocator_type; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| for (size_t i = 0; i != 10; ++i) | |||
| values.push_back(hash_internal::Generator<T>()()); | |||
| TypeParam m(values.begin(), values.end(), 123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketAlloc) | |||
| { | |||
| InputIteratorBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InputIteratorBucketHashAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| std::vector<T> values; | |||
| for (size_t i = 0; i != 10; ++i) | |||
| values.push_back(hash_internal::Generator<T>()()); | |||
| TypeParam m(values.begin(), values.end(), 123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InputIteratorBucketHashAlloc) | |||
| { | |||
| InputIteratorBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, CopyConstructor) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(hash_internal::Generator<T>()()); | |||
| TypeParam n(m); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| EXPECT_NE(TypeParam(0, hasher, equal, alloc), n); | |||
| } | |||
| template<typename TypeParam> | |||
| void CopyConstructorAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void CopyConstructorAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(hash_internal::Generator<T>()()); | |||
| TypeParam n(m, A(11)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_NE(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, CopyConstructorAlloc) | |||
| { | |||
| CopyConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| // TODO(alkis): Test non-propagating allocators on copy constructors. | |||
| TYPED_TEST_P(ConstructorTest, MoveConstructor) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(hash_internal::Generator<T>()()); | |||
| TypeParam t(m); | |||
| TypeParam n(std::move(t)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| template<typename TypeParam> | |||
| void MoveConstructorAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void MoveConstructorAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(123, hasher, equal, alloc); | |||
| for (size_t i = 0; i != 10; ++i) | |||
| m.insert(hash_internal::Generator<T>()()); | |||
| TypeParam t(m); | |||
| TypeParam n(std::move(t), A(1)); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_NE(m.get_allocator(), n.get_allocator()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, MoveConstructorAlloc) | |||
| { | |||
| MoveConstructorAllocTest<TypeParam>(expect_alloc_constructors<TypeParam>()); | |||
| } | |||
| // TODO(alkis): Test non-propagating allocators on move constructors. | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketHashEqualAlloc) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| TypeParam m(values, 123, hasher, equal, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.key_eq(), equal); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using A = typename TypeParam::allocator_type; | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| A alloc(0); | |||
| TypeParam m(values, 123, alloc); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketAlloc) | |||
| { | |||
| InitializerListBucketAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketHashAllocTest(std::false_type) | |||
| { | |||
| } | |||
| template<typename TypeParam> | |||
| void InitializerListBucketHashAllocTest(std::true_type) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| A alloc(0); | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m(values, 123, hasher, alloc); | |||
| EXPECT_EQ(m.hash_function(), hasher); | |||
| EXPECT_EQ(m.get_allocator(), alloc); | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| EXPECT_GE(m.bucket_count(), 123); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, InitializerListBucketHashAlloc) | |||
| { | |||
| InitializerListBucketHashAllocTest<TypeParam>(expect_cxx14_apis<TypeParam>()); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, CopyAssignment) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::Generator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); | |||
| TypeParam n; | |||
| n = m; | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| // TODO(alkis): Test [non-]propagating allocators on move/copy assignments | |||
| // (it depends on traits). | |||
| TYPED_TEST_P(ConstructorTest, MoveAssignment) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| using H = typename TypeParam::hasher; | |||
| using E = typename TypeParam::key_equal; | |||
| using A = typename TypeParam::allocator_type; | |||
| H hasher; | |||
| E equal; | |||
| A alloc(0); | |||
| hash_internal::Generator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}, 123, hasher, equal, alloc); | |||
| TypeParam t(m); | |||
| TypeParam n; | |||
| n = std::move(t); | |||
| EXPECT_EQ(m.hash_function(), n.hash_function()); | |||
| EXPECT_EQ(m.key_eq(), n.key_eq()); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerList) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m; | |||
| m = values; | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}); | |||
| TypeParam n({gen()}); | |||
| n = m; | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, MoveAssignmentOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| TypeParam m({gen(), gen(), gen()}); | |||
| TypeParam t(m); | |||
| TypeParam n({gen()}); | |||
| n = std::move(t); | |||
| EXPECT_EQ(m, n); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentFromInitializerListOverwritesExisting) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m; | |||
| m = values; | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| TYPED_TEST_P(ConstructorTest, AssignmentOnSelf) | |||
| { | |||
| using T = hash_internal::GeneratedType<TypeParam>; | |||
| hash_internal::Generator<T> gen; | |||
| std::initializer_list<T> values = {gen(), gen(), gen(), gen(), gen()}; | |||
| TypeParam m(values); | |||
| m = *&m; // Avoid -Wself-assign. | |||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||
| } | |||
| REGISTER_TYPED_TEST_SUITE_P( | |||
| ConstructorTest, NoArgs, BucketCount, BucketCountHash, BucketCountHashEqual, BucketCountHashEqualAlloc, BucketCountAlloc, BucketCountHashAlloc, Alloc, InputIteratorBucketHashEqualAlloc, InputIteratorBucketAlloc, InputIteratorBucketHashAlloc, CopyConstructor, CopyConstructorAlloc, MoveConstructor, MoveConstructorAlloc, InitializerListBucketHashEqualAlloc, InitializerListBucketAlloc, InitializerListBucketHashAlloc, CopyAssignment, MoveAssignment, AssignmentFromInitializerList, AssignmentOverwritesExisting, MoveAssignmentOverwritesExisting, AssignmentFromInitializerListOverwritesExisting, AssignmentOnSelf | |||
| ); | |||
| } // namespace container_internal | |||
| ABSL_NAMESPACE_END | |||
| } // namespace absl | |||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_CONSTRUCTOR_TEST_H_ | |||