| @@ -1,6 +1,8 @@ | |||||
| tclap/** linguist-vendored | |||||
| * text=auto | |||||
| tclap/** linguist-vendored | |||||
| spdlog/** linguist-vendored | spdlog/** linguist-vendored | ||||
| grpc/** linguist-vendored | |||||
| proto/** linguist-generated | proto/** linguist-generated | ||||
| @@ -499,3 +499,4 @@ CTestTestfile.cmake | |||||
| _deps | _deps | ||||
| /build/ | /build/ | ||||
| /lib/ | |||||
| @@ -90,10 +90,15 @@ | |||||
| <ConformanceMode>true</ConformanceMode> | <ConformanceMode>true</ConformanceMode> | ||||
| <LanguageStandard>stdcpp17</LanguageStandard> | <LanguageStandard>stdcpp17</LanguageStandard> | ||||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | <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> | </ClCompile> | ||||
| <Link> | <Link> | ||||
| <SubSystem>Console</SubSystem> | <SubSystem>Console</SubSystem> | ||||
| <GenerateDebugInformation>true</GenerateDebugInformation> | <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> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | ||||
| @@ -106,12 +111,17 @@ | |||||
| <ConformanceMode>true</ConformanceMode> | <ConformanceMode>true</ConformanceMode> | ||||
| <LanguageStandard>stdcpp17</LanguageStandard> | <LanguageStandard>stdcpp17</LanguageStandard> | ||||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | <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> | </ClCompile> | ||||
| <Link> | <Link> | ||||
| <SubSystem>Console</SubSystem> | <SubSystem>Console</SubSystem> | ||||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
| <OptimizeReferences>true</OptimizeReferences> | <OptimizeReferences>true</OptimizeReferences> | ||||
| <GenerateDebugInformation>true</GenerateDebugInformation> | <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> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||||
| @@ -122,10 +132,15 @@ | |||||
| <ConformanceMode>true</ConformanceMode> | <ConformanceMode>true</ConformanceMode> | ||||
| <LanguageStandard>stdcpp17</LanguageStandard> | <LanguageStandard>stdcpp17</LanguageStandard> | ||||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | <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> | </ClCompile> | ||||
| <Link> | <Link> | ||||
| <SubSystem>Console</SubSystem> | <SubSystem>Console</SubSystem> | ||||
| <GenerateDebugInformation>true</GenerateDebugInformation> | <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> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||||
| @@ -138,15 +153,47 @@ | |||||
| <ConformanceMode>true</ConformanceMode> | <ConformanceMode>true</ConformanceMode> | ||||
| <LanguageStandard>stdcpp17</LanguageStandard> | <LanguageStandard>stdcpp17</LanguageStandard> | ||||
| <LanguageStandard_C>stdc17</LanguageStandard_C> | <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> | </ClCompile> | ||||
| <Link> | <Link> | ||||
| <SubSystem>Console</SubSystem> | <SubSystem>Console</SubSystem> | ||||
| <EnableCOMDATFolding>true</EnableCOMDATFolding> | <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||||
| <OptimizeReferences>true</OptimizeReferences> | <OptimizeReferences>true</OptimizeReferences> | ||||
| <GenerateDebugInformation>true</GenerateDebugInformation> | <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> | </Link> | ||||
| </ItemDefinitionGroup> | </ItemDefinitionGroup> | ||||
| <ItemGroup> | <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> | </ItemGroup> | ||||
| <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||||
| <ImportGroup Label="ExtensionTargets"> | <ImportGroup Label="ExtensionTargets"> | ||||
| @@ -13,5 +13,90 @@ | |||||
| <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | <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> | <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | ||||
| </Filter> | </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> | </ItemGroup> | ||||
| </Project> | </Project> | ||||
| @@ -42,12 +42,12 @@ private: | |||||
| std::unique_ptr<Communication> pComm; | std::unique_ptr<Communication> pComm; | ||||
| // ID、阵营记录 | // ID、阵营记录 | ||||
| int64_t playerID; | |||||
| THUAI6::PlayerType playerType; | THUAI6::PlayerType playerType; | ||||
| int64_t playerID; | |||||
| // 类型记录 | // 类型记录 | ||||
| THUAI6::StudentType studentType; | |||||
| THUAI6::TrickerType trickerType; | THUAI6::TrickerType trickerType; | ||||
| THUAI6::StudentType studentType; | |||||
| // GUID信息 | // GUID信息 | ||||
| std::vector<int64_t> playerGUIDs; | std::vector<int64_t> playerGUIDs; | ||||
| @@ -177,7 +177,7 @@ namespace THUAI6 | |||||
| int32_t viewRange; // 视野范围 | int32_t viewRange; // 视野范围 | ||||
| int64_t playerID; // 玩家ID | int64_t playerID; // 玩家ID | ||||
| int64_t guid; // 全局唯一ID | int64_t guid; // 全局唯一ID | ||||
| int16_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||||
| int32_t radius; // 圆形物体的半径或正方形物体的内切圆半径 | |||||
| int32_t score; // 分数 | int32_t score; // 分数 | ||||
| double facingDirection; // 朝向 | double facingDirection; // 朝向 | ||||
| @@ -23,6 +23,11 @@ namespace AssistFunction | |||||
| return grid / numOfGridPerCell; | 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) | inline bool HaveView(int viewRange, int x, int y, int newX, int newY, std::vector<std::vector<THUAI6::PlaceType>>& map) | ||||
| { | { | ||||
| int deltaX = newX - x; | int deltaX = newX - x; | ||||
| @@ -419,7 +424,7 @@ namespace THUAI62Proto | |||||
| return playerMsg; | return playerMsg; | ||||
| } | } | ||||
| inline protobuf::IDMsg THUAI62ProtobufID(int playerID) | |||||
| inline protobuf::IDMsg THUAI62ProtobufID(int64_t playerID) | |||||
| { | { | ||||
| protobuf::IDMsg idMsg; | protobuf::IDMsg idMsg; | ||||
| idMsg.set_player_id(playerID); | idMsg.set_player_id(playerID); | ||||
| @@ -84,7 +84,7 @@ std::vector<std::vector<THUAI6::PlaceType>> Logic::GetFullMap() const | |||||
| THUAI6::PlaceType Logic::GetPlaceType(int32_t cellX, int32_t cellY) const | THUAI6::PlaceType Logic::GetPlaceType(int32_t cellX, int32_t cellY) const | ||||
| { | { | ||||
| std::unique_lock<std::mutex> lock(mtxState); | 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!"); | logger->warn("Invalid position!"); | ||||
| return THUAI6::PlaceType::NullPlaceType; | return THUAI6::PlaceType::NullPlaceType; | ||||
| @@ -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,159 @@ | |||||
| // 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,762 @@ | |||||
| // 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,219 @@ | |||||
| // 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,180 @@ | |||||
| // | |||||
| // 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,913 @@ | |||||
| // | |||||
| // 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,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. | |||||
| // | |||||
| // ----------------------------------------------------------------------------- | |||||
| // 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,471 @@ | |||||
| // 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,200 @@ | |||||
| // 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,34 @@ | |||||
| // 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,159 @@ | |||||
| // | |||||
| // 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,169 @@ | |||||
| // 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,398 @@ | |||||
| // 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,282 @@ | |||||
| // 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,43 @@ | |||||
| // 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,50 @@ | |||||
| // | |||||
| // 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,51 @@ | |||||
| // 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,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. | |||||
| // | |||||
| #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,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. | |||||
| #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,241 @@ | |||||
| // 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,126 @@ | |||||
| // 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,134 @@ | |||||
| // 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,138 @@ | |||||
| // 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,196 @@ | |||||
| // 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,58 @@ | |||||
| // 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,45 @@ | |||||
| // | |||||
| // 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,256 @@ | |||||
| // | |||||
| // 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,95 @@ | |||||
| // 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,39 @@ | |||||
| // 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,74 @@ | |||||
| // 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,271 @@ | |||||
| // 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,265 @@ | |||||
| // 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,75 @@ | |||||
| // | |||||
| // 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,82 @@ | |||||
| // | |||||
| // 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,133 @@ | |||||
| // 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,172 @@ | |||||
| // 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,158 @@ | |||||
| // | |||||
| // 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,252 @@ | |||||
| // | |||||
| // 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,238 @@ | |||||
| // 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,335 @@ | |||||
| // 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,140 @@ | |||||
| // 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,100 @@ | |||||
| // 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,851 @@ | |||||
| // 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,793 @@ | |||||
| // 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,166 @@ | |||||
| // 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,529 @@ | |||||
| // 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,613 @@ | |||||
| // 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,510 @@ | |||||
| // 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,866 @@ | |||||
| // 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,699 @@ | |||||
| // 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,207 @@ | |||||
| // 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,290 @@ | |||||
| // 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,442 @@ | |||||
| // 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,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. | |||||
| #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,163 @@ | |||||
| // 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,182 @@ | |||||
| // 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,184 @@ | |||||
| // 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,208 @@ | |||||
| // 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,110 @@ | |||||
| // 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,85 @@ | |||||
| // 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,299 @@ | |||||
| // 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,953 @@ | |||||
| // 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_INLINED_VECTOR_INTERNAL_H_ | |||||
| #define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ | |||||
| #include <algorithm> | |||||
| #include <cstddef> | |||||
| #include <cstring> | |||||
| #include <iterator> | |||||
| #include <limits> | |||||
| #include <memory> | |||||
| #include <new> | |||||
| #include <type_traits> | |||||
| #include <utility> | |||||
| #include "absl/base/attributes.h" | |||||
| #include "absl/base/macros.h" | |||||
| #include "absl/container/internal/compressed_tuple.h" | |||||
| #include "absl/memory/memory.h" | |||||
| #include "absl/meta/type_traits.h" | |||||
| #include "absl/types/span.h" | |||||
| namespace absl { | |||||
| ABSL_NAMESPACE_BEGIN | |||||
| namespace inlined_vector_internal { | |||||
| // GCC does not deal very well with the below code | |||||
| #if !defined(__clang__) && defined(__GNUC__) | |||||
| #pragma GCC diagnostic push | |||||
| #pragma GCC diagnostic ignored "-Warray-bounds" | |||||
| #endif | |||||
| template <typename A> | |||||
| using AllocatorTraits = std::allocator_traits<A>; | |||||
| template <typename A> | |||||
| using ValueType = typename AllocatorTraits<A>::value_type; | |||||
| template <typename A> | |||||
| using SizeType = typename AllocatorTraits<A>::size_type; | |||||
| template <typename A> | |||||
| using Pointer = typename AllocatorTraits<A>::pointer; | |||||
| template <typename A> | |||||
| using ConstPointer = typename AllocatorTraits<A>::const_pointer; | |||||
| template <typename A> | |||||
| using SizeType = typename AllocatorTraits<A>::size_type; | |||||
| template <typename A> | |||||
| using DifferenceType = typename AllocatorTraits<A>::difference_type; | |||||
| template <typename A> | |||||
| using Reference = ValueType<A>&; | |||||
| template <typename A> | |||||
| using ConstReference = const ValueType<A>&; | |||||
| template <typename A> | |||||
| using Iterator = Pointer<A>; | |||||
| template <typename A> | |||||
| using ConstIterator = ConstPointer<A>; | |||||
| template <typename A> | |||||
| using ReverseIterator = typename std::reverse_iterator<Iterator<A>>; | |||||
| template <typename A> | |||||
| using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>; | |||||
| template <typename A> | |||||
| using MoveIterator = typename std::move_iterator<Iterator<A>>; | |||||
| template <typename Iterator> | |||||
| using IsAtLeastForwardIterator = std::is_convertible< | |||||
| typename std::iterator_traits<Iterator>::iterator_category, | |||||
| std::forward_iterator_tag>; | |||||
| template <typename A> | |||||
| using IsMemcpyOk = | |||||
| absl::conjunction<std::is_same<A, std::allocator<ValueType<A>>>, | |||||
| absl::is_trivially_copy_constructible<ValueType<A>>, | |||||
| absl::is_trivially_copy_assignable<ValueType<A>>, | |||||
| absl::is_trivially_destructible<ValueType<A>>>; | |||||
| template <typename T> | |||||
| struct TypeIdentity { | |||||
| using type = T; | |||||
| }; | |||||
| // Used for function arguments in template functions to prevent ADL by forcing | |||||
| // callers to explicitly specify the template parameter. | |||||
| template <typename T> | |||||
| using NoTypeDeduction = typename TypeIdentity<T>::type; | |||||
| template <typename A, bool IsTriviallyDestructible = | |||||
| absl::is_trivially_destructible<ValueType<A>>::value> | |||||
| struct DestroyAdapter; | |||||
| template <typename A> | |||||
| struct DestroyAdapter<A, /* IsTriviallyDestructible */ false> { | |||||
| static void DestroyElements(A& allocator, Pointer<A> destroy_first, | |||||
| SizeType<A> destroy_size) { | |||||
| for (SizeType<A> i = destroy_size; i != 0;) { | |||||
| --i; | |||||
| AllocatorTraits<A>::destroy(allocator, destroy_first + i); | |||||
| } | |||||
| } | |||||
| }; | |||||
| template <typename A> | |||||
| struct DestroyAdapter<A, /* IsTriviallyDestructible */ true> { | |||||
| static void DestroyElements(A& allocator, Pointer<A> destroy_first, | |||||
| SizeType<A> destroy_size) { | |||||
| static_cast<void>(allocator); | |||||
| static_cast<void>(destroy_first); | |||||
| static_cast<void>(destroy_size); | |||||
| } | |||||
| }; | |||||
| template <typename A> | |||||
| struct Allocation { | |||||
| Pointer<A> data; | |||||
| SizeType<A> capacity; | |||||
| }; | |||||
| template <typename A, | |||||
| bool IsOverAligned = | |||||
| (alignof(ValueType<A>) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> | |||||
| struct MallocAdapter { | |||||
| static Allocation<A> Allocate(A& allocator, SizeType<A> requested_capacity) { | |||||
| return {AllocatorTraits<A>::allocate(allocator, requested_capacity), | |||||
| requested_capacity}; | |||||
| } | |||||
| static void Deallocate(A& allocator, Pointer<A> pointer, | |||||
| SizeType<A> capacity) { | |||||
| AllocatorTraits<A>::deallocate(allocator, pointer, capacity); | |||||
| } | |||||
| }; | |||||
| template <typename A, typename ValueAdapter> | |||||
| void ConstructElements(NoTypeDeduction<A>& allocator, | |||||
| Pointer<A> construct_first, ValueAdapter& values, | |||||
| SizeType<A> construct_size) { | |||||
| for (SizeType<A> i = 0; i < construct_size; ++i) { | |||||
| ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } | |||||
| ABSL_INTERNAL_CATCH_ANY { | |||||
| DestroyAdapter<A>::DestroyElements(allocator, construct_first, i); | |||||
| ABSL_INTERNAL_RETHROW; | |||||
| } | |||||
| } | |||||
| } | |||||
| template <typename A, typename ValueAdapter> | |||||
| void AssignElements(Pointer<A> assign_first, ValueAdapter& values, | |||||
| SizeType<A> assign_size) { | |||||
| for (SizeType<A> i = 0; i < assign_size; ++i) { | |||||
| values.AssignNext(assign_first + i); | |||||
| } | |||||
| } | |||||
| template <typename A> | |||||
| struct StorageView { | |||||
| Pointer<A> data; | |||||
| SizeType<A> size; | |||||
| SizeType<A> capacity; | |||||
| }; | |||||
| template <typename A, typename Iterator> | |||||
| class IteratorValueAdapter { | |||||
| public: | |||||
| explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} | |||||
| void ConstructNext(A& allocator, Pointer<A> construct_at) { | |||||
| AllocatorTraits<A>::construct(allocator, construct_at, *it_); | |||||
| ++it_; | |||||
| } | |||||
| void AssignNext(Pointer<A> assign_at) { | |||||
| *assign_at = *it_; | |||||
| ++it_; | |||||
| } | |||||
| private: | |||||
| Iterator it_; | |||||
| }; | |||||
| template <typename A> | |||||
| class CopyValueAdapter { | |||||
| public: | |||||
| explicit CopyValueAdapter(ConstPointer<A> p) : ptr_(p) {} | |||||
| void ConstructNext(A& allocator, Pointer<A> construct_at) { | |||||
| AllocatorTraits<A>::construct(allocator, construct_at, *ptr_); | |||||
| } | |||||
| void AssignNext(Pointer<A> assign_at) { *assign_at = *ptr_; } | |||||
| private: | |||||
| ConstPointer<A> ptr_; | |||||
| }; | |||||
| template <typename A> | |||||
| class DefaultValueAdapter { | |||||
| public: | |||||
| explicit DefaultValueAdapter() {} | |||||
| void ConstructNext(A& allocator, Pointer<A> construct_at) { | |||||
| AllocatorTraits<A>::construct(allocator, construct_at); | |||||
| } | |||||
| void AssignNext(Pointer<A> assign_at) { *assign_at = ValueType<A>(); } | |||||
| }; | |||||
| template <typename A> | |||||
| class AllocationTransaction { | |||||
| public: | |||||
| explicit AllocationTransaction(A& allocator) | |||||
| : allocator_data_(allocator, nullptr), capacity_(0) {} | |||||
| ~AllocationTransaction() { | |||||
| if (DidAllocate()) { | |||||
| MallocAdapter<A>::Deallocate(GetAllocator(), GetData(), GetCapacity()); | |||||
| } | |||||
| } | |||||
| AllocationTransaction(const AllocationTransaction&) = delete; | |||||
| void operator=(const AllocationTransaction&) = delete; | |||||
| A& GetAllocator() { return allocator_data_.template get<0>(); } | |||||
| Pointer<A>& GetData() { return allocator_data_.template get<1>(); } | |||||
| SizeType<A>& GetCapacity() { return capacity_; } | |||||
| bool DidAllocate() { return GetData() != nullptr; } | |||||
| Pointer<A> Allocate(SizeType<A> requested_capacity) { | |||||
| Allocation<A> result = | |||||
| MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); | |||||
| GetData() = result.data; | |||||
| GetCapacity() = result.capacity; | |||||
| return result.data; | |||||
| } | |||||
| ABSL_MUST_USE_RESULT Allocation<A> Release() && { | |||||
| Allocation<A> result = {GetData(), GetCapacity()}; | |||||
| Reset(); | |||||
| return result; | |||||
| } | |||||
| private: | |||||
| void Reset() { | |||||
| GetData() = nullptr; | |||||
| GetCapacity() = 0; | |||||
| } | |||||
| container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; | |||||
| SizeType<A> capacity_; | |||||
| }; | |||||
| template <typename A> | |||||
| class ConstructionTransaction { | |||||
| public: | |||||
| explicit ConstructionTransaction(A& allocator) | |||||
| : allocator_data_(allocator, nullptr), size_(0) {} | |||||
| ~ConstructionTransaction() { | |||||
| if (DidConstruct()) { | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), GetData(), GetSize()); | |||||
| } | |||||
| } | |||||
| ConstructionTransaction(const ConstructionTransaction&) = delete; | |||||
| void operator=(const ConstructionTransaction&) = delete; | |||||
| A& GetAllocator() { return allocator_data_.template get<0>(); } | |||||
| Pointer<A>& GetData() { return allocator_data_.template get<1>(); } | |||||
| SizeType<A>& GetSize() { return size_; } | |||||
| bool DidConstruct() { return GetData() != nullptr; } | |||||
| template <typename ValueAdapter> | |||||
| void Construct(Pointer<A> data, ValueAdapter& values, SizeType<A> size) { | |||||
| ConstructElements<A>(GetAllocator(), data, values, size); | |||||
| GetData() = data; | |||||
| GetSize() = size; | |||||
| } | |||||
| void Commit() && { | |||||
| GetData() = nullptr; | |||||
| GetSize() = 0; | |||||
| } | |||||
| private: | |||||
| container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; | |||||
| SizeType<A> size_; | |||||
| }; | |||||
| template <typename T, size_t N, typename A> | |||||
| class Storage { | |||||
| public: | |||||
| static SizeType<A> NextCapacity(SizeType<A> current_capacity) { | |||||
| return current_capacity * 2; | |||||
| } | |||||
| static SizeType<A> ComputeCapacity(SizeType<A> current_capacity, | |||||
| SizeType<A> requested_capacity) { | |||||
| return (std::max)(NextCapacity(current_capacity), requested_capacity); | |||||
| } | |||||
| // --------------------------------------------------------------------------- | |||||
| // Storage Constructors and Destructor | |||||
| // --------------------------------------------------------------------------- | |||||
| Storage() : metadata_(A(), /* size and is_allocated */ 0u) {} | |||||
| explicit Storage(const A& allocator) | |||||
| : metadata_(allocator, /* size and is_allocated */ 0u) {} | |||||
| ~Storage() { | |||||
| if (GetSizeAndIsAllocated() == 0) { | |||||
| // Empty and not allocated; nothing to do. | |||||
| } else if (IsMemcpyOk<A>::value) { | |||||
| // No destructors need to be run; just deallocate if necessary. | |||||
| DeallocateIfAllocated(); | |||||
| } else { | |||||
| DestroyContents(); | |||||
| } | |||||
| } | |||||
| // --------------------------------------------------------------------------- | |||||
| // Storage Member Accessors | |||||
| // --------------------------------------------------------------------------- | |||||
| SizeType<A>& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } | |||||
| const SizeType<A>& GetSizeAndIsAllocated() const { | |||||
| return metadata_.template get<1>(); | |||||
| } | |||||
| SizeType<A> GetSize() const { return GetSizeAndIsAllocated() >> 1; } | |||||
| bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } | |||||
| Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; } | |||||
| ConstPointer<A> GetAllocatedData() const { | |||||
| return data_.allocated.allocated_data; | |||||
| } | |||||
| Pointer<A> GetInlinedData() { | |||||
| return reinterpret_cast<Pointer<A>>( | |||||
| std::addressof(data_.inlined.inlined_data[0])); | |||||
| } | |||||
| ConstPointer<A> GetInlinedData() const { | |||||
| return reinterpret_cast<ConstPointer<A>>( | |||||
| std::addressof(data_.inlined.inlined_data[0])); | |||||
| } | |||||
| SizeType<A> GetAllocatedCapacity() const { | |||||
| return data_.allocated.allocated_capacity; | |||||
| } | |||||
| SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); } | |||||
| StorageView<A> MakeStorageView() { | |||||
| return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(), | |||||
| GetAllocatedCapacity()} | |||||
| : StorageView<A>{GetInlinedData(), GetSize(), | |||||
| GetInlinedCapacity()}; | |||||
| } | |||||
| A& GetAllocator() { return metadata_.template get<0>(); } | |||||
| const A& GetAllocator() const { return metadata_.template get<0>(); } | |||||
| // --------------------------------------------------------------------------- | |||||
| // Storage Member Mutators | |||||
| // --------------------------------------------------------------------------- | |||||
| ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); | |||||
| template <typename ValueAdapter> | |||||
| void Initialize(ValueAdapter values, SizeType<A> new_size); | |||||
| template <typename ValueAdapter> | |||||
| void Assign(ValueAdapter values, SizeType<A> new_size); | |||||
| template <typename ValueAdapter> | |||||
| void Resize(ValueAdapter values, SizeType<A> new_size); | |||||
| template <typename ValueAdapter> | |||||
| Iterator<A> Insert(ConstIterator<A> pos, ValueAdapter values, | |||||
| SizeType<A> insert_count); | |||||
| template <typename... Args> | |||||
| Reference<A> EmplaceBack(Args&&... args); | |||||
| Iterator<A> Erase(ConstIterator<A> from, ConstIterator<A> to); | |||||
| void Reserve(SizeType<A> requested_capacity); | |||||
| void ShrinkToFit(); | |||||
| void Swap(Storage* other_storage_ptr); | |||||
| void SetIsAllocated() { | |||||
| GetSizeAndIsAllocated() |= static_cast<SizeType<A>>(1); | |||||
| } | |||||
| void UnsetIsAllocated() { | |||||
| GetSizeAndIsAllocated() &= ((std::numeric_limits<SizeType<A>>::max)() - 1); | |||||
| } | |||||
| void SetSize(SizeType<A> size) { | |||||
| GetSizeAndIsAllocated() = | |||||
| (size << 1) | static_cast<SizeType<A>>(GetIsAllocated()); | |||||
| } | |||||
| void SetAllocatedSize(SizeType<A> size) { | |||||
| GetSizeAndIsAllocated() = (size << 1) | static_cast<SizeType<A>>(1); | |||||
| } | |||||
| void SetInlinedSize(SizeType<A> size) { | |||||
| GetSizeAndIsAllocated() = size << static_cast<SizeType<A>>(1); | |||||
| } | |||||
| void AddSize(SizeType<A> count) { | |||||
| GetSizeAndIsAllocated() += count << static_cast<SizeType<A>>(1); | |||||
| } | |||||
| void SubtractSize(SizeType<A> count) { | |||||
| ABSL_HARDENING_ASSERT(count <= GetSize()); | |||||
| GetSizeAndIsAllocated() -= count << static_cast<SizeType<A>>(1); | |||||
| } | |||||
| void SetAllocation(Allocation<A> allocation) { | |||||
| data_.allocated.allocated_data = allocation.data; | |||||
| data_.allocated.allocated_capacity = allocation.capacity; | |||||
| } | |||||
| void MemcpyFrom(const Storage& other_storage) { | |||||
| ABSL_HARDENING_ASSERT(IsMemcpyOk<A>::value || | |||||
| other_storage.GetIsAllocated()); | |||||
| GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); | |||||
| data_ = other_storage.data_; | |||||
| } | |||||
| void DeallocateIfAllocated() { | |||||
| if (GetIsAllocated()) { | |||||
| MallocAdapter<A>::Deallocate(GetAllocator(), GetAllocatedData(), | |||||
| GetAllocatedCapacity()); | |||||
| } | |||||
| } | |||||
| private: | |||||
| ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); | |||||
| using Metadata = container_internal::CompressedTuple<A, SizeType<A>>; | |||||
| struct Allocated { | |||||
| Pointer<A> allocated_data; | |||||
| SizeType<A> allocated_capacity; | |||||
| }; | |||||
| struct Inlined { | |||||
| alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])]; | |||||
| }; | |||||
| union Data { | |||||
| Allocated allocated; | |||||
| Inlined inlined; | |||||
| }; | |||||
| template <typename... Args> | |||||
| ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args); | |||||
| Metadata metadata_; | |||||
| Data data_; | |||||
| }; | |||||
| template <typename T, size_t N, typename A> | |||||
| void Storage<T, N, A>::DestroyContents() { | |||||
| Pointer<A> data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), data, GetSize()); | |||||
| DeallocateIfAllocated(); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| void Storage<T, N, A>::InitFrom(const Storage& other) { | |||||
| const SizeType<A> n = other.GetSize(); | |||||
| ABSL_HARDENING_ASSERT(n > 0); // Empty sources handled handled in caller. | |||||
| ConstPointer<A> src; | |||||
| Pointer<A> dst; | |||||
| if (!other.GetIsAllocated()) { | |||||
| dst = GetInlinedData(); | |||||
| src = other.GetInlinedData(); | |||||
| } else { | |||||
| // Because this is only called from the `InlinedVector` constructors, it's | |||||
| // safe to take on the allocation with size `0`. If `ConstructElements(...)` | |||||
| // throws, deallocation will be automatically handled by `~Storage()`. | |||||
| SizeType<A> requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); | |||||
| Allocation<A> allocation = | |||||
| MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); | |||||
| SetAllocation(allocation); | |||||
| dst = allocation.data; | |||||
| src = other.GetAllocatedData(); | |||||
| } | |||||
| if (IsMemcpyOk<A>::value) { | |||||
| std::memcpy(reinterpret_cast<char*>(dst), | |||||
| reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>)); | |||||
| } else { | |||||
| auto values = IteratorValueAdapter<A, ConstPointer<A>>(src); | |||||
| ConstructElements<A>(GetAllocator(), dst, values, n); | |||||
| } | |||||
| GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename ValueAdapter> | |||||
| auto Storage<T, N, A>::Initialize(ValueAdapter values, SizeType<A> new_size) | |||||
| -> void { | |||||
| // Only callable from constructors! | |||||
| ABSL_HARDENING_ASSERT(!GetIsAllocated()); | |||||
| ABSL_HARDENING_ASSERT(GetSize() == 0); | |||||
| Pointer<A> construct_data; | |||||
| if (new_size > GetInlinedCapacity()) { | |||||
| // Because this is only called from the `InlinedVector` constructors, it's | |||||
| // safe to take on the allocation with size `0`. If `ConstructElements(...)` | |||||
| // throws, deallocation will be automatically handled by `~Storage()`. | |||||
| SizeType<A> requested_capacity = | |||||
| ComputeCapacity(GetInlinedCapacity(), new_size); | |||||
| Allocation<A> allocation = | |||||
| MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); | |||||
| construct_data = allocation.data; | |||||
| SetAllocation(allocation); | |||||
| SetIsAllocated(); | |||||
| } else { | |||||
| construct_data = GetInlinedData(); | |||||
| } | |||||
| ConstructElements<A>(GetAllocator(), construct_data, values, new_size); | |||||
| // Since the initial size was guaranteed to be `0` and the allocated bit is | |||||
| // already correct for either case, *adding* `new_size` gives us the correct | |||||
| // result faster than setting it directly. | |||||
| AddSize(new_size); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename ValueAdapter> | |||||
| auto Storage<T, N, A>::Assign(ValueAdapter values, SizeType<A> new_size) | |||||
| -> void { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| AllocationTransaction<A> allocation_tx(GetAllocator()); | |||||
| absl::Span<ValueType<A>> assign_loop; | |||||
| absl::Span<ValueType<A>> construct_loop; | |||||
| absl::Span<ValueType<A>> destroy_loop; | |||||
| if (new_size > storage_view.capacity) { | |||||
| SizeType<A> requested_capacity = | |||||
| ComputeCapacity(storage_view.capacity, new_size); | |||||
| construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; | |||||
| destroy_loop = {storage_view.data, storage_view.size}; | |||||
| } else if (new_size > storage_view.size) { | |||||
| assign_loop = {storage_view.data, storage_view.size}; | |||||
| construct_loop = {storage_view.data + storage_view.size, | |||||
| new_size - storage_view.size}; | |||||
| } else { | |||||
| assign_loop = {storage_view.data, new_size}; | |||||
| destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; | |||||
| } | |||||
| AssignElements<A>(assign_loop.data(), values, assign_loop.size()); | |||||
| ConstructElements<A>(GetAllocator(), construct_loop.data(), values, | |||||
| construct_loop.size()); | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), destroy_loop.data(), | |||||
| destroy_loop.size()); | |||||
| if (allocation_tx.DidAllocate()) { | |||||
| DeallocateIfAllocated(); | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| SetIsAllocated(); | |||||
| } | |||||
| SetSize(new_size); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename ValueAdapter> | |||||
| auto Storage<T, N, A>::Resize(ValueAdapter values, SizeType<A> new_size) | |||||
| -> void { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| Pointer<A> const base = storage_view.data; | |||||
| const SizeType<A> size = storage_view.size; | |||||
| A& alloc = GetAllocator(); | |||||
| if (new_size <= size) { | |||||
| // Destroy extra old elements. | |||||
| DestroyAdapter<A>::DestroyElements(alloc, base + new_size, size - new_size); | |||||
| } else if (new_size <= storage_view.capacity) { | |||||
| // Construct new elements in place. | |||||
| ConstructElements<A>(alloc, base + size, values, new_size - size); | |||||
| } else { | |||||
| // Steps: | |||||
| // a. Allocate new backing store. | |||||
| // b. Construct new elements in new backing store. | |||||
| // c. Move existing elements from old backing store to new backing store. | |||||
| // d. Destroy all elements in old backing store. | |||||
| // Use transactional wrappers for the first two steps so we can roll | |||||
| // back if necessary due to exceptions. | |||||
| AllocationTransaction<A> allocation_tx(alloc); | |||||
| SizeType<A> requested_capacity = | |||||
| ComputeCapacity(storage_view.capacity, new_size); | |||||
| Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); | |||||
| ConstructionTransaction<A> construction_tx(alloc); | |||||
| construction_tx.Construct(new_data + size, values, new_size - size); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| (MoveIterator<A>(base))); | |||||
| ConstructElements<A>(alloc, new_data, move_values, size); | |||||
| DestroyAdapter<A>::DestroyElements(alloc, base, size); | |||||
| std::move(construction_tx).Commit(); | |||||
| DeallocateIfAllocated(); | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| SetIsAllocated(); | |||||
| } | |||||
| SetSize(new_size); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename ValueAdapter> | |||||
| auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values, | |||||
| SizeType<A> insert_count) -> Iterator<A> { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| SizeType<A> insert_index = | |||||
| std::distance(ConstIterator<A>(storage_view.data), pos); | |||||
| SizeType<A> insert_end_index = insert_index + insert_count; | |||||
| SizeType<A> new_size = storage_view.size + insert_count; | |||||
| if (new_size > storage_view.capacity) { | |||||
| AllocationTransaction<A> allocation_tx(GetAllocator()); | |||||
| ConstructionTransaction<A> construction_tx(GetAllocator()); | |||||
| ConstructionTransaction<A> move_construction_tx(GetAllocator()); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(storage_view.data)); | |||||
| SizeType<A> requested_capacity = | |||||
| ComputeCapacity(storage_view.capacity, new_size); | |||||
| Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); | |||||
| construction_tx.Construct(new_data + insert_index, values, insert_count); | |||||
| move_construction_tx.Construct(new_data, move_values, insert_index); | |||||
| ConstructElements<A>(GetAllocator(), new_data + insert_end_index, | |||||
| move_values, storage_view.size - insert_index); | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, | |||||
| storage_view.size); | |||||
| std::move(construction_tx).Commit(); | |||||
| std::move(move_construction_tx).Commit(); | |||||
| DeallocateIfAllocated(); | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| SetAllocatedSize(new_size); | |||||
| return Iterator<A>(new_data + insert_index); | |||||
| } else { | |||||
| SizeType<A> move_construction_destination_index = | |||||
| (std::max)(insert_end_index, storage_view.size); | |||||
| ConstructionTransaction<A> move_construction_tx(GetAllocator()); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_construction_values( | |||||
| MoveIterator<A>(storage_view.data + | |||||
| (move_construction_destination_index - insert_count))); | |||||
| absl::Span<ValueType<A>> move_construction = { | |||||
| storage_view.data + move_construction_destination_index, | |||||
| new_size - move_construction_destination_index}; | |||||
| Pointer<A> move_assignment_values = storage_view.data + insert_index; | |||||
| absl::Span<ValueType<A>> move_assignment = { | |||||
| storage_view.data + insert_end_index, | |||||
| move_construction_destination_index - insert_end_index}; | |||||
| absl::Span<ValueType<A>> insert_assignment = {move_assignment_values, | |||||
| move_construction.size()}; | |||||
| absl::Span<ValueType<A>> insert_construction = { | |||||
| insert_assignment.data() + insert_assignment.size(), | |||||
| insert_count - insert_assignment.size()}; | |||||
| move_construction_tx.Construct(move_construction.data(), | |||||
| move_construction_values, | |||||
| move_construction.size()); | |||||
| for (Pointer<A> | |||||
| destination = move_assignment.data() + move_assignment.size(), | |||||
| last_destination = move_assignment.data(), | |||||
| source = move_assignment_values + move_assignment.size(); | |||||
| ;) { | |||||
| --destination; | |||||
| --source; | |||||
| if (destination < last_destination) break; | |||||
| *destination = std::move(*source); | |||||
| } | |||||
| AssignElements<A>(insert_assignment.data(), values, | |||||
| insert_assignment.size()); | |||||
| ConstructElements<A>(GetAllocator(), insert_construction.data(), values, | |||||
| insert_construction.size()); | |||||
| std::move(move_construction_tx).Commit(); | |||||
| AddSize(insert_count); | |||||
| return Iterator<A>(storage_view.data + insert_index); | |||||
| } | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename... Args> | |||||
| auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> Reference<A> { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| const SizeType<A> n = storage_view.size; | |||||
| if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { | |||||
| // Fast path; new element fits. | |||||
| Pointer<A> last_ptr = storage_view.data + n; | |||||
| AllocatorTraits<A>::construct(GetAllocator(), last_ptr, | |||||
| std::forward<Args>(args)...); | |||||
| AddSize(1); | |||||
| return *last_ptr; | |||||
| } | |||||
| // TODO(b/173712035): Annotate with musttail attribute to prevent regression. | |||||
| return EmplaceBackSlow(std::forward<Args>(args)...); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| template <typename... Args> | |||||
| auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> Reference<A> { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| AllocationTransaction<A> allocation_tx(GetAllocator()); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(storage_view.data)); | |||||
| SizeType<A> requested_capacity = NextCapacity(storage_view.capacity); | |||||
| Pointer<A> construct_data = allocation_tx.Allocate(requested_capacity); | |||||
| Pointer<A> last_ptr = construct_data + storage_view.size; | |||||
| // Construct new element. | |||||
| AllocatorTraits<A>::construct(GetAllocator(), last_ptr, | |||||
| std::forward<Args>(args)...); | |||||
| // Move elements from old backing store to new backing store. | |||||
| ABSL_INTERNAL_TRY { | |||||
| ConstructElements<A>(GetAllocator(), allocation_tx.GetData(), move_values, | |||||
| storage_view.size); | |||||
| } | |||||
| ABSL_INTERNAL_CATCH_ANY { | |||||
| AllocatorTraits<A>::destroy(GetAllocator(), last_ptr); | |||||
| ABSL_INTERNAL_RETHROW; | |||||
| } | |||||
| // Destroy elements in old backing store. | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, | |||||
| storage_view.size); | |||||
| DeallocateIfAllocated(); | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| SetIsAllocated(); | |||||
| AddSize(1); | |||||
| return *last_ptr; | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to) | |||||
| -> Iterator<A> { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| SizeType<A> erase_size = std::distance(from, to); | |||||
| SizeType<A> erase_index = | |||||
| std::distance(ConstIterator<A>(storage_view.data), from); | |||||
| SizeType<A> erase_end_index = erase_index + erase_size; | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(storage_view.data + erase_end_index)); | |||||
| AssignElements<A>(storage_view.data + erase_index, move_values, | |||||
| storage_view.size - erase_end_index); | |||||
| DestroyAdapter<A>::DestroyElements( | |||||
| GetAllocator(), storage_view.data + (storage_view.size - erase_size), | |||||
| erase_size); | |||||
| SubtractSize(erase_size); | |||||
| return Iterator<A>(storage_view.data + erase_index); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| auto Storage<T, N, A>::Reserve(SizeType<A> requested_capacity) -> void { | |||||
| StorageView<A> storage_view = MakeStorageView(); | |||||
| if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; | |||||
| AllocationTransaction<A> allocation_tx(GetAllocator()); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(storage_view.data)); | |||||
| SizeType<A> new_requested_capacity = | |||||
| ComputeCapacity(storage_view.capacity, requested_capacity); | |||||
| Pointer<A> new_data = allocation_tx.Allocate(new_requested_capacity); | |||||
| ConstructElements<A>(GetAllocator(), new_data, move_values, | |||||
| storage_view.size); | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, | |||||
| storage_view.size); | |||||
| DeallocateIfAllocated(); | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| SetIsAllocated(); | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| auto Storage<T, N, A>::ShrinkToFit() -> void { | |||||
| // May only be called on allocated instances! | |||||
| ABSL_HARDENING_ASSERT(GetIsAllocated()); | |||||
| StorageView<A> storage_view{GetAllocatedData(), GetSize(), | |||||
| GetAllocatedCapacity()}; | |||||
| if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; | |||||
| AllocationTransaction<A> allocation_tx(GetAllocator()); | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(storage_view.data)); | |||||
| Pointer<A> construct_data; | |||||
| if (storage_view.size > GetInlinedCapacity()) { | |||||
| SizeType<A> requested_capacity = storage_view.size; | |||||
| construct_data = allocation_tx.Allocate(requested_capacity); | |||||
| if (allocation_tx.GetCapacity() >= storage_view.capacity) { | |||||
| // Already using the smallest available heap allocation. | |||||
| return; | |||||
| } | |||||
| } else { | |||||
| construct_data = GetInlinedData(); | |||||
| } | |||||
| ABSL_INTERNAL_TRY { | |||||
| ConstructElements<A>(GetAllocator(), construct_data, move_values, | |||||
| storage_view.size); | |||||
| } | |||||
| ABSL_INTERNAL_CATCH_ANY { | |||||
| SetAllocation({storage_view.data, storage_view.capacity}); | |||||
| ABSL_INTERNAL_RETHROW; | |||||
| } | |||||
| DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, | |||||
| storage_view.size); | |||||
| MallocAdapter<A>::Deallocate(GetAllocator(), storage_view.data, | |||||
| storage_view.capacity); | |||||
| if (allocation_tx.DidAllocate()) { | |||||
| SetAllocation(std::move(allocation_tx).Release()); | |||||
| } else { | |||||
| UnsetIsAllocated(); | |||||
| } | |||||
| } | |||||
| template <typename T, size_t N, typename A> | |||||
| auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { | |||||
| using std::swap; | |||||
| ABSL_HARDENING_ASSERT(this != other_storage_ptr); | |||||
| if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { | |||||
| swap(data_.allocated, other_storage_ptr->data_.allocated); | |||||
| } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { | |||||
| Storage* small_ptr = this; | |||||
| Storage* large_ptr = other_storage_ptr; | |||||
| if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); | |||||
| for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) { | |||||
| swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); | |||||
| } | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize())); | |||||
| ConstructElements<A>(large_ptr->GetAllocator(), | |||||
| small_ptr->GetInlinedData() + small_ptr->GetSize(), | |||||
| move_values, | |||||
| large_ptr->GetSize() - small_ptr->GetSize()); | |||||
| DestroyAdapter<A>::DestroyElements( | |||||
| large_ptr->GetAllocator(), | |||||
| large_ptr->GetInlinedData() + small_ptr->GetSize(), | |||||
| large_ptr->GetSize() - small_ptr->GetSize()); | |||||
| } else { | |||||
| Storage* allocated_ptr = this; | |||||
| Storage* inlined_ptr = other_storage_ptr; | |||||
| if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); | |||||
| StorageView<A> allocated_storage_view{ | |||||
| allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), | |||||
| allocated_ptr->GetAllocatedCapacity()}; | |||||
| IteratorValueAdapter<A, MoveIterator<A>> move_values( | |||||
| MoveIterator<A>(inlined_ptr->GetInlinedData())); | |||||
| ABSL_INTERNAL_TRY { | |||||
| ConstructElements<A>(inlined_ptr->GetAllocator(), | |||||
| allocated_ptr->GetInlinedData(), move_values, | |||||
| inlined_ptr->GetSize()); | |||||
| } | |||||
| ABSL_INTERNAL_CATCH_ANY { | |||||
| allocated_ptr->SetAllocation(Allocation<A>{ | |||||
| allocated_storage_view.data, allocated_storage_view.capacity}); | |||||
| ABSL_INTERNAL_RETHROW; | |||||
| } | |||||
| DestroyAdapter<A>::DestroyElements(inlined_ptr->GetAllocator(), | |||||
| inlined_ptr->GetInlinedData(), | |||||
| inlined_ptr->GetSize()); | |||||
| inlined_ptr->SetAllocation(Allocation<A>{allocated_storage_view.data, | |||||
| allocated_storage_view.capacity}); | |||||
| } | |||||
| swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); | |||||
| swap(GetAllocator(), other_storage_ptr->GetAllocator()); | |||||
| } | |||||
| // End ignore "array-bounds" | |||||
| #if !defined(__clang__) && defined(__GNUC__) | |||||
| #pragma GCC diagnostic pop | |||||
| #endif | |||||
| } // namespace inlined_vector_internal | |||||
| ABSL_NAMESPACE_END | |||||
| } // namespace absl | |||||
| #endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_INTERNAL_H_ | |||||
| @@ -0,0 +1,743 @@ | |||||
| // 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,92 @@ | |||||
| // 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,198 @@ | |||||
| // 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,274 @@ | |||||
| // 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,83 @@ | |||||
| // 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,494 @@ | |||||
| // 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,117 @@ | |||||
| // 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,87 @@ | |||||
| // 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,352 @@ | |||||
| // 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,496 @@ | |||||
| // 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_ | |||||
| @@ -0,0 +1,91 @@ | |||||
| // 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_LOOKUP_TEST_H_ | |||||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_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 UnordSet> | |||||
| class LookupTest : public ::testing::Test {}; | |||||
| TYPED_TEST_SUITE_P(LookupTest); | |||||
| 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& v : values) | |||||
| EXPECT_EQ(0, m.count(v)) << ::testing::PrintToString(v); | |||||
| m.insert(values.begin(), values.end()); | |||||
| for (const auto& v : values) | |||||
| EXPECT_EQ(1, m.count(v)) << ::testing::PrintToString(v); | |||||
| } | |||||
| TYPED_TEST_P(LookupTest, Find) { | |||||
| 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& v : values) | |||||
| EXPECT_TRUE(m.end() == m.find(v)) << ::testing::PrintToString(v); | |||||
| m.insert(values.begin(), values.end()); | |||||
| for (const auto& v : values) { | |||||
| typename TypeParam::iterator it = m.find(v); | |||||
| static_assert(std::is_same<const typename TypeParam::value_type&, | |||||
| decltype(*it)>::value, | |||||
| ""); | |||||
| static_assert(std::is_same<const typename TypeParam::value_type*, | |||||
| decltype(it.operator->())>::value, | |||||
| ""); | |||||
| EXPECT_TRUE(m.end() != it) << ::testing::PrintToString(v); | |||||
| EXPECT_EQ(v, *it) << ::testing::PrintToString(v); | |||||
| } | |||||
| } | |||||
| TYPED_TEST_P(LookupTest, EqualRange) { | |||||
| 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& v : values) { | |||||
| auto r = m.equal_range(v); | |||||
| ASSERT_EQ(0, std::distance(r.first, r.second)); | |||||
| } | |||||
| m.insert(values.begin(), values.end()); | |||||
| for (const auto& v : values) { | |||||
| auto r = m.equal_range(v); | |||||
| ASSERT_EQ(1, std::distance(r.first, r.second)); | |||||
| EXPECT_EQ(v, *r.first); | |||||
| } | |||||
| } | |||||
| REGISTER_TYPED_TEST_SUITE_P(LookupTest, Count, Find, EqualRange); | |||||
| } // namespace container_internal | |||||
| ABSL_NAMESPACE_END | |||||
| } // namespace absl | |||||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_LOOKUP_TEST_H_ | |||||
| @@ -0,0 +1,86 @@ | |||||
| // 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_SET_MEMBERS_TEST_H_ | |||||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_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 UnordSet> | |||||
| 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<typename TypeParam::key_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_SET_MEMBERS_TEST_H_ | |||||
| @@ -0,0 +1,221 @@ | |||||
| // 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_MODIFIERS_TEST_H_ | |||||
| #define ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_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 UnordSet> | |||||
| 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(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||||
| m.clear(); | |||||
| EXPECT_THAT(keys(m), ::testing::UnorderedElementsAre()); | |||||
| EXPECT_TRUE(m.empty()); | |||||
| } | |||||
| TYPED_TEST_P(ModifiersTest, Insert) { | |||||
| using T = hash_internal::GeneratedType<TypeParam>; | |||||
| T val = hash_internal::Generator<T>()(); | |||||
| TypeParam m; | |||||
| auto p = m.insert(val); | |||||
| EXPECT_TRUE(p.second); | |||||
| EXPECT_EQ(val, *p.first); | |||||
| p = m.insert(val); | |||||
| EXPECT_FALSE(p.second); | |||||
| } | |||||
| TYPED_TEST_P(ModifiersTest, InsertHint) { | |||||
| using T = hash_internal::GeneratedType<TypeParam>; | |||||
| T val = hash_internal::Generator<T>()(); | |||||
| TypeParam m; | |||||
| auto it = m.insert(m.end(), val); | |||||
| EXPECT_TRUE(it != m.end()); | |||||
| EXPECT_EQ(val, *it); | |||||
| it = m.insert(it, val); | |||||
| 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(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||||
| } | |||||
| TYPED_TEST_P(ModifiersTest, InsertWithinCapacity) { | |||||
| using T = hash_internal::GeneratedType<TypeParam>; | |||||
| 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); | |||||
| m.insert(val); | |||||
| 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) { | |||||
| values.insert(values.end(), base_values.begin(), base_values.end()); | |||||
| } | |||||
| 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, Emplace) { | |||||
| using T = hash_internal::GeneratedType<TypeParam>; | |||||
| 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); | |||||
| p = m.emplace(val); | |||||
| EXPECT_FALSE(p.second); | |||||
| EXPECT_EQ(val, *p.first); | |||||
| } | |||||
| TYPED_TEST_P(ModifiersTest, EmplaceHint) { | |||||
| using T = hash_internal::GeneratedType<TypeParam>; | |||||
| 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); | |||||
| it = m.emplace_hint(it, val); | |||||
| EXPECT_EQ(val, *it); | |||||
| } | |||||
| 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>; | |||||
| std::vector<T> values; | |||||
| std::generate_n(std::back_inserter(values), 10, | |||||
| hash_internal::Generator<T>()); | |||||
| TypeParam m(values.begin(), values.end()); | |||||
| ASSERT_THAT(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||||
| std::vector<T> values2; | |||||
| for (const auto& val : values) | |||||
| if (val != *m.begin()) 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(keys(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(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||||
| auto it = m.erase(m.begin(), m.end()); | |||||
| EXPECT_THAT(keys(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(keys(m), ::testing::UnorderedElementsAreArray(values)); | |||||
| EXPECT_EQ(1, m.erase(values[0])); | |||||
| EXPECT_EQ(0, std::count(m.begin(), m.end(), values[0])); | |||||
| EXPECT_THAT(keys(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(keys(m1), ::testing::UnorderedElementsAreArray(v1)); | |||||
| EXPECT_THAT(keys(m2), ::testing::UnorderedElementsAreArray(v2)); | |||||
| m1.swap(m2); | |||||
| EXPECT_THAT(keys(m1), ::testing::UnorderedElementsAreArray(v2)); | |||||
| EXPECT_THAT(keys(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, Emplace, EmplaceHint, | |||||
| Erase, EraseRange, EraseKey, Swap); | |||||
| } // namespace container_internal | |||||
| ABSL_NAMESPACE_END | |||||
| } // namespace absl | |||||
| #endif // ABSL_CONTAINER_INTERNAL_UNORDERED_SET_MODIFIERS_TEST_H_ | |||||
| @@ -0,0 +1,604 @@ | |||||
| // 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: node_hash_map.h | |||||
| // ----------------------------------------------------------------------------- | |||||
| // | |||||
| // An `absl::node_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, | |||||
| // `node_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`. However, if you need pointer stability and cannot store | |||||
| // a `flat_hash_map` with `unique_ptr` elements, a `node_hash_map` may be a | |||||
| // valid alternative. As well, if you are migrating your code from using | |||||
| // `std::unordered_map`, a `node_hash_map` provides a more straightforward | |||||
| // migration, because it guarantees pointer stability. Consider migrating to | |||||
| // `node_hash_map` and perhaps converting to a more efficient `flat_hash_map` | |||||
| // upon further review. | |||||
| #ifndef ABSL_CONTAINER_NODE_HASH_MAP_H_ | |||||
| #define ABSL_CONTAINER_NODE_HASH_MAP_H_ | |||||
| #include <tuple> | |||||
| #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/node_slot_policy.h" | |||||
| #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 Key, class Value> | |||||
| class NodeHashMapPolicy; | |||||
| } // namespace container_internal | |||||
| // ----------------------------------------------------------------------------- | |||||
| // absl::node_hash_map | |||||
| // ----------------------------------------------------------------------------- | |||||
| // | |||||
| // An `absl::node_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: | |||||
| // | |||||
| // * Supports heterogeneous lookup, through `find()`, `operator[]()` and | |||||
| // `insert()`, provided that the map is provided a compatible heterogeneous | |||||
| // hashing function and equality operator. | |||||
| // * 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, `node_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 `node_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::node_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. | |||||
| // | |||||
| // Example: | |||||
| // | |||||
| // // Create a node hash map of three strings (that map to strings) | |||||
| // absl::node_hash_map<std::string, std::string> ducks = | |||||
| // {{"a", "huey"}, {"b", "dewey"}, {"c", "louie"}}; | |||||
| // | |||||
| // // Insert a new element into the node hash map | |||||
| // ducks.insert({"d", "donald"}}; | |||||
| // | |||||
| // // Force a rehash of the node 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 Key, class Value, | |||||
| class Hash = absl::container_internal::hash_default_hash<Key>, | |||||
| class Eq = absl::container_internal::hash_default_eq<Key>, | |||||
| class Alloc = std::allocator<std::pair<const Key, Value>>> | |||||
| class node_hash_map | |||||
| : public absl::container_internal::raw_hash_map< | |||||
| absl::container_internal::NodeHashMapPolicy<Key, Value>, Hash, Eq, | |||||
| Alloc> { | |||||
| using Base = typename node_hash_map::raw_hash_map; | |||||
| public: | |||||
| // Constructors and Assignment Operators | |||||
| // | |||||
| // A node_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::node_hash_map<int, std::string> map1; | |||||
| // | |||||
| // * Initializer List constructor | |||||
| // | |||||
| // absl::node_hash_map<int, std::string> map2 = | |||||
| // {{1, "huey"}, {2, "dewey"}, {3, "louie"},}; | |||||
| // | |||||
| // * Copy constructor | |||||
| // | |||||
| // absl::node_hash_map<int, std::string> map3(map2); | |||||
| // | |||||
| // * Copy assignment operator | |||||
| // | |||||
| // // Hash functor and Comparator are copied as well | |||||
| // absl::node_hash_map<int, std::string> map4; | |||||
| // map4 = map3; | |||||
| // | |||||
| // * Move constructor | |||||
| // | |||||
| // // Move is guaranteed efficient | |||||
| // absl::node_hash_map<int, std::string> map5(std::move(map4)); | |||||
| // | |||||
| // * Move assignment operator | |||||
| // | |||||
| // // May be efficient if allocators are compatible | |||||
| // absl::node_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::node_hash_map<int, std::string> map7(v.begin(), v.end()); | |||||
| node_hash_map() {} | |||||
| using Base::Base; | |||||
| // node_hash_map::begin() | |||||
| // | |||||
| // Returns an iterator to the beginning of the `node_hash_map`. | |||||
| using Base::begin; | |||||
| // node_hash_map::cbegin() | |||||
| // | |||||
| // Returns a const iterator to the beginning of the `node_hash_map`. | |||||
| using Base::cbegin; | |||||
| // node_hash_map::cend() | |||||
| // | |||||
| // Returns a const iterator to the end of the `node_hash_map`. | |||||
| using Base::cend; | |||||
| // node_hash_map::end() | |||||
| // | |||||
| // Returns an iterator to the end of the `node_hash_map`. | |||||
| using Base::end; | |||||
| // node_hash_map::capacity() | |||||
| // | |||||
| // Returns the number of element slots (assigned, deleted, and empty) | |||||
| // available within the `node_hash_map`. | |||||
| // | |||||
| // NOTE: this member function is particular to `absl::node_hash_map` and is | |||||
| // not provided in the `std::unordered_map` API. | |||||
| using Base::capacity; | |||||
| // node_hash_map::empty() | |||||
| // | |||||
| // Returns whether or not the `node_hash_map` is empty. | |||||
| using Base::empty; | |||||
| // node_hash_map::max_size() | |||||
| // | |||||
| // Returns the largest theoretical possible number of elements within a | |||||
| // `node_hash_map` under current memory constraints. This value can be thought | |||||
| // of as the largest value of `std::distance(begin(), end())` for a | |||||
| // `node_hash_map<K, V>`. | |||||
| using Base::max_size; | |||||
| // node_hash_map::size() | |||||
| // | |||||
| // Returns the number of elements currently within the `node_hash_map`. | |||||
| using Base::size; | |||||
| // node_hash_map::clear() | |||||
| // | |||||
| // Removes all elements from the `node_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; | |||||
| // node_hash_map::erase() | |||||
| // | |||||
| // Erases elements within the `node_hash_map`. Erasing does not trigger a | |||||
| // rehash. Overloads are listed below. | |||||
| // | |||||
| // void erase(const_iterator pos): | |||||
| // | |||||
| // Erases the element at `position` of the `node_hash_map`, returning | |||||
| // `void`. | |||||
| // | |||||
| // NOTE: this return behavior is different than that of STL containers in | |||||
| // general and `std::unordered_map` in particular. | |||||
| // | |||||
| // 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; | |||||
| // node_hash_map::insert() | |||||
| // | |||||
| // Inserts an element of the specified value into the `node_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 `node_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 `node_hash_map`. Returns a `std::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 `node_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 | |||||
| // `node_hash_map` we guarantee the first match is inserted. | |||||
| using Base::insert; | |||||
| // node_hash_map::insert_or_assign() | |||||
| // | |||||
| // Inserts an element of the specified value into the `node_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 iterators are invalidated. Overloads are listed | |||||
| // below. | |||||
| // | |||||
| // std::pair<iterator, bool> insert_or_assign(const init_type& k, T&& obj): | |||||
| // std::pair<iterator, bool> insert_or_assign(init_type&& k, T&& obj): | |||||
| // | |||||
| // Inserts/Assigns (or moves) the element of the specified key into the | |||||
| // `node_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 | |||||
| // `node_hash_map` using the position of `hint` as a non-binding suggestion | |||||
| // for where to begin the insertion search. | |||||
| using Base::insert_or_assign; | |||||
| // node_hash_map::emplace() | |||||
| // | |||||
| // Inserts an element of the specified value by constructing it in-place | |||||
| // within the `node_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; | |||||
| // node_hash_map::emplace_hint() | |||||
| // | |||||
| // Inserts an element of the specified value by constructing it in-place | |||||
| // within the `node_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; | |||||
| // node_hash_map::try_emplace() | |||||
| // | |||||
| // Inserts an element of the specified value by constructing it in-place | |||||
| // within the `node_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. | |||||
| // | |||||
| // 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 | |||||
| // `node_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 | |||||
| // `node_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; | |||||
| // node_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 | |||||
| // `node_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; | |||||
| // node_hash_map::merge() | |||||
| // | |||||
| // Extracts elements from a given `source` node hash map into this | |||||
| // `node_hash_map`. If the destination `node_hash_map` already contains an | |||||
| // element with an equivalent key, that element is not extracted. | |||||
| using Base::merge; | |||||
| // node_hash_map::swap(node_hash_map& other) | |||||
| // | |||||
| // Exchanges the contents of this `node_hash_map` with those of the `other` | |||||
| // node hash map, avoiding invocation of any move, copy, or swap operations on | |||||
| // individual elements. | |||||
| // | |||||
| // All iterators and references on the `node_hash_map` remain valid, excepting | |||||
| // for the past-the-end iterator, which is invalidated. | |||||
| // | |||||
| // `swap()` requires that the node hash map's hashing and key equivalence | |||||
| // functions be Swappable, and are exchaged 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; | |||||
| // node_hash_map::rehash(count) | |||||
| // | |||||
| // Rehashes the `node_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). | |||||
| using Base::rehash; | |||||
| // node_hash_map::reserve(count) | |||||
| // | |||||
| // Sets the number of slots in the `node_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; | |||||
| // node_hash_map::at() | |||||
| // | |||||
| // Returns a reference to the mapped value of the element with key equivalent | |||||
| // to the passed key. | |||||
| using Base::at; | |||||
| // node_hash_map::contains() | |||||
| // | |||||
| // Determines whether an element with a key comparing equal to the given `key` | |||||
| // exists within the `node_hash_map`, returning `true` if so or `false` | |||||
| // otherwise. | |||||
| using Base::contains; | |||||
| // node_hash_map::count(const Key& key) const | |||||
| // | |||||
| // Returns the number of elements with a key comparing equal to the given | |||||
| // `key` within the `node_hash_map`. note that this function will return | |||||
| // either `1` or `0` since duplicate keys are not allowed within a | |||||
| // `node_hash_map`. | |||||
| using Base::count; | |||||
| // node_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 | |||||
| // `node_hash_map`. | |||||
| using Base::equal_range; | |||||
| // node_hash_map::find() | |||||
| // | |||||
| // Finds an element with the passed `key` within the `node_hash_map`. | |||||
| using Base::find; | |||||
| // node_hash_map::operator[]() | |||||
| // | |||||
| // Returns a reference to the value mapped to the passed key within the | |||||
| // `node_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[]; | |||||
| // node_hash_map::bucket_count() | |||||
| // | |||||
| // Returns the number of "buckets" within the `node_hash_map`. | |||||
| using Base::bucket_count; | |||||
| // node_hash_map::load_factor() | |||||
| // | |||||
| // Returns the current load factor of the `node_hash_map` (the average number | |||||
| // of slots occupied with a value within the hash map). | |||||
| using Base::load_factor; | |||||
| // node_hash_map::max_load_factor() | |||||
| // | |||||
| // Manages the maximum load factor of the `node_hash_map`. Overloads are | |||||
| // listed below. | |||||
| // | |||||
| // float node_hash_map::max_load_factor() | |||||
| // | |||||
| // Returns the current maximum load factor of the `node_hash_map`. | |||||
| // | |||||
| // void node_hash_map::max_load_factor(float ml) | |||||
| // | |||||
| // Sets the maximum load factor of the `node_hash_map` to the passed value. | |||||
| // | |||||
| // NOTE: This overload is provided only for API compatibility with the STL; | |||||
| // `node_hash_map` will ignore any set load factor and manage its rehashing | |||||
| // internally as an implementation detail. | |||||
| using Base::max_load_factor; | |||||
| // node_hash_map::get_allocator() | |||||
| // | |||||
| // Returns the allocator function associated with this `node_hash_map`. | |||||
| using Base::get_allocator; | |||||
| // node_hash_map::hash_function() | |||||
| // | |||||
| // Returns the hashing function used to hash the keys within this | |||||
| // `node_hash_map`. | |||||
| using Base::hash_function; | |||||
| // node_hash_map::key_eq() | |||||
| // | |||||
| // Returns the function used for comparing keys equality. | |||||
| using Base::key_eq; | |||||
| }; | |||||
| // erase_if(node_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 node_hash_map<K, V, H, E, A>::size_type erase_if( | |||||
| node_hash_map<K, V, H, E, A>& c, Predicate pred) { | |||||
| return container_internal::EraseIf(pred, &c); | |||||
| } | |||||
| namespace container_internal { | |||||
| template <class Key, class Value> | |||||
| class NodeHashMapPolicy | |||||
| : public absl::container_internal::node_slot_policy< | |||||
| std::pair<const Key, Value>&, NodeHashMapPolicy<Key, Value>> { | |||||
| using value_type = std::pair<const Key, Value>; | |||||
| public: | |||||
| using key_type = Key; | |||||
| using mapped_type = Value; | |||||
| using init_type = std::pair</*non const*/ key_type, mapped_type>; | |||||
| template <class Allocator, class... Args> | |||||
| static value_type* new_element(Allocator* alloc, Args&&... args) { | |||||
| using PairAlloc = typename absl::allocator_traits< | |||||
| Allocator>::template rebind_alloc<value_type>; | |||||
| PairAlloc pair_alloc(*alloc); | |||||
| value_type* res = | |||||
| absl::allocator_traits<PairAlloc>::allocate(pair_alloc, 1); | |||||
| absl::allocator_traits<PairAlloc>::construct(pair_alloc, res, | |||||
| std::forward<Args>(args)...); | |||||
| return res; | |||||
| } | |||||
| template <class Allocator> | |||||
| static void delete_element(Allocator* alloc, value_type* pair) { | |||||
| using PairAlloc = typename absl::allocator_traits< | |||||
| Allocator>::template rebind_alloc<value_type>; | |||||
| PairAlloc pair_alloc(*alloc); | |||||
| absl::allocator_traits<PairAlloc>::destroy(pair_alloc, pair); | |||||
| absl::allocator_traits<PairAlloc>::deallocate(pair_alloc, pair, 1); | |||||
| } | |||||
| 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 element_space_used(const value_type*) { | |||||
| return sizeof(value_type); | |||||
| } | |||||
| static Value& value(value_type* elem) { return elem->second; } | |||||
| static const Value& value(const value_type* elem) { return elem->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::node_hash_map<Key, T, Hash, KeyEqual, Allocator>> : std::true_type {}; | |||||
| } // namespace container_algorithm_internal | |||||
| ABSL_NAMESPACE_END | |||||
| } // namespace absl | |||||
| #endif // ABSL_CONTAINER_NODE_HASH_MAP_H_ | |||||